Skip to content

Instantly share code, notes, and snippets.

@isaacs
Last active October 27, 2020 18:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save isaacs/00521f823ef33da041f389aee55091c9 to your computer and use it in GitHub Desktop.
Save isaacs/00521f823ef33da041f389aee55091c9 to your computer and use it in GitHub Desktop.
commit 5a7c615eea11ba4c540210c0a29c843da5def060
Author: isaacs <i@izs.me>
Date: Tue Oct 27 11:07:00 2020 -0700
Do not return 0-length buffer if stdio is inherited
diff --git a/README.md b/README.md
index 3b604f2..b569948 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,18 @@ Result or error will be decorated with the properties in the `extra`
object. You can use this to attach some helpful info about _why_ the
command is being run, if it makes sense for your use case.
+If `stdio` is set to anything other than `'inherit'`, then the result/error
+will be decorated with `stdout` and `stderr` values. If `stdioString` is
+set to `true`, these will be strings. Otherwise they will be Buffer
+objects.
+
Returned promise is decorated with the `stdin` stream if the process is set
to pipe from `stdin`. Writing to this stream writes to the `stdin` of the
spawned process.
#### Options
-- `stdioString` Boolean, default `false`. Return stdio/stderr output as
+- `stdioString` Boolean, default `false`. Return stdout/stderr output as
strings rather than buffers.
- `cwd` String, default `process.cwd()`. Current working directory for
running the script. Also the argument to `infer-owner` to determine
diff --git a/index.js b/index.js
index 809da93..cc73d1d 100644
--- a/index.js
+++ b/index.js
@@ -2,6 +2,11 @@ const {spawn} = require('child_process')
const inferOwner = require('infer-owner')
+const isPipe = (stdio = 'pipe', fd) =>
+ stdio === 'pipe' || stdio === null ? true
+ : Array.isArray(stdio) ? isPipe(stdio[fd], fd)
+ : false
+
// 'extra' object is for decorating the error a bit more
const promiseSpawn = (cmd, args, opts, extra = {}) => {
const cwd = opts.cwd || process.cwd()
@@ -20,13 +25,14 @@ const promiseSpawn = (cmd, args, opts, extra = {}) => {
}, extra))
}
-const stdioResult = (stdout, stderr, {stdioString}) =>
+const stdioResult = (stdout, stderr, {stdioString, stdio}) =>
stdioString ? {
- stdout: Buffer.concat(stdout).toString(),
- stderr: Buffer.concat(stderr).toString(),
- } : {
- stdout: Buffer.concat(stdout),
- stderr: Buffer.concat(stderr),
+ stdout: isPipe(stdio, 1) ? Buffer.concat(stdout).toString() : null,
+ stderr: isPipe(stdio, 2) ? Buffer.concat(stderr).toString() : null,
+ }
+ : {
+ stdout: isPipe(stdio, 1) ? Buffer.concat(stdout) : null,
+ stderr: isPipe(stdio, 2) ? Buffer.concat(stderr) : null,
}
const promiseSpawnUid = (cmd, args, opts, extra) => {
diff --git a/test/promise-spawn.js b/test/promise-spawn.js
index a34f565..5a79fe2 100644
--- a/test/promise-spawn.js
+++ b/test/promise-spawn.js
@@ -4,15 +4,20 @@ const Minipass = require('minipass')
const EE = require('events')
const fs = require('fs')
+const isPipe = (stdio = 'pipe', fd) =>
+ stdio === 'pipe' || stdio === null ? true
+ : Array.isArray(stdio) ? isPipe(stdio[fd], fd)
+ : false
+
class MockProc extends EE {
constructor (cmd, args, opts) {
super()
this.cmd = cmd
this.args = args
this.opts = opts
- this.stdout = opts.stdio === 'inherit' ? null : new Minipass()
- this.stderr = opts.stdio === 'inherit' ? null : new Minipass()
- this.stdin = opts.stdio === 'inherit' ? null : new Minipass()
+ this.stdin = isPipe(opts.stdio, 0) ? new Minipass() : null
+ this.stdout = isPipe(opts.stdio, 1) ? new Minipass() : null
+ this.stderr = isPipe(opts.stdio, 2) ? new Minipass() : null
this.code = null
this.signal = null
process.nextTick(() => this.run())
@@ -111,8 +116,24 @@ t.test('pass', t => t.resolveMatch(promiseSpawn('pass', [], {stdioString: true},
t.test('pass, share stdio', t => t.resolveMatch(promiseSpawn('pass', [], { stdio: 'inherit'}, {a: 1}), {
code: 0,
signal: null,
- stdout: Buffer.alloc(0),
- stderr: Buffer.alloc(0),
+ stdout: null,
+ stderr: null,
+ a: 1,
+}))
+
+t.test('pass, share stdout', t => t.resolveMatch(promiseSpawn('pass', [], { stdioString: true, stdio: ['pipe', 'inherit', 'pipe']}, {a: 1}), {
+ code: 0,
+ signal: null,
+ stdout: null,
+ stderr: '',
+ a: 1,
+}))
+
+t.test('pass, share stderr', t => t.resolveMatch(promiseSpawn('pass', [], { stdioString: true, stdio: ['pipe', 'pipe', 'inherit']}, {a: 1}), {
+ code: 0,
+ signal: null,
+ stdout: 'OK :)',
+ stderr: null,
a: 1,
}))
@@ -129,8 +150,8 @@ t.test('fail, shared stdio', t => t.rejects(promiseSpawn('fail', [], { stdio: 'i
message: 'command failed',
code: 1,
signal: null,
- stdout: Buffer.alloc(0),
- stderr: Buffer.alloc(0),
+ stdout: null,
+ stderr: null,
a: 1,
}))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment