Skip to content

Instantly share code, notes, and snippets.

@piranha
Last active December 10, 2015 22:38
Show Gist options
  • Save piranha/4504099 to your computer and use it in GitHub Desktop.
Save piranha/4504099 to your computer and use it in GitHub Desktop.
Custom RequireJS packer/optimizer & md5 map generator.
#!/usr/bin/env node
// (c) 2012-2013 Instant Communication Bhd, under terms of ISC License.
//
// Process index.html to add md5ized path mapping for RequireJS.
//
// Needs empty CacheBust variable to be defined (see replacement logic), which
// afterwards should be used in `require.config({paths: CacheBust});` call.
var fs = require('fs');
var exec = require('child_process').exec;
var crypto = require('crypto');
var path = require('path');
function write(index, md5map) {
index = fs.readFileSync(index).toString();
md5map = JSON.stringify(md5map);
index = index
.replace('CacheBust = {}',
'CacheBust = ' + md5map);
console.log(index);
}
function md5(fn) {
var hasher = crypto.createHash('md5');
hasher.update(fs.readFileSync(fn));
return hasher.digest('hex');
}
function findFiles(name) {
var stat = fs.statSync(name);
if (stat.isDirectory()) {
var files = fs.readdirSync(name);
return files
.map(function(fn) {
return findFiles(path.join(name, fn));
})
.filter(function(x) { return x; })
.reduce(function(vals, x) {
return Array.isArray(x) ? vals.concat(x) : vals.push(x) && vals;
}, []);
}
var ext = name.slice(name.lastIndexOf('.') + 1);
if (ext == 'js' || ext == 'json' || ext == 'css') {
return {name: name, value: name + '?' + md5(name)};
}
}
function stripname(dir, path) {
if (dir.length) {
path = path.slice(dir.length + 1);
}
var ext = path.slice(path.lastIndexOf('.') + 1);
if (ext == 'js') {
path = path.slice(0, path.length - 3);
}
return path;
}
function run() {
if (process.argv.length < 4) {
console.log('Usage: ' + process.argv[1] +
' path/to/index.html paths/to prod/dirs\n');
process.exit(0);
}
var index = process.argv[2];
var dirs = process.argv.slice(3);
var dir, i, files, map = {};
for (i = 0; i < dirs.length; i++) {
dir = dirs[i];
findFiles(dir).forEach(function(x) {
map[stripname(dir, x.name)] = stripname(dir, x.value);
});
}
write(index, map);
}
run();
#!/usr/bin/env node
// (c) 2012-2013 Instant Communication Bhd, under terms of ISC License.
var fs = require('fs');
var path = require('path');
var getopt = require('node-getopt');
var uglify = require('uglify-js');
var jsp = uglify.parser;
var pro = uglify.uglify;
var parsed = require('node-getopt').create([
['b', 'base=BASE', 'Base path'],
['o', 'output=OUTPUT', 'Output path']
])
.bindHelp()
.parseSystem();
var args = parsed.argv, opts = parsed.options;
var commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg;
var cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g;
function moduleFromPath(path) {
if (path.slice(path.length - 3) === '.js') {
path = path.slice(0, path.length - 3);
}
if (path.slice(0, opts.base.length) == opts.base) {
path = path.slice(opts.base.length);
}
return path;
}
function getpath(module) {
return path.join(opts.base, module + '.js');
}
function relativeModule(parent, module) {
return path.join(path.dirname(parent), module);
}
function parseModule(found, module) {
if (found[module]) {
return;
}
console.log(' ...processing ' + module);
var content = fs.readFileSync(getpath(module), 'utf8').toString();
var deps = ['require', 'exports', 'module'];
content
.replace(commentRegExp, '')
.replace(cjsRequireRegExp, function(match, dep) {
deps.push(dep);
if (dep[0] === '.') {
parseModule(found, relativeModule(module, dep));
}
});
var def = 'define("' + module + '", ';
if (content.match(/define\( *function/)) {
def = def + '["' + deps.join('", "') + '"], ';
} else {
def = def + '[], ';
}
found[module] = content.replace(/define\(/, def);
return found;
}
function compress(code) {
var ast;
ast = jsp.parse(code);
ast = pro.ast_mangle(ast, {
mangle: true,
mangle_toplevel: false,
defines: {},
except: null,
no_functions: false
});
ast = pro.ast_squeeze(ast, {
make_seqs: false,
dead_code: true,
keep_comps: true,
unsafe: false
});
return pro.gen_code(ast, {
ascii_only: false,
beautify: false,
indent_level: 4,
indent_start: 0,
quote_keys: false,
space_colon: false,
inline_script: false
});
}
function main() {
if (!args.length) {
console.log('Run ' + process.argv[1] + ' --help to see usage\n');
process.exit(0);
}
var modules = {};
args.forEach(function(module) {
module = moduleFromPath(module);
console.log('Traversing from ' + module + '...');
parseModule(modules, module);
});
var content = '';
for (var module in modules) {
content += modules[module];
}
var compressed = compress(content);
if (opts.output) {
fs.writeFileSync(opts.output, compressed, 'utf8');
console.log('Packed to ' + opts.output);
} else {
console.log(compressed);
}
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment