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