wake v0.3.6
wake
wake (short for 'web make') is a build tool for website assets. It aims to fix
the problems caused by other asset toolchains that conflate several concerns
related to deploying static assets:
- Compiling languages like CoffeeScript and Sass to JavaScript and CSS
- Optimising JavaScript and CSS for fast download times in production
- Generating links from HTML to JavaScript, CSS and image files
wake only implements the second part: it optimises JavaScript, CSS and
binary files for production. It is configurable to allow for different use
cases; I have different concerns when working on an open-source library and when
working on an application, and wake supports both these use cases. Its default
settings are tailored to application deployment.
Because it only implements the optimisation phase, wake is not coupled to any
web framework. You can use it in any project, and it generates enough metadata
files that an application can easily find out which files it should generate
links to.
If you want to compile other languages to JS/CSS, or use a dependency resolver
like Browserify, that can be done as a separate step using Make (or one of its
clones), or by running tasks on file changes using Guard. After this compilation
step you can run wake on the resulting files to optimise them.
Here's a few of the things wake can do with your files:
- Concatenate and minify JavaScript and CSS files
- Generate optimised files with a content hash in the filename
- Generate source maps for JavaScript with correct relative path references
- Resolve and inline CSS files referenced by
@importstatements - Rewrite CSS
url()paths so the optimised CSS references the same files as the source code - Inline files referenced by
url()expressions as data URIs
Installation
$ npm install -g wakeUsage
wake is a command-line program; if you installed it globally then type wake
to run it, or if you installed it inside your project then run
./node_modules/.bin/wake.
wake is configured using data stored in your project's package.json file.
Its config has three sections: javascript, css and binary. Each section
has a sourceDirectory, which is the directory containing all the source files,
and a targetDirectory, which is where the optimised files are written to.
The css group also requires a sourceRoot setting, which tells wake where
to resolve absolute paths in url() expressions when it is inlining @import
statements and rewriting path references.
The only other required field is targets, which lists the name of each
optimised file and the names of the source files it should contain.
// package.json
{
"name": "my-project",
"version": "0.0.0",
"devDependencies": {
"wake": ">=0.2.0"
},
"wake": {
"javascript": {
"sourceDirectory": "public/javascripts",
"targetDirectory": "public/assets",
"targets": {
"scripts.js": ["foo.js", "bar.js"]
}
},
"css": {
"sourceDirectory": "public/stylesheets",
"targetDirectory": "public/assets",
"sourceRoot": "public",
"targets": {
"styles.css": ["navigation.css", "footer.css"]
}
},
"binary": {
"sourceDirectory": "public/images",
"targetDirectory": "public/assets",
"targets": {
"logo.png": "logo.png"
}
}
}
}This configuration takes the following project tree:
.
├── package.json
└── public
├── images
│ └── logo.png
├── javascripts
│ ├── bar.js
│ └── foo.js
└── stylesheets
├── footer.css
└── navigation.cssand generates the following files when you run wake:
.
├── .wake.json
└── public
└── assets
├── .manifest.json
├── logo.png
├── logo-2fa8d38.png
├── scripts.js
├── scripts-bb210c6.js
├── scripts.js.map
├── scripts-300b304.js.map
├── styles.css
└── styles-5a2ceb1.cssGenerated files
Two copies of each target are generated: one with a content hash in the filename and one without. The hashed names are for applications that can dynamically detect which files to serve, and the hashes serve to make the files indefinitely cacheable. The un-hashed names are for things that need predictable filenames, like the static page outside your web stack that runs your JS tests.
The build generates two metadata files that give you information about the generated assets, to make it easy to generate links to them from your application.
.wake.json is generated in the root of the project and contains an index
telling you the absolute paths of the source files and target files for each
asset in your configuration. For our example, this looks like:
{
"javascript": {
"scripts.js": {
"sources": [
"public/javascripts/foo.js",
"public/javascripts/bar.js"
],
"targets": {
"min": "public/assets/scripts.js"
}
}
},
"css": {
"styles.css": {
"sources": [
"public/stylesheets/navigation.css",
"public/stylesheets/footer.css"
],
"targets": {
"min": "public/assets/styles.css"
}
}
},
"binary": {
"logo.png": {
"sources": [
"public/images/logo.png"
],
"targets": {
"min": "public/assets/logo.png"
}
}
}
}The second metadata file is .manifest.json. This file is generated in each
directory that contains targets with content hashes in their filenames, and
contains a mapping from the canonical filename to the hashed one. This lets an
application find the hashed filename for each file it wants to link to.
// public/assets/.manifest.json
{
"scripts.js": "scripts-bb210c6.js",
"scripts.js.map": "scripts-300b304.js.map",
"styles.css": "styles-5a2ceb1.css",
"logo.png": "logo-2fa8d38.png"
}Configuration short-hands
It can be cumbersome to list out every file in the project, especially for binary files which just copy one file from one place to another. So, there are two shorthands you can use to make things easier.
Blank targets
If you give the empty string in place of the source list for a target, wake
just inserts the name of the target file as the name of the source. So, these
are equivalent:
"targets": { "targets": {
"logo.png": "" "logo.png": ["logo.png"]
} }Path globbing
Rather than list every file in the project, you can use a glob. For example,
this generates a target for every CSS file in public/stylesheets (but not its
subdirectories):
"css": {
"sourceDirectory": "public/stylesheets",
"targetDirectory": "public/assets",
"targets": {
"*.css": ""
}
}And this generates a target for each file in public/images and all its
subdirectories:
"binary": {
"sourceDirectory": "public/images",
"targetDirectory": "public/assets",
"targets": {
"**/*.*": ""
}
}Customising the build
wake comes with default behaviour that is optimised for application
development, but it can be customised to provide additional information your app
might need, or to change how it builds files. Consider our original example
JavaScript configuration:
"javascript": {
"sourceDirectory": "public/javascripts",
"targetDirectory": "public/assets",
"targets": {
"scripts.js": ["foo.js", "bar.js"]
}
}The source files are converted into the target files using a set of build settings. You can specify multiple builds with different settings. For example, this configuration generates a minified and unminified copy of the build with no content hash, which is more suitable for library development:
"javascript": {
"sourceDirectory": "public/javascripts",
"targetDirectory": "public/assets",
"builds": {
"src": {"digest": false, "minify": false},
"min": {"digest": false, "minify": true, "tag": "suffix", "sourceMap": "src"}
},
"targets": {
"scripts.js": ["foo.js", "bar.js"]
}
}This generates three files: scripts.js, scripts-min.js and
scripts-min.js.map, where the source map references scripts.js instead of
foo.js and bar.js.
By default, wake generates build settings for each group with a build called
min that's optimised for application development. If you specify your own
settings, these are the options you can set:
All asset types
digest- set tofalseif you don't want content hashes in generated filenamestag- specifies where to put the build name in the generated filenames.prefixputs the build name at the start of the filename, e.g.min-scripts.jssuffixputs the build name the end of the filename, e.g.scripts-min.jsdirectoryputs the file inside a directory named for the build, e.g.min/scripts.js
JavaScript assets
minify- set tofalseif you only want the files to be concatenated, not minifiedsafe- set totrueif you want the minifier to avoid potentially unsafe optimisationsseparator- sets the string inserted between files during concatenation, the default is two line feed characterssourceMap- set tofalseif you don't want a source map to be generated, set it to the name of a build (as above) if you want the map to reference that build as the source rather than the original source files
CSS assets
inline- set totrueif you want images, fonts and other files referenced byurl()expressions to be inlined as data URIsminify- set tofalseif you only want the files to be concatenated, not minifiedsafe- set totrueif you want the minifier to avoid potentially unsafe optimisationsseparator- sets the string inserted between files during concatenation, the default is two line feed characters
CSS asset hosts
If you're using multiple hostnames to serve assets from, you'll want to insert
these hostnames into the optimised CSS. You might have different hostnames for
HTTPS vs HTTP pages. To make wake add asset hosts to url() expressions, you
need to specifiy a few things in your css group config:
targetRoot- this is the document root of the server that provides your static assets, it will often be the same assourceRoothosts- this is an index of all your asset hosts, indexed by environment and typebuilds- each build must say which type of host it should use, if any
Here's an example. We have two sets of asset hosts, one called http and one
called https. Each environment we deploy our app to has a different list of
hostnames for each set. The hosts setting below lists the hosts for each set
for the production and staging environments.
"css": {
"sourceDirectory": "public/stylesheets",
"targetDirectory": "public/assets",
"sourceRoot": "public",
"targetRoot": "public",
"hosts": {
"production": {
"http": ["http://static1.example.com", "http://static2.example.com"],
"https": ["https://ssl1.example.com", "https://ssl2.example.com"]
},
"staging": {
"http": ["http://static1.staging.example.net", "http://static2.staging.example.net"],
"https": ["https://ssl1.staging.example.net", "https://ssl2.staging.example.net"]
}
},
"builds": {
"min": {"hosts": "http"},
"ssl": {"hosts": "https", "tag": "suffix"}
},
"targets": {
"styles.css": ["navigation.css", "footer.css"]
}
}The two builds here will generate two files: styles.css will use the http
set of hosts, and styles-ssl.css will use the https set. Which exact list of
hosts you get depends on the environment, which you specify on the command line:
$ WAKE_ENV=production wakeIf you don't specify an environment, wake will use the host sets under
hosts.default, if you have specified a default set.
License
(The MIT License)
Copyright (c) 2013 James Coglan
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.