diff --git a/README.md b/README.md index 429c2de..12cf2e5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,17 @@ -Compile javascript **with** node.js. This allows you to move your executable around *without* needing to install the node.js runtime. +### Nexe + +Nexe is a command-line utility that compiles your Node.js application into a single executable file. + +![screen shot 2014-02-28 at 10 32 54 am](https://f.cloud.github.com/assets/757408/2296993/c276f7b6-a0a6-11e3-86d3-e6c5feba2a85.png) + + +### Motivation + +- Ability to run multiple applications with *different* node.js runtimes. +- Distributable binaries without needing node / npm. +- Starts faster. +- Lockdown specific application versions, and easily rollback. +- Faster deployments. ## Requirements @@ -23,14 +36,6 @@ Or git: git clone ``` - -### Motivation - -- Developing client-side utilities without requiring to install a bunch of dependencies first (node.js, npm). -- Ability to run multiple node.js applications with *different* node.js runtimes. -- Distributable packages without needing node / npm. -- Loads faster. - ### CLI Usage ````text diff --git a/bin/nexe b/bin/nexe index 6230898..8f9f7ac 100755 --- a/bin/nexe +++ b/bin/nexe @@ -55,7 +55,7 @@ require('../lib').compile({ input : toRelative(argv.i), output : toRelative(argv.o), nodeVersion : argv.r, - temp : toRelative(argv.t) }, function(error) { + nodeTempDir : toRelative(argv.t) }, function(error) { if(error) console.log(error.message); }); diff --git a/lib/exe.js b/lib/exe.js index 4095e25..133604b 100644 --- a/lib/exe.js +++ b/lib/exe.js @@ -12,6 +12,10 @@ sardines = require("sardines"), os = require("os"), spawn = child_process.spawn; + +var _log = require("./log"), +_monkeypatch = require("./monkeypatch"); + /** */ @@ -26,7 +30,7 @@ exports.compile = function (options, complete) { */ function downloadNode (next) { - _downloadNode(options.nodeVersion, "/tmp/nexe", next); + _downloadNode(options.nodeVersion, options.nodeTempDir, next); }, /** @@ -257,33 +261,6 @@ function _monkeyPatchMainJs (compiler, complete) { ); } -/** - */ - -function _monkeypatch (filePath, monkeyPatched, processor, complete) { - - async.waterfall([ - - function read (next) { - fs.readFile(filePath, "utf8", next); - }, - - // TODO - need to parse gyp file - this is a bit hacker - function monkeypatch (content, next) { - - if (monkeyPatched(content)) return complete(); - - _log("monkey patch %s", filePath); - processor(content, next); - }, - - function write (content, next) { - fs.writeFile(filePath, content, "utf8", next); - } - ], complete); -} - - /** */ @@ -321,20 +298,3 @@ function _logProgress (req) { return req; } -/** - */ - -function _log () { - - var args = Array.prototype.slice.call(arguments, 0), - level = args.shift(); - - if (!~["log", "error", "warn"].indexOf(level)) { - args.unshift(level); - level = "log"; - } - - args[0] = "----> " + args[0]; - - console[level].apply(console, args); -} diff --git a/lib/index-old.js b/lib/index-old.js deleted file mode 100644 index 5df53b6..0000000 --- a/lib/index-old.js +++ /dev/null @@ -1,254 +0,0 @@ -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, args.temp, 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, temp, next) { - - var tmpPath = path.join(temp, "node-" + version + ".tar.gz"), - srcDr = tmpPath.replace(/\.tar.gz$/, ""); - - - 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/node-" + version + ".tar.gz").pipe(output); - - output.on("close", this); - }, - - /** - * untar - */ - - function() { - - //source dir exists? - if(fs.existsSync(srcDr)) return this(); - - try { - mkdirp.sync(srcDr); - } catch(e) { - - } - - //TODO - use native zlib library for this - exec(sprintf("tar -xf '%s' -C '%s'", tmpPath, srcDr), { cwd: path.dirname(tmpPath) }, this); - }, - - /** - * cleanup the node.js file - */ - - function() { - fs.unlink(tmpfile, this); - }, - - /** - * return make script - */ - - function() { - - console.log(srcDr) - - vDir = path.join(srcDr, fs.readdirSync(srcDr).filter(function(dir) { - return !/^\./.test(dir); - }).pop()); - - next(null, { - - //the directory to the node.js file - dir: vDir, - - releasePath: path.join(vDir, "out/Release/node"), - - //makes node.js - make: function(next) { - var configure = spawn('./configure', [], { cwd: vDir }); - configure.stdout.on('data', function(data) { - process.stdout.write(data.toString()); - }); - configure.stderr.on('data', function(data) { - process.stderr.write(data.toString()); - }); - configure.on('close', function() { - var make = spawn('make', [], { cwd: vDir }); - make.stdout.on('data', function(data) { - process.stdout.write(data.toString()); - }); - make.stderr.on('data', function(data) { - process.stderr.write(data.toString()); - }); - make.on('close', 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");\';\n process.argv.unshift("node");\n'), - "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 - ); -} diff --git a/lib/index.js b/lib/index.js index ba6760c..d7c212c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1 +1,3 @@ -module.exports = require("./exe"); \ No newline at end of file +module.exports = require("./exe"); + + diff --git a/lib/log.js b/lib/log.js new file mode 100644 index 0000000..a162c30 --- /dev/null +++ b/lib/log.js @@ -0,0 +1,19 @@ +module.exports = _log; + +/** + */ + +function _log () { + + var args = Array.prototype.slice.call(arguments, 0), + level = args.shift(); + + if (!~["log", "error", "warn"].indexOf(level)) { + args.unshift(level); + level = "log"; + } + + args[0] = "----> " + args[0]; + + console[level].apply(console, args); +} \ No newline at end of file diff --git a/lib/monkeypatch.js b/lib/monkeypatch.js new file mode 100644 index 0000000..0e78562 --- /dev/null +++ b/lib/monkeypatch.js @@ -0,0 +1,32 @@ +var async = require("async"), +fs = require("fs"); + +module.exports = _monkeypatch; + + +/** + */ + + +function _monkeypatch (filePath, monkeyPatched, processor, complete) { + + async.waterfall([ + + function read (next) { + fs.readFile(filePath, "utf8", next); + }, + + // TODO - need to parse gyp file - this is a bit hacker + function monkeypatch (content, next) { + + if (monkeyPatched(content)) return complete(); + + _log("monkey patch %s", filePath); + processor(content, next); + }, + + function write (content, next) { + fs.writeFile(filePath, content, "utf8", next); + } + ], complete); +} \ No newline at end of file diff --git a/package.json b/package.json index 3f6c268..8a03ba9 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Craig Condon ", "name": "nexe", "description": "Roll node.s applications into a single executable", - "version": "0.2.4", + "version": "0.2.6", "repository": { "type": "git", "url": "git://github.com/crcn/nexe.git" @@ -25,5 +25,8 @@ "bin": { "nexe": "./bin/nexe" }, - "devDependencies": {} + "devDependencies": { + "expect.js": "~0.3.1", + "mocha": "~1.17.1" + } }