1"use strict"; 2var __importDefault = (this && this.__importDefault) || function (mod) { 3 return (mod && mod.__esModule) ? mod : { "default": mod }; 4}; 5Object.defineProperty(exports, "__esModule", { value: true }); 6exports.foregroundChild = exports.normalizeFgArgs = void 0; 7const child_process_1 = require("child_process"); 8const cross_spawn_1 = __importDefault(require("cross-spawn")); 9const signal_exit_1 = require("signal-exit"); 10const all_signals_js_1 = require("./all-signals.js"); 11const watchdog_js_1 = require("./watchdog.js"); 12/* c8 ignore start */ 13const spawn = process?.platform === 'win32' ? cross_spawn_1.default : child_process_1.spawn; 14/** 15 * Normalizes the arguments passed to `foregroundChild`. 16 * 17 * Exposed for testing. 18 * 19 * @internal 20 */ 21const normalizeFgArgs = (fgArgs) => { 22 let [program, args = [], spawnOpts = {}, cleanup = () => { }] = fgArgs; 23 if (typeof args === 'function') { 24 cleanup = args; 25 spawnOpts = {}; 26 args = []; 27 } 28 else if (!!args && typeof args === 'object' && !Array.isArray(args)) { 29 if (typeof spawnOpts === 'function') 30 cleanup = spawnOpts; 31 spawnOpts = args; 32 args = []; 33 } 34 else if (typeof spawnOpts === 'function') { 35 cleanup = spawnOpts; 36 spawnOpts = {}; 37 } 38 if (Array.isArray(program)) { 39 const [pp, ...pa] = program; 40 program = pp; 41 args = pa; 42 } 43 return [program, args, { ...spawnOpts }, cleanup]; 44}; 45exports.normalizeFgArgs = normalizeFgArgs; 46function foregroundChild(...fgArgs) { 47 const [program, args, spawnOpts, cleanup] = (0, exports.normalizeFgArgs)(fgArgs); 48 spawnOpts.stdio = [0, 1, 2]; 49 if (process.send) { 50 spawnOpts.stdio.push('ipc'); 51 } 52 const child = spawn(program, args, spawnOpts); 53 const unproxySignals = proxySignals(child); 54 const childHangup = () => { 55 try { 56 child.kill('SIGHUP'); 57 /* c8 ignore start */ 58 } 59 catch (_) { 60 // SIGHUP is weird on windows 61 child.kill('SIGTERM'); 62 } 63 /* c8 ignore stop */ 64 }; 65 const removeOnExit = (0, signal_exit_1.onExit)(childHangup); 66 const dog = (0, watchdog_js_1.watchdog)(child); 67 let done = false; 68 child.on('close', async (code, signal) => { 69 dog.kill('SIGKILL'); 70 /* c8 ignore start */ 71 if (done) { 72 return; 73 } 74 /* c8 ignore stop */ 75 done = true; 76 const result = cleanup(code, signal); 77 const res = isPromise(result) ? await result : result; 78 removeOnExit(); 79 unproxySignals(); 80 if (res === false) 81 return; 82 else if (typeof res === 'string') { 83 signal = res; 84 code = null; 85 } 86 else if (typeof res === 'number') { 87 code = res; 88 signal = null; 89 } 90 if (signal) { 91 // If there is nothing else keeping the event loop alive, 92 // then there's a race between a graceful exit and getting 93 // the signal to this process. Put this timeout here to 94 // make sure we're still alive to get the signal, and thus 95 // exit with the intended signal code. 96 /* istanbul ignore next */ 97 setTimeout(() => { }, 2000); 98 try { 99 process.kill(process.pid, signal); 100 /* c8 ignore start */ 101 } 102 catch (_) { 103 process.kill(process.pid, 'SIGTERM'); 104 } 105 /* c8 ignore stop */ 106 } 107 else { 108 process.exit(code || 0); 109 } 110 }); 111 if (process.send) { 112 process.removeAllListeners('message'); 113 child.on('message', (message, sendHandle) => { 114 process.send?.(message, sendHandle); 115 }); 116 process.on('message', (message, sendHandle) => { 117 child.send(message, sendHandle); 118 }); 119 } 120 return child; 121} 122exports.foregroundChild = foregroundChild; 123/** 124 * Starts forwarding signals to `child` through `parent`. 125 */ 126const proxySignals = (child) => { 127 const listeners = new Map(); 128 for (const sig of all_signals_js_1.allSignals) { 129 const listener = () => { 130 // some signals can only be received, not sent 131 try { 132 child.kill(sig); 133 /* c8 ignore start */ 134 } 135 catch (_) { } 136 /* c8 ignore stop */ 137 }; 138 try { 139 // if it's a signal this system doesn't recognize, skip it 140 process.on(sig, listener); 141 listeners.set(sig, listener); 142 /* c8 ignore start */ 143 } 144 catch (_) { } 145 /* c8 ignore stop */ 146 } 147 return () => { 148 for (const [sig, listener] of listeners) { 149 process.removeListener(sig, listener); 150 } 151 }; 152}; 153const isPromise = (o) => !!o && typeof o === 'object' && typeof o.then === 'function'; 154//# sourceMappingURL=index.js.map