Skip to content

Commit

Permalink
child_process: promisify includes stdio in error
Browse files Browse the repository at this point in the history
This converts the initial implementation of a promised exec that used
the customPromisifyArgs support in util.promisify with a custom
implementation. This is because exec and execFile, when there is an
error, still supply the stdout and stderr of the process, and yet
the promisified version with customPromisifyArgs does
not supply this ability.

I created a custom implementation and attached it to exec and execFile
using the util.promisify.custom key.

Fixes: #13364
PR-URL: #13388
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
  • Loading branch information
giltayar authored and addaleax committed Jun 9, 2017
1 parent 55f9c85 commit 8d7f07f
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 9 deletions.
8 changes: 6 additions & 2 deletions doc/api/child_process.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,9 @@ child runs longer than `timeout` milliseconds.
replace the existing process and uses a shell to execute the command.

If this method is invoked as its [`util.promisify()`][]ed version, it returns
a Promise for an object with `stdout` and `stderr` properties.
a Promise for an object with `stdout` and `stderr` properties. In case of an
error, a rejected promise is returned, with the same `error` object given in the
callback, but with an additional two properties `stdout` and `stderr`.

For example:

Expand Down Expand Up @@ -281,7 +283,9 @@ stderr output. If `encoding` is `'buffer'`, or an unrecognized character
encoding, `Buffer` objects will be passed to the callback instead.

If this method is invoked as its [`util.promisify()`][]ed version, it returns
a Promise for an object with `stdout` and `stderr` properties.
a Promise for an object with `stdout` and `stderr` properties. In case of an
error, a rejected promise is returned, with the same `error` object given in the
callback, but with an additional two properties `stdout` and `stderr`.

```js
const util = require('util');
Expand Down
34 changes: 27 additions & 7 deletions lib/child_process.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
'use strict';

const util = require('util');
const {
deprecate, convertToValidSignal, customPromisifyArgs
} = require('internal/util');
const { deprecate, convertToValidSignal } = require('internal/util');
const { createPromise,
promiseResolve, promiseReject } = process.binding('util');
const debug = util.debuglog('child_process');

const uv = process.binding('uv');
Expand Down Expand Up @@ -140,9 +140,27 @@ exports.exec = function(command /*, options, callback*/) {
opts.callback);
};

Object.defineProperty(exports.exec, customPromisifyArgs,
{ value: ['stdout', 'stderr'], enumerable: false });
const customPromiseExecFunction = (orig) => {
return (...args) => {
const promise = createPromise();

orig(...args, (err, stdout, stderr) => {
if (err !== null) {
err.stdout = stdout;
err.stderr = stderr;
promiseReject(promise, err);
} else {
promiseResolve(promise, { stdout, stderr });
}
});
return promise;
};
};

Object.defineProperty(exports.exec, util.promisify.custom, {
enumerable: false,
value: customPromiseExecFunction(exports.exec)
});

exports.execFile = function(file /*, args, options, callback*/) {
var args = [];
Expand Down Expand Up @@ -338,8 +356,10 @@ exports.execFile = function(file /*, args, options, callback*/) {
return child;
};

Object.defineProperty(exports.execFile, customPromisifyArgs,
{ value: ['stdout', 'stderr'], enumerable: false });
Object.defineProperty(exports.execFile, util.promisify.custom, {
enumerable: false,
value: customPromiseExecFunction(exports.execFile)
});

const _deprecatedCustomFds = deprecate(
function deprecateCustomFds(options) {
Expand Down
19 changes: 19 additions & 0 deletions test/parallel/test-child-process-promisified.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,22 @@ const execFile = promisify(child_process.execFile);
assert(err.message.includes('doesntexist'));
}));
}
const failingCodeWithStdoutErr =
'console.log(42);console.error(43);process.exit(1)';
{
exec(`${process.execPath} -e "${failingCodeWithStdoutErr}"`)
.catch(common.mustCall((err) => {
assert.strictEqual(err.code, 1);
assert.strictEqual(err.stdout, '42\n');
assert.strictEqual(err.stderr, '43\n');
}));
}

{
execFile(process.execPath, ['-e', failingCodeWithStdoutErr])
.catch(common.mustCall((err) => {
assert.strictEqual(err.code, 1);
assert.strictEqual(err.stdout, '42\n');
assert.strictEqual(err.stderr, '43\n');
}));
}

0 comments on commit 8d7f07f

Please sign in to comment.