Skip to content

Instantly share code, notes, and snippets.

@kirbysayshi
Created June 13, 2011 04:40
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kirbysayshi/1022323 to your computer and use it in GitHub Desktop.
Save kirbysayshi/1022323 to your computer and use it in GitHub Desktop.
quick nodejs script to combine, possibly minify, scripts, as well as precompile templates:

This is a quick example of taking a config file (example.js), and running jscombine.js. It will bundle up regular scripts as well as templates (hard coded to use doT). Compiled templates are automatically added to a "templates" property on the given namespace (ns key in the example file), keyed by a slightly transformed version of their filename.

// usage: node jscombine.js example.js
var inBase = __dirname + '/';
exports.bundles = [{
ns: 'MYTEST.stuff'
,inputs: [
{ uglify: false, template: false, path: inBase + 'jquery-1.6.1.min.js' }
,{ uglify: true, template: false, path: inBase + 'test.input.file.js' }
]
,output: __dirname + '/testoutput/all.js'
,uglify: true
},{
ns: false
,inputs: [
{ uglify: false, template: false, path: inBase + 'jquery-1.6.1.min.js' }
]
,output: __dirname + '/testoutput/jquery.min.js'
,uglify: true
}];
var fs = require('fs')
,dot = require('dot')
,uglify = require('uglify-js')
,sw = require('./stopwatch.js').stopwatch
,ugJsp = uglify.parser
,ugPro = uglify.uglify
,config;
function makeNamespaceString(ns){
var parts = ns.split('.')
,out = '';
parts.forEach(function(p, i){
var until
if(i == 0) {
out += p + ' = window.' + p + ' || {}; \n';
out += p + '.templates = ' + p + '.templates || {}; \n';
} else {
until = parts.slice(0, i).join('.');
out += until + '.' + p + ' = ' + until + '.' + p + ' || {}; \n';
}
});
return out;
}
function combineFiles(paths, target, uglify, namespace){
var i
,parts = []
,file
,ast
,ns = namespace === false
? ''
: makeNamespaceString( namespace || 'youve.probably.forgotten.something' )
,path;
for(i = 0; i < paths.length; i++){
path = paths[i].path;
file = fs.readFileSync(path, 'utf8');
if(paths[i].template){
file = dot.template(file).toString();
file = namespace + '.templates["'
+ path.split('/').pop().replace(/[\.\-]/gi, '_') + '"] = '
+ file;
}
if(uglify && paths[i].uglify){
// do uglify
ast = ugJsp.parse(file);
ast = ugPro.ast_mangle(ast);
ast = ugPro.ast_squeeze(ast);
file = ugPro.gen_code(ast);
}
parts.push(';' + file + ';');
}
fs.writeFileSync(target, ns + parts.join('\n'));
}
// these are here because window paths are a pain for cygwin-based nodejs as a VS pre-build event
dot.templateSettings.evaluate = /\{\{([\s\S]+?)\}\}/g;
dot.templateSettings.interpolate = /\$\{([\s\S]+?)\}/g;
// 0 = node, 1 = jscombine.js, 2 = path/to/config/file
if(!process.argv[2]) {
throw new Error('A complete file path must be passed as the first command line argument.');
}
config = require(process.argv[2]);
if(!config.bundles){
console.log(config);
throw new Error('No bundles specified in the given config file!');
}
sw.start('finished uglify');
config.bundles.forEach(function(bundle, i){
sw.start(bundle.output.split('/').pop());
combineFiles( bundle.inputs, bundle.output, bundle.uglify, bundle.ns);
sw.stopLog(bundle.output.split('/').pop(), true);
});
sw.stopLog('finished uglify', true);
process.exit();
var watches = {}
,enabled = true;
exports.max = 5 * 60 * 1000; // 5 minutes
setInterval(function(){
if(enabled === true){
for(var w in watches){
if( +new Date() - watches[w] > exports.max ){
delete watches[w];
console.log('culling stopwatch: ' + w);
}
}
}
}, exports.max);
exports.stopwatch = {
enable: function(){
enabled = true;
}
,disable: function(){
enabled = false;
}
,start: function(name){
if(!enabled) return;
if(!name) throw new Error('stopwatch.start must be given a name');
return watches[name] = +new Date();
}
,startLog: function(){
if(!enabled) return;
console.log('start -> ' + arguments[0] + ': ' + exports.stopwatch.start.apply(this, arguments));
}
,stop: function(name, asSeconds){
if(!enabled) return;
if(!name) throw new Error('stopwatch.stop must be given a name');
if(watches[name]){
return ((+new Date()) - watches[name]) * (asSeconds ? 0.001 : 1);
} else {
return 0;
}
}
,stopLog: function(){
if(!enabled) return;
console.log('stop -> ' + arguments[0] + ': ' + exports.stopwatch.stop.apply(this, arguments));
}
}
exports.connect = {
onIn: function(output){
return function(req, res, next){
if(output){
exports.stopwatch.startLog(req)
}
next();
}
}
,onNext: function(name, output){
return function swNext(req, res, next){
var stop = output ? exports.stopwatch.stopLog : exports.stopwatch.stop
,start = exports.stopwatch.start;
if(!enabled) return;
if(watches['middleware:' + name]){
stop('middleware:' + name, true);
}
start('middleware:' + name);
next();
}
}
,onOut: function(req, res, next){
next();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment