diff --git a/src/main.ts b/src/main.ts index 5863591..294e30a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,6 +6,9 @@ import {computeEnv} from './env.js'; import {combineStreams} from './stream.js'; import readline from 'node:readline'; import {_parse} from 'cross-spawn'; +import {NonZeroExitError} from './non-zero-exit-error.js'; + +export {NonZeroExitError}; export interface Output { stderr: string; @@ -188,6 +191,10 @@ export class ExecProcess implements Result { if (this._thrownError) { throw this._thrownError; } + + if (this.exitCode !== 0 && this.exitCode !== undefined) { + throw new NonZeroExitError(this); + } } protected async _waitForOutput(): Promise { @@ -224,6 +231,10 @@ export class ExecProcess implements Result { throw this._thrownError; } + if (this.exitCode !== 0 && this.exitCode !== undefined) { + throw new NonZeroExitError(this); + } + const result: Output = { stderr, stdout diff --git a/src/non-zero-exit-error.ts b/src/non-zero-exit-error.ts new file mode 100644 index 0000000..85692f1 --- /dev/null +++ b/src/non-zero-exit-error.ts @@ -0,0 +1,18 @@ +import type {Result} from './main.js'; + +export class NonZeroExitError extends Error { + public readonly result: Result; + + public get exitCode(): number | undefined { + if (this.result.exitCode !== null) { + return this.result.exitCode; + } + return undefined; + } + + public constructor(result: Result) { + super(`Process exited with non-zero status (${result.exitCode})`); + + this.result = result; + } +} diff --git a/src/test/main_test.ts b/src/test/main_test.ts index 06ea12f..ad1d5b2 100644 --- a/src/test/main_test.ts +++ b/src/test/main_test.ts @@ -1,4 +1,4 @@ -import {x} from '../main.js'; +import {x, NonZeroExitError} from '../main.js'; import * as assert from 'node:assert/strict'; import {test} from 'node:test'; import os from 'node:os'; @@ -19,6 +19,14 @@ test('exec', async (t) => { assert.equal(proc.exitCode, 0); }); + await t.test('non-zero exitCode throws', async () => { + const proc = x('node', ['-e', 'process.exit(1);']); + await assert.rejects(async () => { + await proc; + }, NonZeroExitError); + assert.equal(proc.exitCode, 1); + }); + await t.test('async iterator gets correct output', async () => { const proc = x('node', ['-e', "console.log('foo'); console.log('bar');"]); const lines = [];