A subprocess' output can be piped to another subprocess' input. The syntax is the same as execa(file, arguments?, options?)
.
import {execa} from 'execa';
// Similar to `npm run build | head -n 2` in shells
const {stdout} = await execa('npm', ['run', 'build'])
.pipe('head', ['-n', '2']);
const {stdout} = await execa`npm run build`
.pipe`head -n 2`;
const subprocess = execa`head -n 2`;
const {stdout} = await execa`npm run build`
.pipe(subprocess);
Options can be passed to either the source or the destination subprocess. Some pipe-specific options can also be set by the destination subprocess.
const {stdout} = await execa('npm', ['run', 'build'], subprocessOptions)
.pipe('head', ['-n', '2'], subprocessOrPipeOptions);
const {stdout} = await execa(subprocessOptions)`npm run build`
.pipe(subprocessOrPipeOptions)`head -n 2`;
const subprocess = execa(subprocessOptions)`head -n 2`;
const {stdout} = await execa(subprocessOptions)`npm run build`
.pipe(subprocess, pipeOptions);
When both subprocesses succeed, the result
of the destination subprocess is returned. The result
of the source subprocess is available in a result.pipedFrom
array.
const destinationResult = await execa`npm run build`
.pipe`head -n 2`;
console.log(destinationResult.stdout); // First 2 lines of `npm run build`
const sourceResult = destinationResult.pipedFrom[0];
console.log(sourceResult.stdout); // Full output of `npm run build`
When either subprocess fails, subprocess.pipe()
is rejected with that subprocess' error. If the destination subprocess fails, error.pipedFrom
includes the source subprocess' result, which is useful for debugging.
try {
await execa`npm run build`
.pipe`head -n 2`;
} catch (error) {
if (error.pipedFrom.length === 0) {
// `npm run build` failure
console.error(error);
} else {
// `head -n 2` failure
console.error(error);
// `npm run build` output
console.error(error.pipedFrom[0].stdout);
}
throw error;
}
await execa`npm run build`
.pipe`sort`
.pipe`head -n 2`;
const subprocess = execa`npm run build`;
const [sortedResult, truncatedResult] = await Promise.all([
subprocess.pipe`sort`,
subprocess.pipe`head -n 2`,
]);
const destination = execa`./log-remotely.js`;
await Promise.all([
execa`npm run build`.pipe(destination),
execa`npm run test`.pipe(destination),
]);
By default, the source's stdout
is used, but this can be changed using the from
piping option.
await execa`npm run build`
.pipe({from: 'stderr'})`head -n 2`;
By default, the destination's stdin
is used, but this can be changed using the to
piping option.
await execa`npm run build`
.pipe({to: 'fd3'})`./log-remotely.js`;
Piping can be stopped using the unpipeSignal
piping option.
The subprocess.pipe()
method will be rejected with a cancelation error. However, each subprocess will keep running.
const abortController = new AbortController();
process.on('SIGUSR1', () => {
abortController.abort();
});
// If the process receives SIGUSR1, `npm run build` stopped being logged remotely.
// However, it keeps running successfully.
try {
await execa`npm run build`
.pipe({unpipeSignal: abortController.signal})`./log-remotely.js`;
} catch (error) {
if (!abortController.signal.aborted) {
throw error;
}
}
Next: ⏳️ Streams
Previous: 🧙 Transforms
Top: Table of contents