If the lines
option is true
, the output is split into lines, as an array of strings.
import {execa} from 'execa';
const lines = await execa({lines: true})`npm run build`;
console.log(lines.join('\n'));
The subprocess' return value is an async iterable. It iterates over the output's lines while the subprocess is still running.
for await (const line of execa`npm run build`) {
if (line.includes('ERROR')) {
console.log(line);
}
}
Alternatively, subprocess.iterable()
can be called to pass options or use iterator helpers.
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.
for await (const line of execa`npm run build`.iterable())) { /* ... */ }
Transforms map or filter the input or output of a subprocess, one line at a time. Unlike subprocess.iterable()
, they:
- Modify the subprocess' output and streams.
- Can apply to the subprocess' input.
- Are defined using a generator function,
Duplex
stream, Node.jsTransform
stream or webTransformStream
.
With Node 22 (and later), iterator methods can be used.
const iterable = execa`npm run build`.iterable();
for await (const line of iterable.filter(line => line.includes('ERROR')))) { /* ... */ }
const iterable = execa`npm run build`.iterable();
for await (const line of iterable.map(line => line.replaceAll('secret', '***')))) { /* ... */ }
const iterable = execa`npm run build`.iterable();
// Only first 3 lines
for await (const line of iterable.take(3)) { /* ... */ }
The subprocess' promise does not need to be awaited, except with Iterator.find()
, Iterator.some()
or Iterator.every()
, since those specific methods return as soon as a matching line is found.
const printErrorLine = async iterable => {
const errorLine = await iterable.find(line => line.includes('ERROR'));
console.log(errorLine);
};
const subprocess = execa`npm run build`;
await Promise.all([subprocess, printErrorLine(subprocess.iterable())]);
By default, the subprocess' stdout
is used. The from
iterable option can select a different file descriptor, such as stderr
, all
or fd3
.
for await (const stderrLine of execa`npm run build`.iterable({from: 'stderr'})) { /* ... */ }
The final newline is stripped from the output's last line, unless the stripFinalNewline
is false
.
const {stdout} = await execa({stripFinalNewline: false})`npm run build`;
console.log(stdout.endsWith('\n')); // true
When using the lines
option, newlines are stripped from each line, unless the stripFinalNewline
is false
.
// Each line now ends with '\n'.
// The last `line` might or might not end with '\n', depending on the output.
const lines = await execa({lines: true, stripFinalNewline: false})`npm run build`;
console.log(lines.join(''));
When iterating over lines, newlines are stripped from each line, unless the preserveNewlines
iterable option is true
.
This option can also be used with streams produced by subprocess.readable()
and subprocess.duplex()
and the binary: false
option.
// `line` now ends with '\n'.
// The last `line` might or might not end with '\n', depending on the output.
for await (const line of execa`npm run build`.iterable({preserveNewlines: true})) { /* ... */ }
When using transforms, newlines are stripped from each line
argument, unless the preserveNewlines
transform option is true
.
// `line` now ends with '\n'.
// The last `line` might or might not end with '\n', depending on the output.
const transform = function * (line) { /* ... */ };
await execa({stdout: {transform, preserveNewlines: true}})`npm run build`;
Each yield
produces at least one line. Calling yield
multiple times or calling yield *
produces multiples lines.
const transform = function * (line) {
yield 'Important note:';
yield 'Read the comments below.';
// Or:
yield * [
'Important note:',
'Read the comments below.',
];
// Is the same as:
yield 'Important note:\nRead the comments below.\n';
yield line
};
await execa({stdout: transform})`npm run build`;
However, if the preserveNewlines
transform option is true
, multiple yield
s produce a single line instead.
const transform = function * (line) {
yield 'Important note: ';
yield 'Read the comments below.\n';
// Is the same as:
yield 'Important note: Read the comments below.\n';
yield line
};
await execa({stdout: {transform, preserveNewlines: true}})`npm run build`;