From a35e73c643063b1665155d16b68ebe06e3c8350b Mon Sep 17 00:00:00 2001 From: ehmicky Date: Wed, 29 May 2019 10:00:00 +0200 Subject: [PATCH] Improve error handling of streams --- index.js | 23 ++++++++++++++++++----- test.js | 14 +++++++++++--- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 8e201edd8b..69b87b9a9b 100644 --- a/index.js +++ b/index.js @@ -128,21 +128,24 @@ function getStream(process, stream, {encoding, buffer, maxBuffer}) { ret = _getStream.buffer(process[stream], {maxBuffer}); } + if (stream === 'all') { + return ret.catch(() => {}); + } + return ret.catch(error => { error.stream = stream; - error.message = `${stream} ${error.message}`; throw error; }); } function makeError(result, options) { const {stdout, stderr, signal} = result; - let {error} = result; - const {code, joinedCommand, timedOut, isCanceled, killed, parsed: {options: {timeout}}} = options; + let {error, error: {stream} = {}} = result; + const {code, joinedCommand, timedOut, isCanceled, killed, parsed, parsed: {options: {timeout}}} = options; const [exitCodeName, exitCode] = getCode(result, code); - const prefix = getErrorPrefix({timedOut, timeout, signal, exitCodeName, exitCode, isCanceled}); + const prefix = getErrorPrefix({timedOut, timeout, signal, exitCodeName, exitCode, isCanceled, stream}); const message = `Command ${prefix}: ${joinedCommand}`; if (error instanceof Error) { @@ -162,6 +165,11 @@ function makeError(result, options) { error.all = result.all; } + if (stream !== undefined && error.bufferedData) { + error[stream] = handleOutput(parsed.options, error.bufferedData); + delete error.bufferedData; + } + error.failed = true; error.timedOut = timedOut; error.isCanceled = isCanceled; @@ -185,7 +193,7 @@ function getCode({error = {}}, code) { return []; } -function getErrorPrefix({timedOut, timeout, signal, exitCodeName, exitCode, isCanceled}) { +function getErrorPrefix({timedOut, timeout, signal, exitCodeName, exitCode, isCanceled, stream}) { if (timedOut) { return `timed out after ${timeout} milliseconds`; } @@ -198,6 +206,10 @@ function getErrorPrefix({timedOut, timeout, signal, exitCodeName, exitCode, isCa return `was killed with ${signal}`; } + if (stream !== undefined) { + return `'${stream}' error`; + } + if (exitCode !== undefined) { return `failed with exit code ${exitCode} (${exitCodeName})`; } @@ -289,6 +301,7 @@ const execa = (file, args, options) => { if (spawned.stdin) { spawned.stdin.on('error', error => { + error.stream = 'stdin'; resolve({error}); }); } diff --git a/test.js b/test.js index 4b8028dd06..901ff739e3 100644 --- a/test.js +++ b/test.js @@ -194,6 +194,12 @@ test('input option can be a Buffer - sync', t => { t.is(stdout, 'testing12'); }); +test('stdin errors are handled', async t => { + const child = execa('noop'); + child.stdin.emit('error', new Error('test')); + await t.throwsAsync(child, /Command 'stdin' error.*\ntest/); +}); + test('child process errors are handled', async t => { const child = execa('noop'); child.emit('error', new Error('test')); @@ -236,13 +242,15 @@ test('child_process.spawnSync() errors are propagated', t => { }); test('maxBuffer affects stdout', async t => { - await t.throwsAsync(execa('max-buffer', ['stdout', '11'], {maxBuffer: 10}), /stdout maxBuffer exceeded/); await t.notThrowsAsync(execa('max-buffer', ['stdout', '10'], {maxBuffer: 10})); + const {stdout} = await t.throwsAsync(execa('max-buffer', ['stdout', '11'], {maxBuffer: 10}), /max-buffer stdout/); + t.is(stdout, '.'.repeat(10)); }); test('maxBuffer affects stderr', async t => { - await t.throwsAsync(execa('max-buffer', ['stderr', '13'], {maxBuffer: 12}), /stderr maxBuffer exceeded/); - await t.notThrowsAsync(execa('max-buffer', ['stderr', '12'], {maxBuffer: 12})); + await t.notThrowsAsync(execa('max-buffer', ['stderr', '10'], {maxBuffer: 10})); + const {stderr} = await t.throwsAsync(execa('max-buffer', ['stderr', '11'], {maxBuffer: 10}), /max-buffer stderr/); + t.is(stderr, '.'.repeat(10)); }); test('do not buffer stdout when `buffer` set to `false`', async t => {