123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- const signalExit = require('signal-exit')
- /* istanbul ignore next */
- const spawn = process.platform === 'win32' ? require('cross-spawn') : require('child_process').spawn
-
- /**
- * Normalizes the arguments passed to `foregroundChild`.
- *
- * See the signature of `foregroundChild` for the supported arguments.
- *
- * @param fgArgs Array of arguments passed to `foregroundChild`.
- * @return Normalized arguments
- * @internal
- */
- function normalizeFgArgs(fgArgs) {
- let program, args, cb;
- let processArgsEnd = fgArgs.length;
- const lastFgArg = fgArgs[fgArgs.length - 1];
- if (typeof lastFgArg === "function") {
- cb = lastFgArg;
- processArgsEnd -= 1;
- } else {
- cb = (done) => done();
- }
-
- if (Array.isArray(fgArgs[0])) {
- [program, ...args] = fgArgs[0];
- } else {
- program = fgArgs[0];
- args = Array.isArray(fgArgs[1]) ? fgArgs[1] : fgArgs.slice(1, processArgsEnd);
- }
-
- return {program, args, cb};
- }
-
- /**
- *
- * Signatures:
- * ```
- * (program: string | string[], cb?: CloseHandler);
- * (program: string, args: string[], cb?: CloseHandler);
- * (program: string, ...args: string[], cb?: CloseHandler);
- * ```
- */
- function foregroundChild (...fgArgs) {
- const {program, args, cb} = normalizeFgArgs(fgArgs);
-
- const spawnOpts = { stdio: [0, 1, 2] }
-
- if (process.send) {
- spawnOpts.stdio.push('ipc')
- }
-
- const child = spawn(program, args, spawnOpts)
-
- const unproxySignals = proxySignals(process, child)
- process.on('exit', childHangup)
- function childHangup () {
- child.kill('SIGHUP')
- }
-
- child.on('close', (code, signal) => {
- // Allow the callback to inspect the child’s exit code and/or modify it.
- process.exitCode = signal ? 128 + signal : code
-
- let done = false;
- const doneCB = () => {
- if (done) {
- return
- }
-
- done = true
- unproxySignals()
- process.removeListener('exit', childHangup)
- 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.
- /* istanbul ignore next */
- setTimeout(function () {}, 200)
- process.kill(process.pid, signal)
- } else {
- process.exit(process.exitCode)
- }
- };
-
- const result = cb(doneCB);
- if (result && result.then) {
- result.then(doneCB);
- }
- })
-
- if (process.send) {
- process.removeAllListeners('message')
-
- child.on('message', (message, sendHandle) => {
- process.send(message, sendHandle)
- })
-
- process.on('message', (message, sendHandle) => {
- child.send(message, sendHandle)
- })
- }
-
- return child
- }
-
- /**
- * Starts forwarding signals to `child` through `parent`.
- *
- * @param parent Parent process.
- * @param child Child Process.
- * @return `unproxy` function to stop the forwarding.
- * @internal
- */
- function proxySignals (parent, child) {
- const listeners = new Map()
-
- for (const sig of signalExit.signals()) {
- const listener = () => child.kill(sig)
- listeners.set(sig, listener)
- parent.on(sig, listener)
- }
-
- return function unproxySignals () {
- for (const [sig, listener] of listeners) {
- parent.removeListener(sig, listener)
- }
- }
- }
-
- module.exports = foregroundChild
|