Git Product home page Git Product logo

execa's Introduction

execa logo

Coverage Status

Process execution for humans





Why

This package improves child_process methods with:

Install

npm install execa

Usage

Promise interface

import {execa} from 'execa';

const {stdout} = await execa('echo', ['unicorns']);
console.log(stdout);
//=> 'unicorns'

Global/shared options

import {execa as execa_} from 'execa';

const execa = execa_({verbose: 'full'});

await execa('echo', ['unicorns']);
//=> 'unicorns'

Template string syntax

Basic

import {execa} from 'execa';

const arg = 'unicorns';
const {stdout} = await execa`echo ${arg} & rainbows!`;
console.log(stdout);
//=> 'unicorns & rainbows!'

Multiple arguments

import {execa} from 'execa';

const args = ['unicorns', '&', 'rainbows!'];
const {stdout} = await execa`echo ${args}`;
console.log(stdout);
//=> 'unicorns & rainbows!'

With options

import {execa} from 'execa';

await execa({verbose: 'full'})`echo unicorns`;
//=> 'unicorns'

Scripts

For more information about Execa scripts, please see this page.

Basic

import {$} from 'execa';

const branch = await $`git branch --show-current`;
await $`dep deploy --branch=${branch}`;

Verbose mode

> node file.js
unicorns
rainbows

> NODE_DEBUG=execa node file.js
[19:49:00.360] [0] $ echo unicorns
unicorns
[19:49:00.383] [0] √ (done in 23ms)
[19:49:00.383] [1] $ echo rainbows
rainbows
[19:49:00.404] [1] √ (done in 21ms)

Input/output

Redirect output to a file

import {execa} from 'execa';

// Similar to `echo unicorns > stdout.txt` in Bash
await execa('echo', ['unicorns'], {stdout: {file: 'stdout.txt'}});

// Similar to `echo unicorns 2> stdout.txt` in Bash
await execa('echo', ['unicorns'], {stderr: {file: 'stderr.txt'}});

// Similar to `echo unicorns &> stdout.txt` in Bash
await execa('echo', ['unicorns'], {stdout: {file: 'all.txt'}, stderr: {file: 'all.txt'}});

Redirect input from a file

import {execa} from 'execa';

// Similar to `cat < stdin.txt` in Bash
const {stdout} = await execa('cat', {inputFile: 'stdin.txt'});
console.log(stdout);
//=> 'unicorns'

Save and pipe output from a subprocess

import {execa} from 'execa';

const {stdout} = await execa('echo', ['unicorns'], {stdout: ['pipe', 'inherit']});
// Prints `unicorns`
console.log(stdout);
// Also returns 'unicorns'

Pipe multiple subprocesses

import {execa} from 'execa';

// Similar to `npm run build | sort | head -n2` in Bash
const {stdout, pipedFrom} = await execa('npm', ['run', 'build'])
	.pipe('sort')
	.pipe('head', ['-n2']);
console.log(stdout); // Result of `head -n2`
console.log(pipedFrom[0]); // Result of `sort`
console.log(pipedFrom[0].pipedFrom[0]); // Result of `npm run build`

Pipe with template strings

import {execa} from 'execa';

await execa`npm run build`
	.pipe`sort`
	.pipe`head -n2`;

Iterate over output lines

import {execa} from 'execa';

for await (const line of execa`npm run build`)) {
	if (line.includes('ERROR')) {
		console.log(line);
	}
}

Handling Errors

import {execa} from 'execa';

// Catching an error
try {
	await execa('unknown', ['command']);
} catch (error) {
	console.log(error);
	/*
	ExecaError: Command failed with ENOENT: unknown command
	spawn unknown ENOENT
			at ...
			at ... {
		shortMessage: 'Command failed with ENOENT: unknown command\nspawn unknown ENOENT',
		originalMessage: 'spawn unknown ENOENT',
		command: 'unknown command',
		escapedCommand: 'unknown command',
		cwd: '/path/to/cwd',
		durationMs: 28.217566,
		failed: true,
		timedOut: false,
		isCanceled: false,
		isTerminated: false,
		isMaxBuffer: false,
		code: 'ENOENT',
		stdout: '',
		stderr: '',
		stdio: [undefined, '', ''],
		pipedFrom: []
		[cause]: Error: spawn unknown ENOENT
				at ...
				at ... {
			errno: -2,
			code: 'ENOENT',
			syscall: 'spawn unknown',
			path: 'unknown',
			spawnargs: [ 'command' ]
		}
	}
	*/
}

API

Methods

execa(file, arguments?, options?)

file: string | URL
arguments: string[]
options: Options
Returns: Subprocess

Executes a command using file ...arguments.

Arguments are automatically escaped. They can contain any character, including spaces, tabs and newlines.

execa`command`

execa(options)`command`

command: string
options: Options
Returns: Subprocess

Executes a command. command is a template string and includes both the file and its arguments.

The command template string can inject any ${value} with the following types: string, number, subprocess or an array of those types. For example: execa`echo one ${'two'} ${3} ${['four', 'five']}`. For ${subprocess}, the subprocess's stdout is used.

Arguments are automatically escaped. They can contain any character, but spaces, tabs and newlines must use ${} like execa`echo ${'has space'}`.

The command template string can use multiple lines and indentation.

execa(options)

options: Options
Returns: execa

Returns a new instance of Execa but with different default options. Consecutive calls are merged to previous ones.

This allows setting global options or sharing options between multiple commands.

execaSync(file, arguments?, options?)

execaSync`command`

Same as execa() but synchronous.

Returns or throws a subprocess result. The subprocess is not returned: its methods and properties are not available.

The following features cannot be used:

$(file, arguments?, options?)

file: string | URL
arguments: string[]
options: Options
Returns: Subprocess

Same as execa() but using the stdin: 'inherit' and preferLocal: true options.

Just like execa(), this can use the template string syntax or bind options. It can also be run synchronously using $.sync() or $.s().

This is the preferred method when executing multiple commands in a script file. For more information, please see this page.

execaNode(scriptPath, arguments?, options?)

scriptPath: string | URL
arguments: string[]
options: Options
Returns: Subprocess

Same as execa() but using the node: true option. Executes a Node.js file using node scriptPath ...arguments.

Just like execa(), this can use the template string syntax or bind options.

This is the preferred method when executing Node.js files.

execaCommand(command, options?)

command: string
options: Options
Returns: Subprocess

execa with the template string syntax allows the file or the arguments to be user-defined (by injecting them with ${}). However, if both the file and the arguments are user-defined, and those are supplied as a single string, then execaCommand(command) must be used instead.

This is only intended for very specific cases, such as a REPL. This should be avoided otherwise.

Just like execa(), this can bind options. It can also be run synchronously using execaCommandSync().

Arguments are automatically escaped. They can contain any character, but spaces must be escaped with a backslash like execaCommand('echo has\\ space').

Shell syntax

For all the methods above, no shell interpreter (Bash, cmd.exe, etc.) is used unless the shell option is set. This means shell-specific characters and expressions ($variable, &&, ||, ;, |, etc.) have no special meaning and do not need to be escaped.

subprocess

The return value of all asynchronous methods is both:

subprocess.pipe(file, arguments?, options?)

file: string | URL
arguments: string[]
options: Options and PipeOptions
Returns: Promise<Result>

Pipe the subprocess' stdout to a second Execa subprocess' stdin. This resolves with that second subprocess' result. If either subprocess is rejected, this is rejected with that subprocess' error instead.

This follows the same syntax as execa(file, arguments?, options?) except both regular options and pipe-specific options can be specified.

This can be called multiple times to chain a series of subprocesses.

Multiple subprocesses can be piped to the same subprocess. Conversely, the same subprocess can be piped to multiple other subprocesses.

subprocess.pipe`command`

subprocess.pipe(options)`command`

command: string
options: Options and PipeOptions
Returns: Promise<Result>

Like subprocess.pipe(file, arguments?, options?) but using a command template string instead. This follows the same syntax as execa template strings.

subprocess.pipe(secondSubprocess, pipeOptions?)

secondSubprocess: execa() return value
pipeOptions: PipeOptions
Returns: Promise<Result>

Like subprocess.pipe(file, arguments?, options?) but using the return value of another execa() call instead.

This is the most advanced method to pipe subprocesses. It is useful in specific cases, such as piping multiple subprocesses to the same subprocess.

pipeOptions

Type: object

pipeOptions.from

Type: "stdout" | "stderr" | "all" | "fd3" | "fd4" | ...
Default: "stdout"

Which stream to pipe from the source subprocess. A file descriptor like "fd3" can also be passed.

"all" pipes both stdout and stderr. This requires the all option to be true.

pipeOptions.to

Type: "stdin" | "fd3" | "fd4" | ...
Default: "stdin"

Which stream to pipe to the destination subprocess. A file descriptor like "fd3" can also be passed.

pipeOptions.unpipeSignal

Type: AbortSignal

Unpipe the subprocess when the signal aborts.

The subprocess.pipe() method will be rejected with a cancellation error.

subprocess.kill(signal, error?)

subprocess.kill(error?)

signal: string | number
error: Error
Returns: boolean

Sends a signal to the subprocess. The default signal is the killSignal option. killSignal defaults to SIGTERM, which terminates the subprocess.

This returns false when the signal could not be sent, for example when the subprocess has already exited.

When an error is passed as argument, it is set to the subprocess' error.cause. The subprocess is then terminated with the default signal. This does not emit the error event.

More info.

subprocess.pid

Type: number | undefined

Process identifier (PID).

This is undefined if the subprocess failed to spawn.

subprocess.send(message)

message: unknown
Returns: boolean

Send a message to the subprocess. The type of message depends on the serialization option. The subprocess receives it as a message event.

This returns true on success.

This requires the ipc option to be true.

More info.

subprocess.on('message', (message) => void)

message: unknown

Receives a message from the subprocess. The type of message depends on the serialization option. The subprocess sends it using process.send(message).

This requires the ipc option to be true.

More info.

subprocess.stdin

Type: Writable | null

The subprocess stdin as a stream.

This is null if the stdin option is set to 'inherit', 'ignore', Readable or integer.

This is intended for advanced cases. Please consider using the stdin option, input option, inputFile option, or subprocess.pipe() instead.

subprocess.stdout

Type: Readable | null

The subprocess stdout as a stream.

This is null if the stdout option is set to 'inherit', 'ignore', Writable or integer.

This is intended for advanced cases. Please consider using result.stdout, the stdout option, subprocess.iterable(), or subprocess.pipe() instead.

subprocess.stderr

Type: Readable | null

The subprocess stderr as a stream.

This is null if the stderr option is set to 'inherit', 'ignore', Writable or integer.

This is intended for advanced cases. Please consider using result.stderr, the stderr option, subprocess.iterable(), or subprocess.pipe() instead.

subprocess.all

Type: Readable | undefined

Stream combining/interleaving subprocess.stdout and subprocess.stderr.

This is undefined if either:

  • the all option is false (the default value).
  • both stdout and stderr options are set to 'inherit', 'ignore', Writable or integer.

This is intended for advanced cases. Please consider using result.all, the stdout/stderr option, subprocess.iterable(), or subprocess.pipe() instead.

subprocess.stdio

Type: [Writable | null, Readable | null, Readable | null, ...Array<Writable | Readable | null>]

The subprocess stdin, stdout, stderr and other files descriptors as an array of streams.

Each array item is null if the corresponding stdin, stdout, stderr or stdio option is set to 'inherit', 'ignore', Stream or integer.

This is intended for advanced cases. Please consider using result.stdio, the stdio option, subprocess.iterable() or subprocess.pipe() instead.

subprocess[Symbol.asyncIterator]()

Returns: AsyncIterable

Subprocesses are async iterables. They iterate over each output line.

The iteration waits for the subprocess to end. It throws if the subprocess fails. This means you do not need to await the subprocess' promise.

subprocess.iterable(readableOptions?)

readableOptions: ReadableOptions
Returns: AsyncIterable

Same as subprocess[Symbol.asyncIterator] except options can be provided.

subprocess.readable(readableOptions?)

readableOptions: ReadableOptions
Returns: Readable Node.js stream

Converts the subprocess to a readable stream.

Unlike subprocess.stdout, the stream waits for the subprocess to end and emits an error event if the subprocess fails. This means you do not need to await the subprocess' promise. On the other hand, you do need to handle to the stream error event. This can be done by using await finished(stream), await pipeline(..., stream) or await text(stream) which throw an exception when the stream errors.

Before using this method, please first consider the stdin/stdout/stderr/stdio options, subprocess.pipe() or subprocess.iterable().

subprocess.writable(writableOptions?)

writableOptions: WritableOptions
Returns: Writable Node.js stream

Converts the subprocess to a writable stream.

Unlike subprocess.stdin, the stream waits for the subprocess to end and emits an error event if the subprocess fails. This means you do not need to await the subprocess' promise. On the other hand, you do need to handle to the stream error event. This can be done by using await finished(stream) or await pipeline(stream, ...) which throw an exception when the stream errors.

Before using this method, please first consider the stdin/stdout/stderr/stdio options or subprocess.pipe().

subprocess.duplex(duplexOptions?)

duplexOptions: ReadableOptions | WritableOptions
Returns: Duplex Node.js stream

Converts the subprocess to a duplex stream.

The stream waits for the subprocess to end and emits an error event if the subprocess fails. This means you do not need to await the subprocess' promise. On the other hand, you do need to handle to the stream error event. This can be done by using await finished(stream), await pipeline(..., stream, ...) or await text(stream) which throw an exception when the stream errors.

Before using this method, please first consider the stdin/stdout/stderr/stdio options, subprocess.pipe() or subprocess.iterable().

readableOptions

Type: object

readableOptions.from

Type: "stdout" | "stderr" | "all" | "fd3" | "fd4" | ...
Default: "stdout"

Which stream to read from the subprocess. A file descriptor like "fd3" can also be passed.

"all" reads both stdout and stderr. This requires the all option to be true.

readableOptions.binary

Type: boolean
Default: false with subprocess.iterable(), true with subprocess.readable()/subprocess.duplex()

If false, the stream iterates over lines. Each line is a string. Also, the stream is in object mode.

If true, the stream iterates over arbitrary chunks of data. Each line is an Uint8Array (with subprocess.iterable()) or a Buffer (otherwise).

This is always true when the encoding option is binary.

readableOptions.preserveNewlines

Type: boolean
Default: false with subprocess.iterable(), true with subprocess.readable()/subprocess.duplex()

If both this option and the binary option is false, newlines are stripped from each line.

writableOptions

Type: object

writableOptions.to

Type: "stdin" | "fd3" | "fd4" | ...
Default: "stdin"

Which stream to write to the subprocess. A file descriptor like "fd3" can also be passed.

Result

Type: object

Result of a subprocess execution.

When the subprocess fails, it is rejected with an ExecaError instead.

result.command

Type: string

The file and arguments that were run, for logging purposes.

This is not escaped and should not be executed directly as a subprocess, including using execa() or execaCommand().

result.escapedCommand

Type: string

Same as command but escaped.

Unlike command, control characters are escaped, which makes it safe to print in a terminal.

This can also be copied and pasted into a shell, for debugging purposes. Since the escaping is fairly basic, this should not be executed directly as a subprocess, including using execa() or execaCommand().

result.cwd

Type: string

The current directory in which the command was run.

result.durationMs

Type: number

Duration of the subprocess, in milliseconds.

result.stdout

Type: string | Uint8Array | string[] | Uint8Array[] | unknown[] | undefined

The output of the subprocess on stdout.

This is undefined if the stdout option is set to only 'inherit', 'ignore', Writable or integer. This is an array if the lines option is true, or if the stdout option is a transform in object mode.

result.stderr

Type: string | Uint8Array | string[] | Uint8Array[] | unknown[] | undefined

The output of the subprocess on stderr.

This is undefined if the stderr option is set to only 'inherit', 'ignore', Writable or integer. This is an array if the lines option is true, or if the stderr option is a transform in object mode.

result.all

Type: string | Uint8Array | string[] | Uint8Array[] | unknown[] | undefined

The output of the subprocess with result.stdout and result.stderr interleaved.

This is undefined if either:

  • the all option is false (the default value).
  • both stdout and stderr options are set to only 'inherit', 'ignore', Writable or integer.

This is an array if the lines option is true, or if either the stdout or stderr option is a transform in object mode.

result.stdio

Type: Array<string | Uint8Array | string[] | Uint8Array[] | unknown[] | undefined>

The output of the subprocess on stdin, stdout, stderr and other file descriptors.

Items are undefined when their corresponding stdio option is set to 'inherit', 'ignore', Writable or integer. Items are arrays when their corresponding stdio option is a transform in object mode.

result.failed

Type: boolean

Whether the subprocess failed to run.

result.timedOut

Type: boolean

Whether the subprocess timed out.

result.isCanceled

Type: boolean

Whether the subprocess was canceled using the cancelSignal option.

result.isTerminated

Type: boolean

Whether the subprocess was terminated by a signal (like SIGTERM) sent by either:

result.isMaxBuffer

Type: boolean

Whether the subprocess failed because its output was larger than the maxBuffer option.

result.exitCode

Type: number | undefined

The numeric exit code of the subprocess that was run.

This is undefined when the subprocess could not be spawned or was terminated by a signal.

result.signal

Type: string | undefined

The name of the signal (like SIGTERM) that terminated the subprocess, sent by either:

If a signal terminated the subprocess, this property is defined and included in the error message. Otherwise it is undefined.

result.signalDescription

Type: string | undefined

A human-friendly description of the signal that was used to terminate the subprocess. For example, Floating point arithmetic error.

If a signal terminated the subprocess, this property is defined and included in the error message. Otherwise it is undefined. It is also undefined when the signal is very uncommon which should seldomly happen.

result.pipedFrom

Type: Array<Result | ExecaError>

Results of the other subprocesses that were piped into this subprocess. This is useful to inspect a series of subprocesses piped with each other.

This array is initially empty and is populated each time the subprocess.pipe() method resolves.

ExecaError

ExecaSyncError

Type: Error

Exception thrown when the subprocess fails, either:

This has the same shape as successful results, with the following additional properties.

error.message

Type: string

Error message when the subprocess failed to run. In addition to the underlying error message, it also contains some information related to why the subprocess errored.

The subprocess stderr, stdout and other file descriptors' output are appended to the end, separated with newlines and not interleaved.

error.shortMessage

Type: string

This is the same as the message property except it does not include the subprocess stdout/stderr/stdio.

error.originalMessage

Type: string | undefined

Original error message. This is the same as the message property excluding the subprocess stdout/stderr/stdio and some additional information added by Execa.

This exists only if the subprocess exited due to an error event or a timeout.

error.cause

Type: unknown | undefined

Underlying error, if there is one. For example, this is set by subprocess.kill(error).

This is usually an Error instance.

error.code

Type: string | undefined

Node.js-specific error code, when available.

options

Type: object

This lists all options for execa() and the other methods.

Some options are related to the subprocess output: verbose, lines, stripFinalNewline, buffer, maxBuffer. By default, those options apply to all file descriptors (stdout, stderr, etc.). A plain object can be passed instead to apply them to only stdout, stderr, fd3, etc.

await execa('./run.js', {verbose: 'full'}) // Same value for stdout and stderr
await execa('./run.js', {verbose: {stdout: 'none', stderr: 'full'}}) // Different values

options.reject

Type: boolean
Default: true

Setting this to false resolves the promise with the error instead of rejecting it.

options.shell

Type: boolean | string | URL
Default: false

If true, runs file inside of a shell. Uses /bin/sh on UNIX and cmd.exe on Windows. A different shell can be specified as a string. The shell should understand the -c switch on UNIX or /d /s /c on Windows.

We recommend against using this option since it is:

  • not cross-platform, encouraging shell-specific syntax.
  • slower, because of the additional shell interpretation.
  • unsafe, potentially allowing command injection.

options.cwd

Type: string | URL
Default: process.cwd()

Current working directory of the subprocess.

This is also used to resolve the nodePath option when it is a relative path.

options.env

Type: object
Default: process.env

Environment key-value pairs.

Unless the extendEnv option is false, the subprocess also uses the current process' environment variables (process.env).

options.extendEnv

Type: boolean
Default: true

If true, the subprocess uses both the env option and the current process' environment variables (process.env). If false, only the env option is used, not process.env.

options.preferLocal

Type: boolean
Default: true with $, false otherwise

Prefer locally installed binaries when looking for a binary to execute.
If you $ npm install foo, you can then execa('foo').

options.localDir

Type: string | URL
Default: process.cwd()

Preferred path to find locally installed binaries in (use with preferLocal).

options.node

Type: boolean
Default: true with execaNode(), false otherwise

If true, runs with Node.js. The first argument must be a Node.js file.

options.nodeOptions

Type: string[]
Default: process.execArgv (current Node.js CLI options)

List of CLI options passed to the Node.js executable.

Requires the node option to be true.

options.nodePath

Type: string | URL
Default: process.execPath (current Node.js executable)

Path to the Node.js executable.

For example, this can be used together with get-node to run a specific Node.js version.

Requires the node option to be true.

options.verbose

Type: 'none' | 'short' | 'full'
Default: 'none'

If verbose is 'short' or 'full', prints each command on stderr before executing it. When the command completes, prints its duration and (if it failed) its error.

If verbose is 'full', the command's stdout and stderr are printed too, unless either:

This can also be set to 'full' by setting the NODE_DEBUG=execa environment variable in the current process.

By default, this applies to both stdout and stderr, but different values can also be passed.

options.buffer

Type: boolean
Default: true

Whether to return the subprocess' output using the result.stdout, result.stderr, result.all and result.stdio properties.

On failure, the error.stdout, error.stderr, error.all and error.stdio properties are used instead.

When buffer is false, the output can still be read using the subprocess.stdout, subprocess.stderr, subprocess.stdio and subprocess.all streams. If the output is read, this should be done right away to avoid missing any data.

By default, this applies to both stdout and stderr, but different values can also be passed.

options.input

Type: string | Uint8Array | stream.Readable

Write some input to the subprocess' stdin.

See also the inputFile and stdin options.

options.inputFile

Type: string | URL

Use a file as input to the subprocess' stdin.

See also the input and stdin options.

options.stdin

Type: string | number | stream.Readable | ReadableStream | TransformStream | URL | {file: string} | Uint8Array | Iterable<string | Uint8Array | unknown> | AsyncIterable<string | Uint8Array | unknown> | GeneratorFunction<string | Uint8Array | unknown> | AsyncGeneratorFunction<string | Uint8Array | unknown> | {transform: GeneratorFunction | AsyncGeneratorFunction | Duplex | TransformStream} (or a tuple of those types)
Default: inherit with $, pipe otherwise

How to setup the subprocess' standard input. This can be:

  • 'pipe': Sets subprocess.stdin stream.
  • 'overlapped': Like 'pipe' but asynchronous on Windows.
  • 'ignore': Do not use stdin.
  • 'inherit': Re-use the current process' stdin.
  • an integer: Re-use a specific file descriptor from the current process.
  • a Node.js Readable stream.
  • { file: 'path' } object.
  • a file URL.
  • a web ReadableStream.
  • an Iterable or an AsyncIterable
  • an Uint8Array.

This can be an array of values such as ['inherit', 'pipe'] or [filePath, 'pipe'].

This can also be a generator function, a Duplex or a web TransformStream to transform the input. Learn more.

More info.

options.stdout

Type: string | number | stream.Writable | WritableStream | TransformStream | URL | {file: string} | GeneratorFunction<string | Uint8Array | unknown> | AsyncGeneratorFunction<string | Uint8Array | unknown> | {transform: GeneratorFunction | AsyncGeneratorFunction | Duplex | TransformStream} (or a tuple of those types)
Default: pipe

How to setup the subprocess' standard output. This can be:

  • 'pipe': Sets result.stdout (as a string or Uint8Array) and subprocess.stdout (as a stream).
  • 'overlapped': Like 'pipe' but asynchronous on Windows.
  • 'ignore': Do not use stdout.
  • 'inherit': Re-use the current process' stdout.
  • an integer: Re-use a specific file descriptor from the current process.
  • a Node.js Writable stream.
  • { file: 'path' } object.
  • a file URL.
  • a web WritableStream.

This can be an array of values such as ['inherit', 'pipe'] or [filePath, 'pipe'].

This can also be a generator function, a Duplex or a web TransformStream to transform the output. Learn more.

More info.

options.stderr

Type: string | number | stream.Writable | WritableStream | TransformStream | URL | {file: string} | GeneratorFunction<string | Uint8Array | unknown> | AsyncGeneratorFunction<string | Uint8Array | unknown> | {transform: GeneratorFunction | AsyncGeneratorFunction | Duplex | TransformStream} (or a tuple of those types)
Default: pipe

How to setup the subprocess' standard error. This can be:

  • 'pipe': Sets result.stderr (as a string or Uint8Array) and subprocess.stderr (as a stream).
  • 'overlapped': Like 'pipe' but asynchronous on Windows.
  • 'ignore': Do not use stderr.
  • 'inherit': Re-use the current process' stderr.
  • an integer: Re-use a specific file descriptor from the current process.
  • a Node.js Writable stream.
  • { file: 'path' } object.
  • a file URL.
  • a web WritableStream.

This can be an array of values such as ['inherit', 'pipe'] or [filePath, 'pipe'].

This can also be a generator function, a Duplex or a web TransformStream to transform the output. Learn more.

More info.

options.stdio

Type: string | Array<string | number | stream.Readable | stream.Writable | ReadableStream | WritableStream | TransformStream | URL | {file: string} | Uint8Array | Iterable<string> | Iterable<Uint8Array> | Iterable<unknown> | AsyncIterable<string | Uint8Array | unknown> | GeneratorFunction<string | Uint8Array | unknown> | AsyncGeneratorFunction<string | Uint8Array | unknown> | {transform: GeneratorFunction | AsyncGeneratorFunction | Duplex | TransformStream}> (or a tuple of those types)
Default: pipe

Like the stdin, stdout and stderr options but for all file descriptors at once. For example, {stdio: ['ignore', 'pipe', 'pipe']} is the same as {stdin: 'ignore', stdout: 'pipe', stderr: 'pipe'}.

A single string can be used as a shortcut. For example, {stdio: 'pipe'} is the same as {stdin: 'pipe', stdout: 'pipe', stderr: 'pipe'}.

The array can have more than 3 items, to create additional file descriptors beyond stdin/stdout/stderr. For example, {stdio: ['pipe', 'pipe', 'pipe', 'pipe']} sets a fourth file descriptor.

More info.

options.all

Type: boolean
Default: false

Add a subprocess.all stream and a result.all property. They contain the combined/interleaved output of the subprocess' stdout and stderr.

options.lines

Type: boolean
Default: false

Set result.stdout, result.stderr, result.all and result.stdio as arrays of strings, splitting the subprocess' output into lines.

This cannot be used if the encoding option is binary.

By default, this applies to both stdout and stderr, but different values can also be passed.

options.encoding

Type: string
Default: 'utf8'

If the subprocess outputs text, specifies its character encoding, either 'utf8' or 'utf16le'.

If it outputs binary data instead, this should be either:

  • 'buffer': returns the binary output as an Uint8Array.
  • 'hex', 'base64', 'base64url', 'latin1' or 'ascii': encodes the binary output as a string.

The output is available with result.stdout, result.stderr and result.stdio.

options.stripFinalNewline

Type: boolean
Default: true

Strip the final newline character from the output.

If the lines option is true, this applies to each output line instead.

By default, this applies to both stdout and stderr, but different values can also be passed.

options.maxBuffer

Type: number
Default: 100_000_000

Largest amount of data allowed on stdout, stderr and stdio.

When this threshold is hit, the subprocess fails and error.isMaxBuffer becomes true.

This is measured:

By default, this applies to both stdout and stderr, but different values can also be passed.

options.ipc

Type: boolean
Default: true if the node option is enabled, false otherwise

Enables exchanging messages with the subprocess using subprocess.send(message) and subprocess.on('message', (message) => {}).

options.serialization

Type: string
Default: 'advanced'

Specify the kind of serialization used for sending messages between subprocesses when using the ipc option:

  • json: Uses JSON.stringify() and JSON.parse().
  • advanced: Uses v8.serialize()

More info.

options.detached

Type: boolean
Default: false

Prepare subprocess to run independently of the current process. Specific behavior depends on the platform.

More info..

options.cleanup

Type: boolean
Default: true

Kill the subprocess when the current process exits unless either:

  • the subprocess is detached.
  • the current process is terminated abruptly, for example, with SIGKILL as opposed to SIGTERM or a normal exit.

options.timeout

Type: number
Default: 0

If timeout is greater than 0, the subprocess will be terminated if it runs for longer than that amount of milliseconds.

options.cancelSignal

Type: AbortSignal

You can abort the subprocess using AbortController.

When AbortController.abort() is called, result.isCanceled becomes true.

options.forceKillAfterDelay

Type: number | false
Default: 5000

If the subprocess is terminated but does not exit, forcefully exit it by sending SIGKILL.

The grace period is 5 seconds by default. This feature can be disabled with false.

This works when the subprocess is terminated by either:

This does not work when the subprocess is terminated by either:

Also, this does not work on Windows, because Windows doesn't support signals: SIGKILL and SIGTERM both terminate the subprocess immediately. Other packages (such as taskkill) can be used to achieve fail-safe termination on Windows.

options.killSignal

Type: string | number
Default: SIGTERM

Signal used to terminate the subprocess when:

This can be either a name (like "SIGTERM") or a number (like 9).

options.argv0

Type: string

Explicitly set the value of argv[0] sent to the subprocess. This will be set to file if not specified.

options.uid

Type: number

Sets the user identity of the subprocess.

options.gid

Type: number

Sets the group identity of the subprocess.

options.windowsVerbatimArguments

Type: boolean
Default: false

If true, no quoting or escaping of arguments is done on Windows. Ignored on other platforms. This is set to true automatically when the shell option is true.

options.windowsHide

Type: boolean
Default: true

On Windows, do not create a new console window. Please note this also prevents CTRL-C from working on Windows.

Tips

Redirect stdin/stdout/stderr to multiple destinations

The stdin, stdout and stderr options can be an array of values. The following example redirects stdout to both the terminal and an output.txt file, while also retrieving its value programmatically.

const {stdout} = await execa('npm', ['install'], {stdout: ['inherit', './output.txt', 'pipe']});
console.log(stdout);

When combining inherit with other values, please note that the subprocess will not be an interactive TTY, even if the current process is one.

Redirect a Node.js stream from/to stdin/stdout/stderr

When passing a Node.js stream to the stdin, stdout or stderr option, Node.js requires that stream to have an underlying file or socket, such as the streams created by the fs, net or http core modules. Otherwise the following error is thrown.

TypeError [ERR_INVALID_ARG_VALUE]: The argument 'stdio' is invalid.

This limitation can be worked around by passing either:

- await execa(..., {stdout: nodeStream});
+ await execa(..., {stdout: [nodeStream, 'pipe']});

Retry on error

Safely handle failures by using automatic retries and exponential backoff with the p-retry package:

import pRetry from 'p-retry';

const run = async () => {
	const results = await execa('curl', ['-sSL', 'https://sindresorhus.com/unicorn']);
	return results;
};

console.log(await pRetry(run, {retries: 5}));

Cancelling a subprocess

import {execa} from 'execa';

const abortController = new AbortController();
const subprocess = execa('node', [], {cancelSignal: abortController.signal});

setTimeout(() => {
	abortController.abort();
}, 1000);

try {
	await subprocess;
} catch (error) {
	console.log(error.isTerminated); // true
	console.log(error.isCanceled); // true
}

Execute the current package's binary

Execa can be combined with get-bin-path to test the current package's binary. As opposed to hard-coding the path to the binary, this validates that the package.json bin field is correctly set up.

import {getBinPath} from 'get-bin-path';

const binPath = await getBinPath();
await execa(binPath);

Ensuring all output is interleaved

The subprocess.all stream and result.all string/Uint8Array property are guaranteed to interleave stdout and stderr.

However, for performance reasons, the subprocess might buffer and merge multiple simultaneous writes to stdout or stderr. This prevents proper interleaving.

For example, this prints 1 3 2 instead of 1 2 3 because both console.log() are merged into a single write.

import {execa} from 'execa';

const {all} = await execa('node', ['example.js'], {all: true});
console.log(all);
// example.js
console.log('1'); // writes to stdout
console.error('2'); // writes to stderr
console.log('3'); // writes to stdout

This can be worked around by using setTimeout().

import {setTimeout} from 'timers/promises';

console.log('1');
console.error('2');
await setTimeout(0);
console.log('3');

Related

Maintainers

execa's People

Contributors

aaronccasanova avatar acburdine avatar addaleax avatar ammarbinfaisal avatar bendingbender avatar bradfordlemley avatar brandon93s avatar coreyfarrell avatar dignifiedquire avatar ehmicky avatar fisker avatar forivall avatar fregante avatar gmartigny avatar honzajavorek avatar jamestalmage avatar jopemachine avatar justsml avatar kevva avatar kikobeats avatar leafac avatar linusu avatar mifi avatar otakustay avatar papb avatar samverschueren avatar satazor avatar sindresorhus avatar sonicdoe avatar strikeentco avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

execa's Issues

`preferLocal` fails for globally installed CLI programs

When you have a globally installed binary that uses execa within it, the preferLocal option doesn't work correctly.

Example: foo is a js cli program that depends on a (which has a binary installed to foo's node_modules/.bin/ folder.

npm link # within foo directory
foo doSomethingThatCallsA # within another directory
output: spawn a ENOENT

The reason for this is because npmRunPath uses the CWD by default. This isn't an issue in npm-run-path necessarily though, because execa should provide an option (along with preferLocal to specify the directory to look into.

For example:

// foo.js
const execa = require('execa');

execa('a', ['some', 'options'], {
    preferLocal: true,
    localDir: __dirname 
});

More than happy to PR this, just want to get approval on the localDir option name or to have a better name suggested before I submit a PR 😄

Why append the entire contents of stdout on error?

It seems just as likely the info needed will be in stderr, and it could be a whole bunch of data to print. Also, it gives you no means of extracting the original error.message property.

Is this providing any real value? If people want to log it to the console, can't they just:

console.log(error.message + error.stdout);

Promisify spawn

Is there any reason that execa.spawn isn't promisified as execa?

Undefined and null argument variables are passed as strings

Undefined and null variables are passed to child process as their string representations, "undefined" and "null". I'm not sure what the expected behavior is, but when working with shell scripts, I expect the undefined or null arguments to be passed as empty strings.

Observable support with customizable through filters

var child = execa('./cli.js', [], {stdio: [null, 'observable', null]});

t.plan(5); // should get 5 lines
await child.stdin
  .filter(line => /^s/.test(line)) // only lines that start with "s"
  .forEach(line => t.true(fitsSpecifiedFormat(line)));

Additionally, it would be cool if you could use DuplexStreams to modify what reaches the observable, I'm thinking "newline-separated-json" byte stream converted to a JSON object stream, etc.

There are a number of "split-stream-XXX" modules already on NPM, but they convert to a stream, would be cool to write object-stream-to-observable, and then use that to make CLI output processing awesome.

How to maintain console colors

I have created a simple script:

	execa('bash',['./scripts/lint-styles.sh'])
		.then(result => {
			console.log(result.stdout);
		});

This script calls

#!/usr/bin/env bash

./node_modules/.bin/sass-lint "./resources/assets/sass/*.s+(a|c)ss" -q -v

When executed, the output is correct, but it does not have the color output which displays when executing from cli

Shorthand for piping stdout and stderr

I use this pattern a lot and have created a function for personal use:

const cp = execa('echo', ['foo', 'unicorns'])
cp.stdout.pipe(process.stdout)
cp.stderr.pipe(process.stderr)

Do you think it's a good idea to include it as a shorthand in the library? It could be named execa.echo or execa.log or execa.piped...

Sync methods

execa already cover the async methods and the sync methods could then take advantage of of the npm path thing, and utf8 by default instead of buffer.

Allow transform streams convert output to an array of any objects, instead of a single string

We need to think through the ideal API for this and #20 as well, but something like:

execa('./cli', [], {
  transforms: {
     // shortcut to a built-in transform we provide
    stdout: 'line',

    stderr: through2(function (chunk, encoding, callback) {
      // custom processing here
    })
  }
}).then(function (result) {
  result.stdout;
  // => an array of lines

  result.stderr;
  // => an array of whatever the custom transform emits
});

re-implement errname resolution

#27 dropped execFile in favor of using spawn directly, so we've lost some features.

One is whatever these two lines do:

  1. https://github.com/nodejs/node/blob/31600735f40e02935b936802ff42d66d49e9769b/lib/child_process.js#L8
  2. https://github.com/nodejs/node/blob/31600735f40e02935b936802ff42d66d49e9769b/lib/child_process.js#L204

Can we reliably just do process.binding('uv')? Or is that asking for problems. I know it means importing some native lib, but not much beyond that.

Maybe we look back all the way to Node 0.12 and make sure it was still imported and used the same back then?

readable stream 'end' event

This is not an issue, but more a question on node streams.
Just wanted to know if there is any technical difference between those 2 implementations

  function runCommandAndWriteToFile (cmd, name) {
   // cmd is an execa instance
    return new Promise((resolve) => {
      cmd.stderr.pipe(process.stdout)
      cmd.stderr.on('end', () => {
        cmd.then((res) => {
          const msg = `Command run:\n${res.cmd}`
          utils.log(chalk.magenta(msg))
          resolve()
        })
      })
    })
  }

and

  function runCommandAndWriteToFile (cmd, name) {
    cmd.stderr.pipe(process.stdout)
    return cmd.then((res) => {
      const msg = `Command run:\n${res.cmd}`
      utils.log(chalk.magenta(msg))
    })
  }

thanks

warning: possible EventEmitter memory leak detected

Description

My test print a warning

(node) warning: possible EventEmitter memory leak detected. 11 exit listeners added. Use emitter.setMaxListeners() to increase limit.

I use execa 16 times in my unit tests

Test Source

tests are here : https://github.com/lesspass/cli/blob/master/test.js

I try to test my command line interface with 16 tests like this one :

test('test space in password', async t => {
    const {stdout} = await execa('./cli.js', ['lesspass.com', '[email protected]', 'my Master Password']);
    t.is(stdout, 'onAV7&uvEC2=');
});

Here the output for my tests : https://travis-ci.org/lesspass/cli/jobs/169243714

Github

Here the github repository to reproduce the bug : https://github.com/lesspass/cli

Environment

Node.js v4.6.0
linux 4.4.0-45-generic
ava version 0.16.0
excea version 0.5.0
npm version 3.10.8

How can I avoid to raise this warning ?

previously post on avajs/ava#1087

Error: spawn EACCES

When I run my tests, which have this format:

import test from 'ava';
import execa from 'execa';

test('action', async (t) => {
  const { stdout } = await execa('./cli.js', ['action']);
  t.true(stdout.search('export default ClassName;') > -1);
});

I am getting the Error of Error: spawn EACCES on each test run.

image

I have a feeling it could be something simple I am overlooking, like bad formatting or something, but figured I would ask here in case I have stumbled onto something. Is this error trying to tell me that the module doesn't have access to execa's index.js file from within the node_modules directory? I deleted the node_modules directory and reinstalled everything, but I got the same set of errors.

If you want to see more of the code in context, the module that is experiencing this issue is here.

Thanks in advance for your help.

Add `.fork()` method

Issuehunt badges

We could just use execa('node', ['becomeTheOne.js']), but that would mean not capitalising on the added optimization that happens when using fork, as mentioned here.

There is no spoon matrix


IssueHunt Summary

[
<
i
m
g

s
r
c

'
h
t
t
p
s
:
/
/
a
v
a
t
a
r
s
3
.
g
i
t
h
u
b
u
s
e
r
c
o
n
t
e
n
t
.
c
o
m
/
u
/
2
5
4
3
5
1
1
?
v

4
'

a
l
t

'
g
m
a
r
t
i
g
n
y
'

w
i
d
t
h

2
4

h
e
i
g
h
t

2
4

g
m
a
r
t
i
g
n
y
]
(
h
t
t
p
s
:
/
/
i
s
s
u
e
h
u
n
t
.
i
o
/
u
/
g
m
a
r
t
i
g
n
y
)

h
a
s

b
e
e
n

r
e
w
a
r
d
e
d
.

Backers (Total: $60.00)

Submitted pull Requests


Tips


IssueHunt has been backed by the following sponsors. Become a sponsor

Inherited stdio results in null stdout, stderr in Error

Ran across a funky error case today while converting gulp-mocha to use execa. If {stdio: 'inherit'} is used in execa's options, and the command fails, the resulting Error message contains nullnull

Line in question: https://github.com/sindresorhus/execa/blob/master/index.js#L201

Example output:

{ Error: Command failed: mocha /.../github/gulp-mocha/test/fixtures/fixture-fail.js
nullnull
    at Promise.all.then.arr (/.../github/gulp-mocha/node_modules/execa/index.js:201:11)
    at process._tickCallback (internal/process/next_tick.js:103:7)

FWIW, for commands that succeed, result.stdout and result.stderr are also both null in the .then(result => handler when {stdio: 'inherit'{ is used. That isn't mentioned anywhere in the README, and the option appears to be the only way to preserve terminal formatting.

Ideas?

Are there any annoyances you've had with child processes that could potentially be fixed in execa? Anything that could be done better? I'm open for any kind of suggestions.

Automatically wait for file streams to open

It might be possible to wait for the open event on file streams (if we can find a way to detect that a stream is in fact a file stream, and that it will indeed eventually open).

See the discussion here

`execa` and `execa.sync` have return type mismatches.

I think it would be better if the following were basically equivalent:

const val = execa.sync(...args);

const val = await execa(...args);

This would mean a breaking change to execa.sync, using child.spawnSync instead, but I think it's worth it to have a more consistent API.

Add some tests around the logic copied out of `execFile`

#27 really only went so far as copying the execFile implementation where necessary to get tests passing using spawn.

Most of the required test aren't terribly consequential, but should still be completed. The one set of tests I can think of right now is the creation of the Error property. It builds the command by concatting args together, then uses that to build the error message. Also, it attaches a few extra properties: killed, code, etc.

Examine the diff of #27 for a complete picture of what should be tested.

I will tackle this, just recording for posterity.

Stdio `tap` option

For stderr and stdout the choice between inherit and pipe, has tradeoffs.

Specifically, if you choose inherit, you loose the ability to observe the stream, it's piped straight to the parents stdout/stderr.

I propose we add a tap option. It will be converted internally to the pipe option, but we will grab the pipe, split it into two streams, one stream we pipe directly to stdout/stderr, the other we make available for observation.

Capture exit code

I'm not sure what would be the best way to implement this, but it would be great if this module also captured the exit code of whatever file or command is ran somehow.

stdout encoding issue.

This is more of a heads up to an interesting issue I've run into. I'm curious if others have seen it and if there's a known workaround.

foo.py (Python 2.7.10):

#!/usr/bin/env python
import sys
print(sys.stdout.encoding)

foo.js (execa v0.6.0, node 7.6.0):

'use strict'
const execa = require('execa')
const spawned = execa('./foo.py', [], { 'encoding': 'utf8' })
spawned.stdout.setEncoding('utf8')
spawned.then(({ stdout }) => console.log(stdout))

running node foo.js:

None

I'm wondering how to ensure the invoked Python script has a stdout.encoding of UTF-8.

Getting the pid of a long-running process

I'm using execa to start a process that runs "forever" (a web server). I would like to send a kill signal to the process about 30 seconds after launch, but I don't know how/where to get its process id from execa. Is it possible?

At the moment, execa returns a promise that is only resolved after the process ends, which isn't really useful for indefinitely-running processes. It would be nice if execa returned some additional info along with the promise, like the pid.

Ability to turn off buffering

If you only pipe the output it's a waste of resources to buffer it and the user can also easily hit the maxBuffer.

Either {buffer: false} or detect when .then() is used. I would prefer the latter, but not sure how feasible it is.


This would have prevented sindresorhus/gulp-mocha#169

Doesn't work with electron

Feel free to close this as a wontfix but I wanted to log the issue anyway.

With standard exec:

const exec = require('child_process').exec;
exec('networksetup', console.log.bind(console));
// 'networksetup Help Information...' Works OK

With execa:

const execa = require('execa');
execa.shell('networksetup').then(console.log.bind(console));
// Error: Command failed: /bin/sh -c networksetup

execa.shell not running on windows

This code from the readme was tested on windows 7:

execa.shell('echo unicorns').then(result => {
    console.log(result.stdout);
});

and the result was no output at all. No error, unicorns or anything.

I also tested known windows commands like dir, and echo:

execa.shell('dir').then(result => {
    console.log(result.stdout);
});

execa.shell('echo unicorns').then(result => {
    console.log(result.stdout);
});

None of these commands produced any results. Just nothing in the console at all.

However, when I run the execa example, it works:

execa('echo', ['unicorns']).then(result => {
    console.log(result.stdout);
});
//=> unicorns

Am I missing something simple?

`reject` option

Currently execa rejects the promise on non-zero exit code. Sometimes though, you want to get the output regardless, like e.g. running ESLint and reading the result regardless of it having linter issues.

Not totally sold on the name. Suggestions welcome.

Does this option make sense?

execa.shell(cmd, {shell: '/bin/bash'}) only runs the first word of command

Let's say that I have to run man ls command.

// runs correctly, with man page of 'ls' in stdout
execa.shell('man ls')
  .then(function (result) {
    console.log(`stdout: ${result.stdout}`)
    console.log(`stderr: ${result.stderr}`)
  })

but

// this just runs 'man'
execa.shell('man ls', {shell: '/bin/bash'})
  .then(function (result) {
    console.log(`stdout: ${result.stdout}`)
    console.log(`stderr: ${result.stderr}`)
  })

Output

stdout: 
stderr: What manual page do you want?

Cleanup doesn't work reliably

I'm using execa to start a child node process which simply starts an express server listening on port 5000.

But this child process often ends up running after I've exited the parent program, so when I run the parent again, I get an error saying port 5000 is already bound when it tries to start the child process. This happens about 25% of the time.

I haven't yet figured out what conditions cause the cleanup to fail. Any ideas?

Release?

Could you release a new version with the reject option available?

Kill child when exiting

If I use execa.shell to spawn a long running process, but the original process dies for some reason it would be great if the spawned process gets terminated automatically.

A small example

'use strict'

const execa = require('execa')

execa.shell('ipfs daemon')
  .then((res) => {
    console.log(res.stdout)
  })
  .catch((err) => {
    console.log('failed', err)
  })

console.log('starting')
setTimeout(() => {
  console.log('exiting')
  process.exit()
}, 10 * 1000)

Running this will print exiting after 10s and exit but when I grep for ipfs dameon the process is still running.

Crash when running file without execute permissions

Steps to reproduce:

  1. Create a normal file touch foo.sh
  2. chmod -x foo.sh to be explicit.
  3. npm i execa
  4. Run execa('foo.sh') as below
// index.js
const execa = require('execa')
execa(__dirname + '/foo.sh')
  .then(() => console.log('Ran successfully'))
  .catch(err => console.error('Caught an error', err))

Crashes with error message

/repos/node_modules/execa/index.js:120
        spawned.then = promise.then.bind(promise);
                     ^

TypeError: Cannot set property 'then' of undefined
    at module.exports (/repos/node_modules/execa/index.js:120:15)
    at Object.<anonymous> (/repos/index.js:2:1)
    at Module._compile (module.js:556:32)
    at Object.Module._extensions..js (module.js:565:10)
    at Module.load (module.js:473:32)
    at tryModuleLoad (module.js:432:12)
    at Function.Module._load (module.js:424:3)
    at Module.runMain (module.js:590:10)
    at run (bootstrap_node.js:394:7)
    at startup (bootstrap_node.js:149:9)

Allow userland streams to be passed to `stdio` option

UPDATE: I think this is the right way to do it. Everything in between here and there is interesting background though.


I think you should be able to do this:

const PassThrough = require('stream').PassThrough;
const execa = require('execa');

const stdin = new PassThrough();
const stdout = new PassThrough();
const stderr = new PassThrough();

execa('foo', [], {stdio: [stdin, stdout, stderr]});

But you aren't actually allowed to pass any old stream to child_process. It has to be a very specific type of native stream (here is the internal Node code that checks that).

Instead, if you've got some userland stream you want to use, you've got to go about it a bit more convoluted way:

const PassThrough = require('stream').PassThrough;
const execa = require('execa');

const stdin = new PassThrough();
const stdout = new PassThrough();
const stderr = new PassThrough();

const cp = execa('foo', [], ['pipe', 'pipe', 'pipe']);

stdin.pipe(cp.stdin);
cp.stdout.pipe(stdout);
cp.stderr.pipe(stderr);

I think it's a weird choice to allow streams, but only certain types of streams. I've written native-stream-type which would help us figure out if we can pass the stream directly to child_process.spawn or not. For userland streams, we just convert to using pipe.

Add a promise-getter to spawned child process objects

promoted from #6 (comment)

Sometimes you want to use execa.spawn() because you need an object representing the child process while it's running, so you can send signals or listen to events. But then you lose the convenience of a promise-based API (i.e. having a thing you can easily await, which will collect up the stdout/stderr for you).

Suggest adding a method to spawned child objects. This method would return a promise that resolves when the child process exits.

const child = execa.spawn(...);

// do any necessary manual stuff with events/signals etc
child.on(...);

// await the final outcome
const { stdio, stderr, code } = await child.exited();

(I'm not sure about the name exited though.)


Possible extra feature: the method could take an optional parameter specifying the expected exit code, e.g. .exited(0). If the child process exits with any other code, the promise rejects. If no exit code is specified, the promise will always resolve when the child process exits, no matter how.

Better `stdio` option.

I think the stdio array is a pretty poor UI, you have to remember the order of things. I'm constantly pulling up that piece of documentation. Instead, of the three member array, let's just give each one it's own named property:

execa('./cli.js', [], {
  stdin: someReadableStream,
  stdout: 'pipe',
  stderr: 'inherit'
});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.