🔥 🐷 🔥
imu Incrementally build statically-linked pages and servers with tup.
What is it? Why?
This is an opinionated build system that has made all the decisions for you.
It works with simple commands. It is incremental and runs only tasks when needed. Eligible tasks are run in parallel. It also prevents stale files from leaking into your deployment directory. You do not need to specify your project dependencies for this to work, but in exchange you need to follow the provided project layout.
It uses tup to accomplish this.
For rapid prototyping, Stage0, S.js and TailwindCSS related libraries are provided. Their use are optional and they consume roughly 3 to 6KB of the compressed bundle size when optimized. You also have the option of using the Vue.js suite instead of Stage0 and S.js, however this adds about 30KB in size and has different performance characteristics. It is, however, much more familiar to developers.
JavaScript is ES6, CSS is PostCSS Preset Env, HTML is preprocessed, all is aggressively minified and packed into single HTML page. It is easy to build many pages, with as little or as much CSS and JavaScript as needed -- the natural unit for splitting up bundle sizes.
At around 64KB~128KB compressed, you may want to start thinking about a new page.
Reading the .imu
source installed into your project is a good idea. You are
encouraged to fork, modify and otherwise twist the contents to your needs.
Requirements
Optional Dependencies
npm install --global imu-build
- tup
- .NET Core 3.0
- MySQL
Neither tup nor imu-build
are required to build an imu project. Your
end-users may always execute npm build
in the project root to get a
release build.
Tup
By default, it generates tup build logic to provide the incrementing, parallel build and deployment system.
Although it was designed to work with tup, its use is optional. If it is not found, the entire project is built as a single synchronous thread. This is both slower, not incremental and may also result in stale artifacts in the deployment directory, but this will allow anyone to build a project with no additional dependencies beyond Node.js.
imu-build
This guide will assume you will have installed imu-build
in order to use the
imu
command from anywhere in the project tree. However, from the project
root you may always use npm run
to execute the very same commands.
There is no default command for npm run
by itself, however
npm run build
will execute a release build.
You will also need to run the new
command as npm run new -- -p example
with the --
in order to pass arguments.
The init
command is only available with imu-build
.
Client Overview
Commands | Description |
---|---|
imu init [option] | Installs the build scripts and npm dependencies. |
imu new [options] | Create new projects and templates. |
imu client-debug | Builds the client in debug mode. |
imu client-release | Builds the client in release mode. |
The init
command may take an optional arguement of either stage0
or vue
.
If not provided, it will default to stage0
. The following example explains
the default workflow.
$ npm install --global imu-build
$ mkdir (Project)
$ cd (Project)
$ imu init
$ imu new --page (PageName)
$ imu
This will build an empty page to confirm the toolchain is working. You may edit
the code in client/pages/(PageName)
to further experiment.
After making changes, executing imu
inside the project directory will rebuild
only the necessary files. For speed, this is an unoptimized build. Running
imu client-release
will compile an optimized, ready-to-deploy build.
When you are ready to start a new component for your page:
imu new --page (PageName) --component (ComponentName)
You may provide the flags in either order, and in shortened form:
imu new -p (PageName) -c (ComponentName)
At some point, you will want to start again with a fresh page:
imu new -p (NewPageName)
Names should not contain spaces and should be valid as an HTML ID tag. These
commands generate a new set of files to edit inside client/pages/(PageName)
.
Layout
Page Structure | |
---|---|
components/*.js | Component code. |
license.html | License comment tag; prepended to the page. |
root.html | Entry point into the page's actual DOM code. |
root.css | Entry point into the page style. |
root.js | Entry point into the page code. |
Each page is given a components/
directory to hold component JS,
HTML templates and assorted JavaScript code specific to the page.
Most editing will be done on root.js
and its child components/*
. When
you need to embed resources into the actual DOM, such as inlining SVGs and
templates, you would edit root.html
.
JS, CSS and HTML import directives will all search for paths relative to
the importing file. In addition, it will also search the subdirectories of
client
and client/pages
. The use of file extensions is optional except
to resolve conflicts.
The compiled page files are placed in deploy/client
.
Project Structure | |
---|---|
client/pages | Code for each application page. |
client/share | Code shared between each application page. |
client/style | Style code and Tailwind configuration. |
client/svg | SVG icons available for inlining. |
client/util | Third-party libraries used by application pages. |
deploy/client | The destination of compiled pages. |
deploy/static | All static files and content, available via /static |
tmp | Temporary files created during the build process. |
The client/style
directory contains the tailwind.config.js
file and
is a good place for stylesheets you wish to @import
into pages. For example,
a site may consist of many pages all importing the same set of Tailwind
components. As each page has its own CSS file, you may safely place unique
classes and overrides into it without affecting the styling of other pages.
The style/base.css
file is the project-wide style reset. It is mostly the
same as TailwindCSS's reset, using sections of Normalize.css,
SUIT CSS and TailwindCSS-specific rules. It has had a few
additional rules added, including part of Eric Meyer's reset.
If you need to use third-party ES6 modules that are not available via npm
,
use the client/util
directory.
Static files should be placed in deploy/static
. A built-in favicon.ico
generator will place one inside deploy/static
if one is not present when
calling imu new -p
. This is a 43 byte 1x1 transparent GIF image, as small
as it can be made.
A suggested directory is client/share
, to be used for components that are
shared and imported across pages. For example, a '404 Not Found' component
may be the same across all of your pages. You will need to create the
client/share
directory, should you choose to use it.
Other potential paths could include things such as client/store
and
client/api
for holding modules for S.js (or Vuex) store and API
request related abstractions. client/router.js
could hold your application's
router configuration. These paths require no specific support from imu.
Bundling
JavaScript is bundled with Rollup.js, offering you ES6-level modules your page. For release builds, Terser is used to minimize the compiled page bundle with full ES6 support.
CSS is processed with PostCSS, with CSS Import and PostCSS Preset Env, which provides a number of current CSS spec features. PostCSS Preset Env also runs a pass with Autoprefixer, saving you from writing vendor-specific prefixes. For release builds, all unused CSS classes are automatically purged from the resulting code using PurgeCSS. CSSO is used as a final pass to minimize the code.
On release builds, the final HTML is run through HTMLMinifier prior to compression with gzip.
Preprocessor Directives
imu provides a set of directives available for use within files being
built. For HTML it is: @import
, @script
and@svg
.
The @import
directive must be enclosed in a comment tag. This will copy the
contents of the file into its place place.
As mentioned prior, @import
will all search for paths relative to the
importing file. It will also search the subdirectories of client
and
client/pages
.
A @script
directive has also been included, which behaves the same as
@import
but will wrap the results in <script type="x/templates">
tags. An
ID based upon the filename is included in these tags. As such, filenames must
consist only of valid ID characters and should not contain spaces.
In Vue.js HTML templates, only the @import
directive is available.
<div>
<!-- @import "./local/project/file.html" //-->
</div>
<div>
<!-- @import "main/components/example" //-->
</div>
<div>
<!-- @import "share/example" //-->
</div>
SVG
An @svg
directive has also been provided. It is used just like @import
and
@script
.
SVG files imported will first be parsed and processed to add an ID based upon
the filename, sans extension. The same restrictions on naming that apply to
@script
also apply here.
Furthermore, the fill
attribute is set to 'inherit' and any height
or
width
attribute is removed prior to inlining.
Vue
A @vue
directive has been provided for use inside JavaScript components.
It will compile and inline the specified HTML template as a set of render
and staticRenderFns
variables that can then be passed to the Vue object.
The search path behavior is identical to @import
discussed above. It
also will resolve and inline any @import
directive encountered.
<section>
<!-- @import "main/components/something" //-->
<p>Example!</p>
</section>
import Vue from "vue/dist/vue.runtime.esm";
// @vue "main/components/example"
var App = Vue.component("Example", {render, staticRenderFns});
export default App;
Server Overview
Optional .NET Core 3.0 and MySQL support is provided as follows:
* `imu server` builds the server project natively & SQL schema.
* `imu server-linux` builds the server project specifically for Linux.
* `imu sql` builds the SQL schema.
Copying configuration files, including nginx is done by:
* `imu copy-conf` Copies all server-related configuration into 'deploy/'.
Furthermore, new
has the following additional commands:
* `imu new --server ServerName` starts a new server codebase.
* `imu new --table Name` starts a new SQL table file.
* `imu new --patch` starts a new SQL migration patch file.
Neither .NET Core 3.0 or MySQL build support makes use of tup.
SQL Preprocessor
An equivalent of @import
has been added for MySQL and is used much the same.
# @import "./local/table/name"
This is processed via the imu sql
command.
Resetting
Should any issues with build artifacts arise, you may always try imu reset
.
This will remove all build artifacts from the project. As your project expands,
you may wish to update the list of files reset
will remove in ./.imu/reset.js
.
Version Check
You may quickly determine the version installed by using imu version
. This
will print the version of the globally installed imu-build
package. If you
are currently in a project directory, it will also print the version of the
build scripts that have been installed via imu init
.
License
As imu aggressively strips code, including the original copyright comments,
the relevant copyright notices are added as a document comment to all pages
built for release. Be sure to keep license.html
up to date as you change
libraries.