123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- import {once} from 'node:events';
- import {isStream as isNodeStream} from 'is-stream';
- import {throwOnTimeout} from '../terminate/timeout.js';
- import {throwOnCancel} from '../terminate/cancel.js';
- import {throwOnGracefulCancel} from '../terminate/graceful.js';
- import {isStandardStream} from '../utils/standard-stream.js';
- import {TRANSFORM_TYPES} from '../stdio/type.js';
- import {getBufferedData} from '../io/contents.js';
- import {waitForIpcOutput, getBufferedIpcOutput} from '../ipc/buffer-messages.js';
- import {sendIpcInput} from '../ipc/ipc-input.js';
- import {waitForAllStream} from './all-async.js';
- import {waitForStdioStreams} from './stdio.js';
- import {waitForExit, waitForSuccessfulExit} from './exit-async.js';
- import {waitForStream} from './wait-stream.js';
- // Retrieve result of subprocess: exit code, signal, error, streams (stdout/stderr/all)
- export const waitForSubprocessResult = async ({
- subprocess,
- options: {
- encoding,
- buffer,
- maxBuffer,
- lines,
- timeoutDuration: timeout,
- cancelSignal,
- gracefulCancel,
- forceKillAfterDelay,
- stripFinalNewline,
- ipc,
- ipcInput,
- },
- context,
- verboseInfo,
- fileDescriptors,
- originalStreams,
- onInternalError,
- controller,
- }) => {
- const exitPromise = waitForExit(subprocess, context);
- const streamInfo = {
- originalStreams,
- fileDescriptors,
- subprocess,
- exitPromise,
- propagating: false,
- };
- const stdioPromises = waitForStdioStreams({
- subprocess,
- encoding,
- buffer,
- maxBuffer,
- lines,
- stripFinalNewline,
- verboseInfo,
- streamInfo,
- });
- const allPromise = waitForAllStream({
- subprocess,
- encoding,
- buffer,
- maxBuffer,
- lines,
- stripFinalNewline,
- verboseInfo,
- streamInfo,
- });
- const ipcOutput = [];
- const ipcOutputPromise = waitForIpcOutput({
- subprocess,
- buffer,
- maxBuffer,
- ipc,
- ipcOutput,
- verboseInfo,
- });
- const originalPromises = waitForOriginalStreams(originalStreams, subprocess, streamInfo);
- const customStreamsEndPromises = waitForCustomStreamsEnd(fileDescriptors, streamInfo);
- try {
- return await Promise.race([
- Promise.all([
- {},
- waitForSuccessfulExit(exitPromise),
- Promise.all(stdioPromises),
- allPromise,
- ipcOutputPromise,
- sendIpcInput(subprocess, ipcInput),
- ...originalPromises,
- ...customStreamsEndPromises,
- ]),
- onInternalError,
- throwOnSubprocessError(subprocess, controller),
- ...throwOnTimeout(subprocess, timeout, context, controller),
- ...throwOnCancel({
- subprocess,
- cancelSignal,
- gracefulCancel,
- context,
- controller,
- }),
- ...throwOnGracefulCancel({
- subprocess,
- cancelSignal,
- gracefulCancel,
- forceKillAfterDelay,
- context,
- controller,
- }),
- ]);
- } catch (error) {
- context.terminationReason ??= 'other';
- return Promise.all([
- {error},
- exitPromise,
- Promise.all(stdioPromises.map(stdioPromise => getBufferedData(stdioPromise))),
- getBufferedData(allPromise),
- getBufferedIpcOutput(ipcOutputPromise, ipcOutput),
- Promise.allSettled(originalPromises),
- Promise.allSettled(customStreamsEndPromises),
- ]);
- }
- };
- // Transforms replace `subprocess.std*`, which means they are not exposed to users.
- // However, we still want to wait for their completion.
- const waitForOriginalStreams = (originalStreams, subprocess, streamInfo) =>
- originalStreams.map((stream, fdNumber) => stream === subprocess.stdio[fdNumber]
- ? undefined
- : waitForStream(stream, fdNumber, streamInfo));
- // Some `stdin`/`stdout`/`stderr` options create a stream, e.g. when passing a file path.
- // The `.pipe()` method automatically ends that stream when `subprocess` ends.
- // This makes sure we wait for the completion of those streams, in order to catch any error.
- const waitForCustomStreamsEnd = (fileDescriptors, streamInfo) => fileDescriptors.flatMap(({stdioItems}, fdNumber) => stdioItems
- .filter(({value, stream = value}) => isNodeStream(stream, {checkOpen: false}) && !isStandardStream(stream))
- .map(({type, value, stream = value}) => waitForStream(stream, fdNumber, streamInfo, {
- isSameDirection: TRANSFORM_TYPES.has(type),
- stopOnExit: type === 'native',
- })));
- // Fails when the subprocess emits an `error` event
- const throwOnSubprocessError = async (subprocess, {signal}) => {
- const [error] = await once(subprocess, 'error', {signal});
- throw error;
- };
|