Skip to content

Commit

Permalink
Fix reject option with early spawning errors (#734)
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Jan 25, 2024
1 parent 66d12bf commit 8a18881
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 11 deletions.
13 changes: 10 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,16 @@ export function execa(rawFile, rawArgs, rawOptions) {
} catch (error) {
// Ensure the returned error is always both a promise and a child process
const dummySpawned = new childProcess.ChildProcess();
const errorPromise = Promise.reject(makeError({
const errorInstance = makeError({
error,
stdio: Array.from({length: stdioStreamsGroups.length}),
command,
escapedCommand,
options,
timedOut: false,
isCanceled: false,
}));
});
const errorPromise = options.reject ? Promise.reject(errorInstance) : Promise.resolve(errorInstance);
mergePromise(dummySpawned, errorPromise);
return dummySpawned;
}
Expand Down Expand Up @@ -219,7 +220,7 @@ export function execaSync(rawFile, rawArgs, rawOptions) {
try {
result = childProcess.spawnSync(file, args, options);
} catch (error) {
throw makeError({
const errorInstance = makeError({
error,
stdio: Array.from({length: stdioStreamsGroups.stdioLength}),
command,
Expand All @@ -228,6 +229,12 @@ export function execaSync(rawFile, rawArgs, rawOptions) {
timedOut: false,
isCanceled: false,
});

if (!options.reject) {
return errorInstance;
}

throw errorInstance;
}

pipeOutputSync(stdioStreamsGroups, result);
Expand Down
4 changes: 2 additions & 2 deletions test/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ test('empty error.stdio[0] even with input', async t => {
const SPAWN_ERROR_CODES = new Set(['EINVAL', 'ENOTSUP', 'EPERM']);

test('stdout/stderr/stdio on process spawning errors', async t => {
const {code, stdout, stderr, stdio} = await t.throwsAsync(execa('noop.js', {uid: -1}));
const {code, stdout, stderr, stdio} = await t.throwsAsync(execa('empty.js', {uid: -1}));
t.true(SPAWN_ERROR_CODES.has(code));
t.is(stdout, undefined);
t.is(stderr, undefined);
Expand All @@ -59,7 +59,7 @@ test('stdout/stderr/stdio on process spawning errors', async t => {

test('stdout/stderr/all/stdio on process spawning errors - sync', t => {
const {code, stdout, stderr, stdio} = t.throws(() => {
execaSync('noop.js', {uid: -1});
execaSync('empty.js', {uid: -1});
});
t.true(SPAWN_ERROR_CODES.has(code));
t.is(stdout, undefined);
Expand Down
25 changes: 19 additions & 6 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,27 +153,40 @@ test('execa() returns a promise with pid', async t => {
await subprocess;
});

test('child_process.spawn() propagated errors have correct shape', t => {
const subprocess = execa('noop.js', {uid: -1});
const testEarlyErrorShape = async (t, reject) => {
const subprocess = execa('', {reject});
t.notThrows(() => {
subprocess.catch(() => {});
subprocess.unref();
subprocess.on('error', () => {});
});
};

test('child_process.spawn() early errors have correct shape', testEarlyErrorShape, true);
test('child_process.spawn() early errors have correct shape - reject false', testEarlyErrorShape, false);

test('child_process.spawn() early errors are propagated', async t => {
const {failed} = await t.throwsAsync(execa(''));
t.true(failed);
});

test('child_process.spawn() errors are propagated', async t => {
const {failed} = await t.throwsAsync(execa('noop.js', {uid: -1}));
test('child_process.spawn() early errors are returned', async t => {
const {failed} = await execa('', {reject: false});
t.true(failed);
});

test('child_process.spawnSync() errors are propagated with a correct shape', t => {
test('child_process.spawnSync() early errors are propagated with a correct shape', t => {
const {failed} = t.throws(() => {
execaSync('noop.js', {uid: -1});
execaSync('');
});
t.true(failed);
});

test('child_process.spawnSync() early errors are propagated with a correct shape - reject false', t => {
const {failed} = execaSync('', {reject: false});
t.true(failed);
});

test('do not try to consume streams twice', async t => {
const subprocess = execa('noop.js', ['foo']);
const {stdout} = await subprocess;
Expand Down

0 comments on commit 8a18881

Please sign in to comment.