Files
archived-nodemon/lib/monitor/run.js

165 lines
4.8 KiB
JavaScript

var utils = require('../utils'),
bus = utils.bus,
childProcess = require('child_process'),
spawn = childProcess.spawn,
exec = childProcess.exec,
watch = require('./watch'),
config = require('../config'),
child = null, // the actual child process we spawn
killedAfterChange = false,
timeout = 1000, // check every 1 second
restart = null;
function run(options) {
var command = run.command(options),
nodeMajor = parseInt((process.versions.node.split('.') || [null,null])[1] || 0, 10);
utils.log.status('starting `' + command.executable + (command.args.length ? ' ' + command.args.join(' ') : '') + '`');
restart = run.bind(this, options);
run.restart = restart;
config.lastStarted = Date.now();
if (nodeMajor >= 8) {
child = spawn(command.executable, command.args, {
stdio: ['pipe', process.stdout, process.stderr]
});
} else {
child = spawn(command.executable, command.args);
child.stdout.on('data', function (data) {
process.stdout.write(data);
});
child.stderr.on('data', function (data) {
process.stderr.write(data);
});
}
bus.emit('start');
utils.log.detail('pid: ' + child.pid);
child.on('error', function (error) {
if (error.code === 'ENOENT') {
utils.log.error('unable to spawn executable: ' + executable);
process.exit(1);
}
});
child.on('exit', function (code, signal) {
// In case we killed the app ourselves, set the signal thusly
if (killedAfterChange) {
killedAfterChange = false;
signal = 'SIGUSR2';
}
// this is nasty, but it gives it windows support
if (utils.isWindows && signal === 'SIGTERM') {
signal = 'SIGUSR2';
}
// exit the monitor, but do it gracefully
if (signal === 'SIGUSR2') {
// restart
restart();
} else if (code === 0) { // clean exit - wait until file change to restart
utils.log.status('clean exit - waiting for changes before restart');
child = null;
} else if (options.exitcrash) {
utils.log.fail('app crashed');
process.exit(0);
} else {
utils.log.fail('app crashed - waiting for file changes before starting...');
child = null;
}
});
process.on('exit', function () {
if (child) bus.emit('kill');
});
run.kill = function () {
if (child !== null) {
// When using CoffeeScript under Windows, child's process is not node.exe
// Instead coffee.cmd is launched, which launches cmd.exe, which starts
// node.exe as a child process child.kill() would only kill cmd.exe, not
// node.exe
// Therefore we use the Windows taskkill utility to kill the process and all
// its children (/T for tree)
if (utils.isWindows) {
// For the on('exit', ...) handler above the following looks like a crash,
// so we set the killedAfterChange flag
killedAfterChange = true;
// Force kill (/F) the whole child tree (/T) by PID (/PID 123)
exec('taskkill /pid '+ child.pid + ' /T /F');
} else {
child.kill('SIGUSR2');
}
} else {
// if there's no child, then we need to manually start the process
// this is because as there was no child, the child.on('exit') event
// handler doesn't exist which would normally trigger the restart.
restart();
}
};
// pinched from https://github.com/DTrejo/run.js - pipes stdin to the child process - cheers DTrejo ;-)
if (options.stdin) {
process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.pipe(child.stdin);
}
watch();
setTimeout(watch, timeout);
}
run.command = function (options) {
var executable = options.execOptions.exec,
args = [];
// after "executable" go the exec args (like --debug, etc)
if (options.execOptions.execArgs) {
[].push.apply(args, options.execOptions.execArgs);
}
// after the "executable" goes the user's script
if (options.script) {
args.push(options.script);
}
// then goes the user's script arguments
if (options.args) {
[].push.apply(args, options.args);
}
return {
executable: executable,
args: args
}
};
// stubbed out for now, filled in during run
run.kill = function () {};
run.restart = function () {};
bus.on('quit', function () {
// remove event listener
var exit = function () {
exit = function () {}; // null out in case of race condition
process.exit(0);
};
child.removeAllListeners('exit');
child.on('exit', exit);
child.kill('SIGINT');
setTimeout(exit, 10 * 1000); // give up waiting for the kids after 10 seconds
});
bus.on('restart', function () {
// run.kill will send a SIGINT to the child process, which will cause it
// to terminate, which in turn uses the 'exit' event handler to restart
run.kill();
});
module.exports = run;