UNPKG

nexe

Version:

Roll node.s applications into a single executable

244 lines (175 loc) 4.46 kB
var step = require("step"), outcome = require("outcome"), request = require("request"), fs = require("fs"), mkdirp = require("mkdirp"), path = require("path"), child_process = require("child_process"), exec = child_process.exec, spawn = child_process.spawn, sprintf = require("sprintf").sprintf, ncp = require("ncp").ncp, sardines = require("sardines"); /** */ exports.compile = function(args, next) { var on = outcome.error(next); args.input = require.resolve(args.input); step( /** * 1. download node */ function() { downloadNode(args.runtime, this); }, /** * 2. build the script into ONE file */ on.success(function(node) { console.log("making application bundle with %s", args.input); this.node = node; sardines({ entries: [args.input], platform: "node" }, this); }), on.success(function(content) { console.log("writing application bundle: \n\n%s\n\n", content); fs.writeFile(this.nexePath = path.join(this.node.dir, "lib", "nexe.js"), content, "utf8", this); }), /** * 4. monkeypatch the node entry */ on.success(function() { console.log("monkey patching node.js file"); monkeyPatchNodejs(this.node, this); }), /** * 5. bundle it all together */ on.success(function() { console.log("building node.js"); this.node.make(this); }), /** * 6. copy the executable */ on.success(function() { console.log("copying binary to output directory to %s", args.output); mkdirp(path.dirname(args.output), this); }), /** */ function() { ncp(this.node.releasePath, args.output, this); }, /** */ next ) } /** */ function downloadNode(version, next) { var tmpPath = path.join("/tmp/nexe", "node-v" + version + ".tar.gz"), srcDr = tmpPath.replace(/\.tar.gz$/, ""); try { mkdirp.sync(path.dirname(tmpPath)) //an error will usually occur when the dir already exists. } catch(e) { } step( /** * download */ function() { //zip exists? if(fs.existsSync(tmpPath) || fs.existsSync(srcDr)) return this(); console.log("downloading node %s", version); var output = fs.createWriteStream(tmpPath, { "flags": "w+" }), req = request("http://nodejs.org/dist/v" + version + "/node-v" + version + ".tar.gz").pipe(output); output.on("close", this); }, /** * untar */ function() { //source dir exists? if(fs.existsSync(srcDr)) return this(); console.log("unzipping node source"); //TODO - use native zlib library for this exec(sprintf("tar -xf '%s'", tmpPath), { cwd: path.dirname(tmpPath) }, this); }, /** * cleanup the node.js file */ function() { fs.unlink(tmpfile, this); }, /** * return make script */ function() { next(null, { //the directory to the node.js file dir: srcDr, releasePath: path.join(srcDr, "out/Release/node"), //makes node.js make: function(next) { var proc = exec(sprintf("cd %s; ./configure; make; ", srcDr)); proc.stdout._events = {}; //remove stdout & stdin capture proc.stdin._events = {}; proc.stdout.on("data", function(chunk) { process.stdout.write(chunk); }); proc.stderr.on("data", function(chunk) { process.stdout.write(chunk); }); proc.on("exit", next); } }); } ); } /** */ function monkeyPatchNodejs(node, next) { var nodejsPath = path.join(node.dir, "src", "node.js"), nodegypPath = path.join(node.dir, "node.gyp"), on = outcome.error(next); step( /** * read the node.js file */ function() { fs.readFile(nodejsPath, "utf8", this); }, /** * inject nexe as _eval IF it hasn't already been injected */ on.success(function(content) { //nexe already injected? if(~content.indexOf('nexe')) return this(); fs.writeFile(nodejsPath, content.replace(/\(function\(process\) \{/,'(function(process){ \n process._eval = \'require("nexe");\'; '), "utf8", this); }), /** * next, find node.gyp and add nexe as a native lib file */ on.success(function() { fs.readFile(nodegypPath, "utf8", this); }), /** * finally, monkeypatch node.gyp */ on.success(function(content) { //nexe already included as dep? if(~content.indexOf('lib/nexe.js')) return this(); fs.writeFile(nodegypPath, content.replace("'lib/fs.js',", "'lib/fs.js', 'lib/nexe.js', "), "utf8", this); }), /** */ next ); }