Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
node.js build script using commander and shelljs
var DIST_FOLDER = '../site/public';
// ----
// more references:
var _cli = require('commander'),
_requirejs = require('requirejs'),
_path = require('path'),
_fs = require('fs'),
_shell = require('shelljs');
// ---
.description('optimize JS files. combine into fewer files to improve load performance')
.description('compile Sass files into CSS')
.description('optimize JS, compile SASS, copy all assets to the "./site" folder')
// ----
function rjs(opts, cb){
var defaultSettings = {
logLevel: 1,
// optimize: 'none',
baseUrl: 'scripts',
mainConfigFile : 'scripts/main.js',
paths: {
// TODO: load jquery from CDN
'jquery': 'lib/jquery/jquery'
for(var key in opts) {
defaultSettings[key] = opts[key];
_requirejs.optimize(defaultSettings, cb);
function optimizeJS(cb){
name: 'main',
out: 'scripts/main.min.js',
stubModules: [
include: [
// map is loaded dynamically to avoid loading gmaps before needed
// TODO: sub-sections should probably be loaded dynamically
// exclude: [ ],
onBuildWrite : function(moduleName, path, content){
// replace handlebars with the runtime version (which is many times
// smaller)
if (moduleName === 'Handlebars') {
content = _fs.readFileSync('scripts/lib/handlebars.runtime.js').toString();
content = content.replace(/(define\()(function)/, '$1"Handlebars", $2');
return content;
}, cb);
// ---
function compileSass(){
if (! _shell.which('compass') ) {
console.error('this build script requires "compass" to be installed globally.');
_shell.exec('compass compile .');
// ---
function deploy(){
var assetsDir = _path.join(DIST_FOLDER, 'assets');
var tempDir = _path.join(DIST_FOLDER, 'temp');
_shell.mkdir('-p', assetsDir);
_shell.mkdir('-p', tempDir);
// purge old files
_shell.rm('-Rf', _path.join(assetsDir, 'styles'));
_shell.rm('-Rf', _path.join(assetsDir, 'js'));
_shell.rm('-Rf', _path.join(assetsDir, 'img'));
_shell.rm('-Rf', _path.join(assetsDir, 'webfonts'));
_shell.rm('-Rf', _path.join(tempDir, 'content'));
// ---
_shell.cp('-R', 'img', assetsDir);
_shell.cp('-R', 'webfonts', assetsDir);
_shell.cp('-R', 'content', tempDir);
var jsDir = _path.join(assetsDir, 'js');
_shell.mkdir('-p', jsDir);
// TODO: minify requirejs
_shell.cp('scripts/lib/require/require.js', _path.join(jsDir, 'require.js'));
_shell.cp('scripts/main.min.js', _path.join(jsDir, 'main.js'));
// need to keep same depth to avoid issues with webfonts/image paths
var cssDir = _path.join(assetsDir, 'styles/css');
_shell.mkdir('-p', cssDir);
_shell.cp('styles/css/*', cssDir);
Copy link

I wrote this build script yesterday for a project in less than 20 minutes. I don't think it would be any simpler to write it with a tool like grunt, and if the build needs to be more complex in the future I can easily augment/refactor it.

Grunt is not all bad since you can still write plain JavaScript inside the tasks, but if you are writing plain JS why tie yourself to a single task runner?

It's a shame to see so many useful tools tied to a single task runner for no real benefit... Grunt and any other similar tool should not need to exist since we already have dozens of argument parsers.

PS: the build script above could be refactored to avoid duplications but I won't do it until it is needed, 160 LOC is pretty easy to manage - verbosity is not always a bad thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment