Last active
December 27, 2015 12:29
-
-
Save kaustubh-karkare/7326488 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
var fs = require("fs"), path = require("path"); | |
var run = function(root,start){ | |
var fs = require("fs"), path = require("path"); // intentionally redefined | |
var ext = ".js", index = "index"+ext, pkg = "package.json"; | |
var locate = function(root,name){ | |
var p = name.split(path.sep); | |
// all relative module references must be inside the specified directory | |
if(p[0]==="..") throw new Error("Out of Scope"); | |
// navigate to the appropriate location | |
for(var current = root; p.length>1; p.splice(0,1)) current = current[p[0]]; | |
// select module, appending the extension if required | |
return current[p[0]+(p[0]+ext in current ? ext : "")]; | |
}; | |
return (function(name,relative){ | |
if(typeof(name)!="string" || name==="" || name.split(path.sep).length<2 ){ | |
// in case of invalid input or external modules | |
return require(name); | |
} else { | |
var relname = path.normalize(path.join(relative,name)), | |
current = locate(root,relname); | |
// if this is a directory and a package.json exists, use the main value | |
check: try { | |
if(typeof(current)!=="object") break check; | |
var data = JSON.parse(current[pkg]); | |
if(typeof(data.main)!=="string") break check; | |
current = locate(current,path.normalize(data.main)); | |
relname = path.join(relname,data.main); | |
} catch(e){ } | |
// if this is a directory, load the index file by default | |
if(typeof(current)==="object"){ | |
current = current[index]; | |
relname = path.join(relname,index); | |
} | |
// load the module appropriately | |
if(typeof(current)==="function"){ | |
var module = { exports:{}, filename: relname }, | |
f1 = arguments.callee, | |
f2 = function(x){ return f1(x,path.dirname(relname)) }; | |
return current.cache || (current.cache = current(module, f2)); | |
} else if(typeof(current)==="string"){ | |
return current; | |
} else throw new Error("Invalid Path"); | |
} | |
})("."+path.sep+start,"."); // start execution | |
}; | |
var compress = (function(){ | |
try { | |
var uglify = require("uglify-js"), | |
options = { | |
fromString: true, | |
reserved: ["module","require"] | |
}; | |
} catch(e) { | |
var uglify = null; | |
} | |
return function(str){ | |
if(!uglify) return str; | |
return "(" + uglify.minify("_="+str,options).code.slice(2,-1) + ")"; | |
}; | |
})(); | |
var load = (function(){ | |
var ext = ".js", | |
prefix = "(function(module,require){\ndelete arguments;", | |
suffix = "\nreturn module.exports; })"; | |
// scan the directory recursively and read data into a JSON object | |
return function(current,options){ | |
var name, result = {}, list = fs.readdirSync(current); | |
// for each item in directory | |
for(var i=0; i<list.length; ++i){ | |
var fullpath = path.join(current,name=list[i]), | |
stats = fs.statSync(fullpath), | |
relpath = path.relative(options.basedir,fullpath); | |
// skip if found in exclusion list | |
if(options.exclude.filter(function(x){ return relpath.match(x); }).length) continue; | |
// if(options.exclude.indexOf( relpath )!=-1) continue; | |
if(stats.isDirectory()){ | |
// recursively load if this is a directory | |
result[name] = arguments.callee( fullpath, options ); | |
} else if(stats.isFile()){ | |
// read file and if executable, evaluate the wrapper function | |
result[name] = fs.readFileSync(fullpath).toString(); | |
if(name.slice(-ext.length)===ext) | |
try { result[name] = eval(compress(prefix+result[name]+suffix)); } | |
catch(e){ } | |
} | |
} | |
return result; | |
}; | |
})(); | |
var combine = (function(){ | |
var stringify = function(x){ | |
// to prevent JSON.stringify from quoting <function>.toString() | |
if(typeof(x)==="function") return x.toString(); | |
if(typeof(x)!="object") return JSON.stringify(x); | |
return "{"+Object.keys(x).map(function(key){ | |
return JSON.stringify(key)+":"+stringify(x[key]); | |
}).join(",")+"}"; | |
}; | |
return function(directory,options,start){ | |
// ensure that directory and start-file exist | |
if( !fs.statSync(directory).isDirectory() || | |
!fs.statSync(path.join(directory,start)).isFile() ) | |
throw new Error("Invalid Options"); | |
// options sanitization | |
if(typeof(options)!="object" || Array.isArray(options)) options = {}; | |
options.basedir = directory; | |
options.exclude = !Array.isArray(options.exclude) ? [] : | |
options.exclude.filter(function(x){ | |
return typeof(x)==="string" || x instanceof RegExp; }); | |
// generate output | |
var x = compress("("+run.toString()+")"), | |
y = stringify(load(directory,options)); | |
return x+"("+y+","+stringify(start)+")"; | |
}; | |
})(); | |
if(!module.parent){ // main | |
var argv = require("optimist").default("e",[".git","node_modules"]).argv; | |
if(argv._.length<3){ | |
console.log("\nIntent : Combine and compress multiple JavaScript files into a single equivalent file."); | |
console.log("Usage : "+argv.$0+" <directory> <main-script> <output-file> (-e <exclude-path>)* [-r]\n"); | |
} else { | |
argv.e = Array.isArray(argv.e) ? argv.e : [argv.e]; | |
var dir = argv._[0], start = argv._[1], target = argv._[2]; | |
var output = combine(dir,{exclude:argv.e},start); | |
if(target==="-") process.stdout.write(output+"\n"); | |
else fs.writeFile(target,output); | |
if(argv.r) eval(output); | |
} | |
} else { // api | |
module.exports = combine; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment