Let's Make a Framework: Node Packaging
Welcome to part 32 of Let’s Make a Framework, the ongoing series about building a JavaScript framework.
If you haven’t been following along, these articles are tagged with lmaf. The project we’re creating is called Turing.
Last week I wrote about building an event delegation chaining API. This week I’m going to talk about packaging.
Packaging with Node
In part 12 I talked about using Narwhal to create a Jakefile to build and minimise Turing. I thought it would be interesting to look at doing the same thing with Node, because I know a lot of DailyJS’s readers already have Node installed.
I’m using node-jake, available from npm with npm install jake.
Detecting Node or Rhino
At the moment Narwhal uses Rhino. Narwhal and Node have slightly different globals available, so I’m using these to detect the active environment:
if (typeof global.system === 'undefined') {
nodeTasks();
} else {
narwhalTasks();
}
If you’re writing your own build tasks I strongly recommend selecting a single build tool (or perhaps one to suit each major platform). My Jakefile only caters for both to support these tutorials, it’s actually rather impractical.
File Concatenation
I’ve made the concat task synchronous to make it easier to read:
task('concat', [], function () {
var files = ('turing.core.js turing.oo.js turing.enumerable.js '
+ 'turing.functional.js turing.dom.js turing.events.js '
+ 'turing.touch.js turing.alias.js turing.anim.js').split(' '),
filesLeft = files.length,
pathName = '.',
outFile = fs.openSync('build/turing.js', 'w+');
files.forEach(function(fileName) {
var fileName = path.join(pathName, fileName),
contents = fs.readFileSync(fileName);
sys.puts('Read: ' + contents.length + ', written: ' + fs.writeSync(outFile, contents.toString()));
});
fs.closeSync(outFile);
});
The output file is opened first, then each file is read into it.
Minifier
There’s also a npm package for minimising JavaScript, called node-jsmin. It’s pretty easy to use:
var jsmin = require('jsmin').jsmin;
jsmin(code);
This is based on Crockford’s original script. You might get better results by pasting Turing into Google Closure or Dean Edwards’ Packer.
For now I’m going to stub this task out until there’s a better minifier.
I haven’t found any particularly strong minifiers for Node yet. jQuery actually uses Google’s compiler, as a Jar file. That means they can also run jslint as well, which is useful. These could be run in our Jakefile simply by shelling out. It’s also interesting to note that jQuery will build with unix make, Ruby’s rake and ant (which makes life easier for Windows users).
Organising Tasks
node-jake allows you to run a list of tasks before the current one:
desc('Main build task');
task('build', ['concat', 'minify'], function() {});
This is equivalent to the old Narwhal build task:
jake.task('build', ['concat', 'minify'])
280 North’s Jake is clever enough to realise a function hasn’t been specified, whereas the current version of node-jake will crash if you run: task('build', ['concat', 'minify']);
Running Tasks
After installing node-jake, run it with jake -T to see a list of tasks. To build the project, run jake build.
A default task could be added (called default) but I’d like to use that to generate documentation and I haven’t picked a JavaScript-friendly documentation system to do that yet.
Conclusion
Both node-jake and 280 North’s Jake are largely compatible, and are good choices for your own project. If you’re working on a node or Narwhal project, the choice is obvious, but when you’re building a web-based library it’s really up to you.