Skip to content

Instantly share code, notes, and snippets.

@millermedeiros
Created June 6, 2013 14:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save millermedeiros/5721901 to your computer and use it in GitHub Desktop.
Save millermedeiros/5721901 to your computer and use it in GitHub Desktop.
node.js build script using commander and shelljs
var DIST_FOLDER = '../site/public';
// ----
// more references:
// https://gist.github.com/millermedeiros/2640928
// https://gist.github.com/millermedeiros/4724047
var _cli = require('commander'),
_requirejs = require('requirejs'),
_path = require('path'),
_fs = require('fs'),
_shell = require('shelljs');
// ---
_cli
.command('js')
.description('optimize JS files. combine into fewer files to improve load performance')
.action(optimizeJS);
_cli
.command('css')
.description('compile Sass files into CSS')
.action(compileSass);
_cli
.command('deploy')
.description('optimize JS, compile SASS, copy all assets to the "./site" folder')
.action(deploy);
_cli.parse(process.argv);
// ----
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){
rjs({
name: 'main',
out: 'scripts/main.min.js',
stubModules: [
'text',
'hbs'
],
include: [
// map is loaded dynamically to avoid loading gmaps before needed
'widgets/map/Map',
'widgets/map/HotelMarker',
'widgets/map/LocationMarker',
'widgets/hotelpicker/Marker',
'booking/entry/LocationMarker',
// TODO: sub-sections should probably be loaded dynamically
'booking/entry/entry',
'booking/confirm/confirm',
'property/entry/entry',
'property/city/city',
'property/explore/explore'
],
// 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. http://compass-style.org/install/');
process.exit(1);
}
_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);
compileSass();
optimizeJS(function(){
// 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);
});
}
@millermedeiros
Copy link
Author

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