var signalExit = require('signal-exit') var spawn = require('child_process').spawn if (process.platform === 'win32') { spawn = require('cross-spawn') } module.exports = function (program, args, cb) { var arrayIndex = arguments.length if (typeof args === 'function') { cb = args args = undefined } else { cb = Array.prototype.slice.call(arguments).filter(function (arg, i) { if (typeof arg === 'function') { arrayIndex = i return true } })[0] } cb = cb || function (done) { return done() } if (Array.isArray(program)) { args = program.slice(1) program = program[0] } else if (!Array.isArray(args)) { args = [].slice.call(arguments, 1, arrayIndex) } var spawnOpts = { stdio: [0, 1, 2] } if (process.send) { spawnOpts.stdio.push('ipc') } var child = spawn(program, args, spawnOpts) var childExited = false var unproxySignals = proxySignals(child) process.on('exit', childHangup) function childHangup () { child.kill('SIGHUP') } child.on('close', function (code, signal) { // Allow the callback to inspect the child’s exit code and/or modify it. process.exitCode = signal ? 128 + signal : code cb(function () { unproxySignals() process.removeListener('exit', childHangup) childExited = true if (signal) { // If there is nothing else keeping the event loop alive, // then there's a race between a graceful exit and getting // the signal to this process. Put this timeout here to // make sure we're still alive to get the signal, and thus // exit with the intended signal code. setTimeout(function () {}, 200) process.kill(process.pid, signal) } else { // Equivalent to process.exit() on Node.js >= 0.11.8 process.exit(process.exitCode) } }) }) if (process.send) { process.removeAllListeners('message') child.on('message', function (message, sendHandle) { process.send(message, sendHandle) }) process.on('message', function (message, sendHandle) { child.send(message, sendHandle) }) } return child } function proxySignals (child) { var listeners = {} signalExit.signals().forEach(function (sig) { listeners[sig] = function () { child.kill(sig) } process.on(sig, listeners[sig]) }) return unproxySignals function unproxySignals () { for (var sig in listeners) { process.removeListener(sig, listeners[sig]) } } }