Merge pull request #1 from crcn/master

Pull in crcn/nexe master changes
This commit is contained in:
Adam Snodgrass
2014-02-28 18:14:51 -06:00
8 changed files with 79 additions and 312 deletions
+14 -9
View File
@@ -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
+1 -1
View File
@@ -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);
});
+5 -45
View File
@@ -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);
}
-254
View File
@@ -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
);
}
+3 -1
View File
@@ -1 +1,3 @@
module.exports = require("./exe");
module.exports = require("./exe");
+19
View File
@@ -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);
}
+32
View File
@@ -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);
}
+5 -2
View File
@@ -2,7 +2,7 @@
"author": "Craig Condon <craig.j.condon@gmail.com>",
"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"
}
}