Releases: sindresorhus/execa
v9.4.1
v9.4.0
Features
- We've created a separate package called nano-spawn. It is similar to Execa but with fewer features, for a much smaller package size. More info.
Bug fixes
- Both
execaNode()
and thepreferLocal
option modify thePATH
environment variable. This release includes some minor improvements to ensure that environment variable remains small (sindresorhus/npm-run-path#20). It also handles a few related edge cases better (sindresorhus/npm-run-path#21).
Documentation
v9.3.1
Thanks @holic and @jimhigson for your contributions!
Bugs
- Do not crash when using a custom Node.js binary without ICU support. (#1144)
Bugs (types)
- Fix type of the
env
option. It was currently failing for Remix or Next.js users. (by @holic) (#1141)
Documentation
- Fix typo in "Inputs" documentation. (by @jimhigson) (#1145)
- Document how to terminate hanging subprocesses. (#1140)
- Document how to add custom logging. (#1131)
v9.3.0
Features
- The
verbose
option can now be a function to customize logging. (#1130)
v9.2.0
This release includes a new set of methods to exchange messages between the current process and a Node.js subprocess, also known as "IPC". This allows passing and returning almost any message type to/from a Node.js subprocess. Also, debugging IPC is now much easier.
Moreover, a new gracefulCancel
option has also been added to terminate a subprocess gracefully.
For a deeper dive-in, please check and share the release post!
Thanks @iiroj for your contribution, @SimonSiefke and @adymorz for reporting the bugs fixed in this release, and @karlhorky for improving the documentation!
Deprecations
- Passing
'ipc'
to thestdio
option has been deprecated. It will be removed in the next major release. Instead, theipc: true
option should be used. (#1056)
- await execa('npm', ['run', 'build'], {stdio: ['pipe', 'pipe', 'pipe', 'ipc']});
+ await execa('npm', ['run', 'build'], {ipc: true});
- The
execaCommand()
method has been deprecated. It will be removed in the next major release. If most cases, the template string syntax should be used instead.
- import {execaCommand} from 'execa';
+ import {execa} from 'execa';
- await execaCommand('npm run build');
+ await execa`npm run build`;
const taskName = 'build';
- await execaCommand(`npm run ${taskName}`);
+ await execa`npm run ${taskName}`;
const commandArguments = ['run', 'task with space'];
await execa`npm ${commandArguments}`;
If the file and/or multiple arguments are supplied as a single string, parseCommandString(command) can split that string into an array. More info. (#1054)
- import {execaCommand} from 'execa';
+ import {execa, parseCommandString} from 'execa';
const commandString = 'npm run task';
- await execaCommand(commandString);
+ const commandArray = parseCommandString(commandString); // ['npm', 'run', 'task']
+ await execa`${commandArray}`;
// Or alternatively:
const [file, ...commandArguments] = commandArray;
await execa(file, commandArguments);
Features
- Add
gracefulCancel
option andgetCancelSignal()
method to terminate a subprocess gracefully.error.isGracefullyCanceled
was also added. (#1109) - Add
error.isForcefullyTerminated
. It istrue
when the subprocess was terminated by theforceKillAfterDelay
option. (#1111) - New methods to simplify exchanging messages between the current process and the subprocess. More info. (#1059, #1061, #1076, #1077, #1079, #1082, #1083, #1086, #1087, #1088, #1089, #1090, #1091, #1092, #1094, #1095, #1098, #1104, #1107)
- The current process sends messages with
subprocess.sendMessage(message)
and receives them withsubprocess.getOneMessage()
.subprocess.getEachMessage()
listens to multiple messages. - The subprocess uses
sendMessage(message)
,getOneMessage()
andgetEachMessage()
instead. Those are the same methods, but imported directly from the'execa'
module.
- The current process sends messages with
- The
ipcInput
option sends an IPC message from the current process to the subprocess as it starts. This enables passing almost any input type to a Node.js subprocess. (#1068) - The
result.ipcOutput
array contains all the IPC messages sent by the subprocess to the current process. This enables returning almost any output type from a Node.js subprocess. (#1067, #1071, #1075) - The error message now contains every IPC message sent by the subprocess. (#1067)
- The
verbose: 'full'
option now logs every IPC message sent by the subprocess, for debugging. More info here and there. (#1063)
Types
- Add
ExecaMethod
,ExecaNodeMethod
andExecaScriptMethod
,ExecaSyncMethod
andExecaScriptSyncMethod
types. (#1066) - Export the
Message
type, for IPC. (#1059) - Fix type of
forceKillAfterDelay: true
option. (#1116)
Bug fixes
- Fix passing a
{file}
to both thestdin
and thestdout
orstderr
options. (#1058) - Fix multiple minor problems with the
cancelSignal
option. (#1108) - Fix accidental publishing of Vim backup files. (#1074)
- Fix
engines.node
field inpackage.json
. Supported Node.js version is^18.19.0
or>=20.5.0
. (by @iiroj) (#1101)
v9.1.0
v9.0.2
v9.0.1
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...
v8.0.1
Fixes
- Fix and document support for the
{encoding: 'buffer'}
option. It is the same as{encoding: null}
, but preferred over it. (#572)