v9.0.0
This major release brings many important features including:
- Split the output into lines, or progressively iterate over them.
- Transform or filter the input/output using simple functions.
- Print the output to the terminal while still retrieving it programmatically.
- Redirect the input/output from/to a file.
- Advanced piping between multiple subprocesses.
- Improved verbose mode, for debugging.
- More detailed errors, including when terminating subprocesses.
- Enhanced template string syntax.
- Global/shared options.
- Web streams and Transform streams support.
- Convert the subprocess to a stream.
- New documentation with many examples.
Please check the release post for a high-level overview! For the full list of breaking changes, features and bug fixes, please read below.
Thanks @younggglcy, @koshic, @am0o0 and @codesmith-emmy for your help!
One of the maintainers @ehmicky is looking for a remote full-time position. Specialized in Node.js back-ends and CLIs, he led Netlify Build, Plugins and Configuration for 2.5 years. Feel free to contact him on his website or on LinkedIn!
Breaking changes (not types)
-
Dropped support for Node.js version
<18.19.0
and20.0.0 - 20.4.0
. (834e372) -
When the
encoding
option is'buffer'
, the output (result.stdout
,result.stderr
,result.all
) is now anUint8Array
instead of aBuffer
. For more information, see this blog post. (by @younggglcy) (#586)
const {stdout} = await execa('node', ['file.js'], {encoding: 'buffer'});
console.log(stdout); // This is now an Uint8Array
- await execa('node', ['file.js'], {encoding: null});
+ await execa('node', ['file.js'], {encoding: 'buffer'});
- await execa('node', ['file.js'], {encoding: 'utf-8'});
+ await execa('node', ['file.js'], {encoding: 'utf8'});
- await execa('node', ['file.js'], {encoding: 'UTF8'});
+ await execa('node', ['file.js'], {encoding: 'utf8'});
- await execa('node', ['file.js'], {encoding: 'utf-16le'});
+ await execa('node', ['file.js'], {encoding: 'utf16le'});
- await execa('node', ['file.js'], {encoding: 'ucs2'});
+ await execa('node', ['file.js'], {encoding: 'utf16le'});
- await execa('node', ['file.js'], {encoding: 'ucs-2'});
+ await execa('node', ['file.js'], {encoding: 'utf16le'});
- await execa('node', ['file.js'], {encoding: 'binary'});
+ await execa('node', ['file.js'], {encoding: 'latin1'});
- Passing a file path to
subprocess.pipeStdout()
,subprocess.pipeStderr()
andsubprocess.pipeAll()
has been removed. Instead, a{file: './path'}
object should be passed to thestdout
orstderr
option. (#752)
- await execa('node', ['file.js']).pipeStdout('output.txt');
+ await execa('node', ['file.js'], {stdout: {file: 'output.txt'}});
- await execa('node', ['file.js']).pipeStderr('output.txt');
+ await execa('node', ['file.js'], {stderr: {file: 'output.txt'}});
- await execa('node', ['file.js']).pipeAll('output.txt');
+ await execa('node', ['file.js'], {
+ stdout: {file: 'output.txt'},
+ stderr: {file: 'output.txt'},
+});
- Passing a writable stream to
subprocess.pipeStdout()
,subprocess.pipeStderr()
andsubprocess.pipeAll()
has been removed. Instead, the stream should be passed to thestdout
orstderr
option. If the stream does not have a file descriptor,['pipe', stream]
should be passed instead. (#752)
- await execa('node', ['file.js']).pipeStdout(stream);
+ await execa('node', ['file.js'], {stdout: ['pipe', stream]});
- await execa('node', ['file.js']).pipeStderr(stream);
+ await execa('node', ['file.js'], {stderr: ['pipe', stream]});
- await execa('node', ['file.js']).pipeAll(stream);
+ await execa('node', ['file.js'], {
+ stdout: ['pipe', stream],
+ stderr: ['pipe', stream],
+});
- The
subprocess.pipeStdout()
,subprocess.pipeStderr()
andsubprocess.pipeAll()
methods have been renamed tosubprocess.pipe()
. The command and its arguments can be passed tosubprocess.pipe()
directly, without callingexeca()
a second time. Thefrom
piping option can specify'stdout'
(the default value),'stderr'
or'all'
. (#757)
- await execa('node', ['file.js']).pipeStdout(execa('node', ['other.js']));
+ await execa('node', ['file.js']).pipe('node', ['other.js']);
- await execa('node', ['file.js']).pipeStderr(execa('node', ['other.js']));
+ await execa('node', ['file.js']).pipe('node', ['other.js'], {from: 'stderr'});
- await execa('node', ['file.js']).pipeAll(execa('node', ['other.js']));
+ await execa('node', ['file.js']).pipe('node', ['other.js'], {from: 'all'});
- Renamed the
signal
option tocancelSignal
. (#880)
- await execa('node', ['file.js'], {signal: abortController.signal});
+ await execa('node', ['file.js'], {cancelSignal: abortController.signal});
- Renamed
error.killed
toerror.isTerminated
. (#625)
try {
await execa('node', ['file.js']);
} catch (error) {
- if (error.killed) {
+ if (error.isTerminated) {
// ...
}
}
subprocess.cancel()
has been removed. Please use eithersubprocess.kill()
or thecancelSignal
option instead. (#711)
- subprocess.cancel();
+ subprocess.kill();
- Renamed the
forceKillAfterTimeout
option toforceKillAfterDelay
. Also, it is now passed toexeca()
instead ofsubprocess.kill()
. (#714, #723)
- const subprocess = execa('node', ['file.js']);
- subprocess.kill('SIGTERM', {forceKillAfterTimeout: 1000});
+ const subprocess = execa('node', ['file.js'], {forceKillAfterDelay: 1000});
+ subprocess.kill('SIGTERM');
- The
verbose
option is now a string enum instead of a boolean.false
has been renamed to'none'
andtrue
has been renamed to'short'
. (#884)
- await execa('node', ['file.js'], {verbose: false});
+ await execa('node', ['file.js'], {verbose: 'none'});
- await execa('node', ['file.js'], {verbose: true});
+ await execa('node', ['file.js'], {verbose: 'short'});
- The
execPath
option has been renamed tonodePath
. It is now a noop unless thenode
option istrue
. Also, it now works even if thepreferLocal
option isfalse
. (#812, #815)
- await execa('node', ['file.js'], {execPath: './path/to/node'});
+ await execa('node', ['file.js'], {nodePath: './path/to/node'});
- The default value for the
serialization
option is now'advanced'
instead of'json'
. In particular, when callingsubprocess.send(object)
with an object that contains functions or symbols, those were previously silently removed. Now this will throw an exception. (#905)
- subprocess.send({example: true, getExample() {}});
+ subprocess.send({example: true});
- If
subprocess.stdout
,subprocess.stderr
orsubprocess.all
is manually piped, the.pipe()
call must now happen as soon assubprocess
is created. Otherwise, the output at the beginning of the subprocess might be missing. (#658, #747)
const subprocess = execa('node', ['file.js']);
- setTimeout(() => {
subprocess.stdout.pipe(process.stdout);
- }, 0);
- Signals passed to
subprocess.kill()
and to thekillSignal
option cannot be lowercase anymore. (#1025)
- const subprocess = execa('node', ['file.js'], {killSignal: 'sigterm'});
+ const subprocess = execa('node', ['file.js'], {killSignal: 'SIGTERM'});
- subprocess.kill('sigterm');
+ subprocess.kill('SIGTERM');
Features
Execution
- Use the template string syntax with any method (including
execa()
), as opposed to only$
. Conversely,$
can now use the regular array syntax. (#933) - A command's template string can span multiple lines. (#843)
- Share options between multiple calls, or set global options, by using
execa(options)
. (#933, #965) - Pass a file URL (as opposed to a file path string) to
execa()
,execaNode()
, theinputFile
option, thenodePath
option or theshell
option. (#630, #631, #632, #635)
Text lines
- Split the output into text lines by using the
lines
option. (#741, #929, #931, #948, #951, #957) - Subprocess is now an async iterable, iterating over the output lines while the subprocess is running. (#923)
Piping multiple subprocesses
- Simpler syntax: pass the command directly to
subprocess.pipe()
without callingexeca()
. A template string can also be used. (#840, #859, #864) - Wait for both subprocesses to complete. Error handling has been improved too. (#757, #778, #834, #854)
- Retrieve the result of each subprocess (not only the last one) by using
result.pipedFrom
anderror.pipedFrom
. (#834) - Pipe 1 or many subprocesses to 1 or many subprocesses. (#834)
- Pipe subprocesses using other file descriptors than
stdin
/stdout
/stderr
by using thefrom
andto
piping options. (#757, #834, #903, #920) - Cancel piping subprocesses by using the
unpipeSignal
piping option. (#834, #852)
Input/output
- Pass an array with multiple values to the
stdin
,stdout
andstderr
options. For example,stdout: ['inherit', 'pipe']
prints the output to the terminal while still returning it asresult.stdout
. (#643, #765, #941, #954) - Redirect the input/output from/to a file by passing a
{file: './path'}
object or a file URL to thestdin
,stdout
orstderr
option. (#610, #614, #621, #671, #1004) - Transform or filter the input/output by passing a generator function to the
stdin
,stdout
orstderr
option. (#693, #697, #698, #699, #709, #736, #737, #739, #740, #746, #748, #755, #756, #780, #783, #867, #915, #916, #917, #919, #924, #926, #945, #969) - Provide some binary input by passing an
Uint8Array
to theinput
orstdin
option. (834e372, #670, #1029) - Provide some progressive input by passing a sync/async iterable to the
stdin
option. (#604, #944) - Provide multiple inputs by combining the
stdin
,input
andinputFile
options. (#666) - Return other file descriptors than
result.stdout
andresult.stderr
by usingresult.stdio
. (#676) - Specify different values for
stdout
andstderr
with the following options:verbose
,lines
,stripFinalNewline
,maxBuffer
,buffer
. (#966, #970, #971, #972, #973, #974)
Streams
- Redirect the input/output from/to a web stream by passing a
ReadableStream
orWritableStream
to thestdin
,stdout
orstderr
option. (#615, #619, #645) - Transform or filter the input/output by passing a
Duplex
, Node.jsTransform
or webTransformStream
to thestdin
,stdout
orstderr
option. (#937, #938) - Convert the subprocess to a stream by using
subprocess.readable()
,subprocess.writable()
orsubprocess.duplex()
. (#912, #922, #958)
Verbose mode
- Print the subprocess' completion, duration and errors with the
verbose: 'short'
orverbose: 'full'
option. (#887, #890) - Print the subprocess' output with the
verbose: 'full'
option. (#884, #950, #962, #990) - Prettier formatting and colors with the
verbose
option. (#883, #893, #894)
Debugging
- Retrieve the subprocess' duration by using
result.durationMs
anderror.durationMs
. (#896) - Retrieve the subprocess' current directory by using
result.cwd
. Previously onlyerror.cwd
was available. Also,result.cwd
anderror.cwd
are now normalized to absolute file paths. (#803) - Printing
result.escapedCommand
in a terminal is now safe. (#875)
Errors
- The
ExecaError
andExecaSyncError
classes are now exported. (#911) - Find the subprocess failure's root cause by using
error.cause
. (#911) - Know whether the subprocess failed due to the
maxBuffer
option by usingerror.isMaxBuffer
. (#963) - Improved
error.message
:error.stdout
anderror.stderr
are now interleaved if theall
option istrue
. Additional file descriptors are now printed too. Also, the formatting has been improved. (#676, #705, #991, #992) - Control characters in
error.message
are now escaped, so they don't result in visual bugs when printed in a terminal. (#879) - Improved stack trace when an
error
event is emitted onsubprocess.stdout
orsubprocess.stderr
. (#814)
Termination
- Specify an error message or stack trace when terminating a subprocess by passing an error instance to
subprocess.kill()
. (#811, #836, #1023) - The
forceKillAfterDelay
andkillSignal
options now apply to terminations due not only tosubprocess.kill()
but also to thecancelSignal
,timeout
,maxBuffer
andcleanup
options. (#714, #728)
Node.js files
- Use the
nodePath
andnodeOptions
options with any method, as opposed to onlyexecaNode()
, by passing thenode: true
option. (#804, #812, #815) - When using
execaNode()
or thenode: true
option, the current Node.js version is now inherited deeply. If the subprocess spawns other subprocesses, they will all use the same Node.js version. (#812, #815, #1011)
Synchronous execution
- Use the
all
andbuffer: false
options withexecaSync()
, as opposed to onlyexeca()
. (#953, #956) - Added the
$.s
alias for$.sync
. (#594)
Inter-process communication
- Use the
ipc: true
option, as opposed to the more verbosestdio: ['pipe', 'pipe', 'pipe', 'ipc']
option. (#794)
Input validation
- Improved the validation of the
input
,timeout
,cwd
,detached
,cancelSignal
andencoding
options. (#668, #715, #803, #928, #940) - Improved the validation of the arguments passed to
execa()
and the other exported methods. (#838, #873, #899) - Improved the validation of signals passed to
subprocess.kill()
and to thekillSignal
option. (#1025)
Bug fixes
- Fixed passing
undefined
values as options. This now uses the option's default value. (#712) - Fixed the process crashing when the
inputFile
option points to a missing file. (#609) - Fixed the process crashing when the
buffer
option isfalse
andsubprocess.stdout
errors. (#729) - Fixed the process crashing when passing
'overlapped'
to thestdout
orstderr
option withexecaSync()
. (#949) - Fixed the process crashing when multiple
'error'
events are emitted on the subprocess. (#790) - Fixed the
reject: false
option not being used when the subprocess fails to spawn. (#734) - Fixed some inaccuracies with
error.isTerminated
. (#625, #719)- It is now
true
when the subprocess fails due to thetimeout
option. - It is now
true
when callingprocess.kill(subprocess.pid)
, except on Windows. - It is now
false
when using non-terminating signals such assubprocess.kill(0)
.
- It is now
- Fixed missing
error.signal
anderror.signalDescription
when the subprocess is terminated by thecancelSignal
option. (#724) - Fixed a situation where the error returned by an
execa()
call might be modified by anotherexeca()
call. (#796, #806, #911) - Fixed the
verbose
option printing the command in the wrong order. (#600) - Fixed using both the
maxBuffer
andencoding
options. For example, when usingencoding: 'hex'
,maxBuffer
will now be measured in hexadecimal characters. Also,error.stdout
,error.stderr
anderror.all
were previously not applying themaxBuffer
option. (#652, #696) - Fixed the
maxBuffer
option not truncatingresult.stdout
andresult.stderr
when usingexecaSync()
. (#960) - Fixed empty output when using the
buffer: true
option (its default value) and iterating oversubprocess.stdout
orsubprocess.stderr
. (#908) - Fixed
subprocess.all
stream incorrectly being in object mode. (#717) - Ensured
subprocess.stdout
andsubprocess.stderr
are properly flushed when the subprocess fails. (#647) - Fixed a race condition leading to random behavior with the
timeout
option. (#727)
Breaking changes (types)
-
The minimum supported TypeScript version is now
5.1.6
. -
Renamed
CommonOptions
type toOptions
(forexeca()
) andSyncOptions
(forexecaSync()
). (#678, #682)
import type {Options} from 'execa';
- const options: CommonOptions = {timeout: 1000};
+ const options: Options = {timeout: 1000};
import type {Options} from 'execa';
- const options: NodeOptions = {nodeOptions: ['--no-warnings']};
+ const options: Options = {nodeOptions: ['--no-warnings']};
import type {Options} from 'execa';
- const options: KillOptions = {forceKillAfterTimeout: 1000};
+ const options: Options = {forceKillAfterDelay: 1000};
- Removed generic parameters from the
Options
andSyncOptions
types. (#681)
import type {Options} from 'execa';
- const options: Options<'utf8'> = {encoding: 'utf8'};
+ const options: Options = {encoding: 'utf8'};
- Renamed
ExecaChildProcess
type toResultPromise
. This is the type ofexeca()
's return value, which is both aPromise<Result>
and aSubprocess
. (#897, #1007, #1009)
import type {ResultPromise, Result} from 'execa';
- const promiseOrSubprocess: ExecaChildProcess = execa('node', ['file.js']);
+ const promiseOrSubprocess: ResultPromise = execa('node', ['file.js']);
const result: Result = await promiseOrSubprocess;
promiseOrSubprocess.kill();
- Renamed
ExecaChildPromise
type toSubprocess
. This is the type of the subprocess instance. (#897, #1007, #1009)
import type {Subprocess} from 'execa';
- const subprocess: ExecaChildPromise = execa('node', ['file.js']);
+ const subprocess: Subprocess = execa('node', ['file.js']);
subprocess.kill();
- Renamed
ExecaReturnBase
,ExecaReturnValue
andExecaSyncReturnValue
type toResult
(forexeca()
) andSyncResult
(forexecaSync()
). (#897, #1009)
import type {Result, SyncResult} from 'execa';
- const result: ExecaReturnBase = await execa('node', ['file.js']);
+ const result: Result = await execa('node', ['file.js']);
- const result: ExecaReturnValue = await execa('node', ['file.js']);
+ const result: Result = await execa('node', ['file.js']);
- const result: ExecaSyncReturnValue = execaSync('node', ['file.js']);
+ const result: SyncResult = execaSync('node', ['file.js']);
- Renamed the type of the
stdin
option fromStdioOption
toStdinOption
(forexeca()
) andStdinSyncOption
(forexecaSync()
). (#942, #1008, #1012)
import {execa, type StdinOption} from 'execa';
- const stdin: StdioOption = 'inherit';
+ const stdin: StdinOption = 'inherit';
await execa('node', ['file.js'], {stdin});
- Renamed the type of the
stdout
andstderr
options fromStdioOption
toStdoutStderrOption
(forexeca()
) andStdoutStderrSyncOption
(forexecaSync()
). (#942, #1008, #1012)
import {execa, type StdoutStderrOption} from 'execa';
- const stdout: StdioOption = 'inherit';
+ const stdout: StdoutStderrOption = 'inherit';
- const stderr: StdioOption = 'inherit';
+ const stderr: StdoutStderrOption = 'inherit';
await execa('node', ['file.js'], {stdout, stderr});
- Renamed the type of the
stdio
option fromStdioOption[]
toOptions['stdio']
(forexeca()
) andSyncOptions['stdio']
(forexecaSync()
). (#942, #1008)
import {execa, type Options} from 'execa';
- const stdio: readonly StdioOption[] = ['inherit', 'pipe', 'pipe'] as const;
+ const stdio: Options['stdio'] = ['inherit', 'pipe', 'pipe'] as const;
await execa('node', ['file.js'], {stdio});
- The optional generic parameter passed to the
Result
,SyncResult
,ExecaError
,ExecaSyncError
,ResultPromise
andSubprocess
types is now anOptions
type. (#681)
import type {Result} from 'execa';
- const result: ExecaReturnValue<Buffer> = await execa('node', ['file.js'], {encoding: 'buffer'});
+ const result: Result<{encoding: 'buffer'}> = await execa('node', ['file.js'], {encoding: 'buffer'});
// Or even better, since it is inferred:
+ const result: Result = await execa('node', ['file.js'], {encoding: 'buffer'});
Improvements (types)
- Stricter types for the
stdin
,stdout
,stderr
andstdio
options. (#634, #943, #952) - Stricter types for
result.stdout
,result.stderr
,result.all
,subprocess.stdout
,subprocess.stderr
andsubprocess.all
. (#681, #684, #687, #689, #833) - Stricter types for the synchronous methods like
execaSync()
. (#678, #939) - Stricter types for the
reject
option. (#688) - Stricter types for
error.signal
and thekillSignal
option. (#1025) - Fixed the type of
error.exitCode
, since that field is sometimesundefined
. (#680) - Refactored and improved the types. (by @koshic) (#583)
Documentation
- Added user guides to let you explore each feature with many examples. (#989, #996, #1015, #1022, #1026)
- Improved the documentation and fixed inaccuracies. (#626, #637, #640, #641, #644, #669, #680, #710, #759, #800, #801, #802, #860, #870, #876, #888, #907, #921, #935, #967, #968, #994, #998, #999, #1000, #1003, #1005, #1006, #1010)
- Fixed the examples for the Script interface. (by @am0o0) (#575)
- Corrected some English grammar mistakes. (by @codesmith-emmy) (#731)