Process execution for humans
npm install execa

> Process execution for humans
---
Sindre's open source work is supported by the community

---
Execa runs commands in your script, application or library. Unlike shells, it is optimized for programmatic usage. Built on top of the child_process core module.
- Simple syntax: promises and template strings, like zx.
- Script interface.
- No escaping nor quoting needed. No risk of shell injection.
- Execute locally installed binaries without npx.
- Improved Windows support: shebangs, PATHEXT, graceful termination, and more.
- Detailed errors, verbose mode and custom logging, for debugging.
- Pipe multiple subprocesses better than in shells: retrieve intermediate results, use multiple sources/destinations, unpipe.
- Split the output into text lines, or iterate progressively over them.
- Strip unnecessary newlines.
- Pass any input to the subprocess: files, strings, Uint8Arrays, iterables, objects and almost any other type.
- Return almost any type from the subprocess, or redirect it to files.
- Get interleaved output from stdout and stderr similar to what is printed on the terminal.
- Retrieve the output programmatically and print it on the console at the same time.
- Transform or filter the input and output with simple functions.
- Pass Node.js streams or web streams to subprocesses, or convert subprocesses to a stream.
- Exchange messages with the subprocess.
- Ensure subprocesses exit even when they intercept termination signals, or when the current process ends abruptly.
``sh`
npm install execa
Execution:
- ▶️ Basic execution
- 💬 Escaping/quoting
- 💻 Shell
- 📜 Scripts
- 🐢 Node.js files
- 🌐 Environment
- ❌ Errors
- 🏁 Termination
Input/output:
- 🎹 Input
- 📢 Output
- 📃 Text lines
- 🤖 Binary data
- 🧙 Transforms
Advanced usage:
- 🔀 Piping multiple subprocesses
- ⏳️ Streams
- 📞 Inter-process communication
- 🐛 Debugging
- 📎 Windows
- 🔍 Difference with Bash and zx
- 🐭 Small packages
- 🤓 TypeScript
- 📔 API reference
#### Simple syntax
`js
import {execa} from 'execa';
const {stdout} = await execanpm run build;`
// Print command's output
console.log(stdout);
#### Script
`js
import {$} from 'execa';
const {stdout: name} = await $cat package.json.pipegrep name;
console.log(name);
const branch = await $git branch --show-current;dep deploy --branch=${branch}
await $;
await Promise.all([
$sleep 1,sleep 2
$,sleep 3
$,
]);
const directoryName = 'foo bar';
await $mkdir /tmp/${directoryName};`
#### Local binaries
`sh`
$ npm install -D eslint
`jseslint
await execa({preferLocal: true});`
#### Pipe multiple subprocesses
`jsnpm run build
const {stdout, pipedFrom} = await execasort
.pipehead -n 2
.pipe;
// Output of npm run build | sort | head -n 2npm run build | sort
console.log(stdout);
// Output of npm run build
console.log(pipedFrom[0].stdout);
// Output of `
console.log(pipedFrom[0].pipedFrom[0].stdout);
#### Interleaved output
`jsnpm run build
const {all} = await execa({all: true});`
// stdout + stderr, interleaved
console.log(all);
#### Programmatic + terminal output
`jsnpm run build
const {stdout} = await execa({stdout: ['pipe', 'inherit']});`
// stdout is also printed to the terminal
console.log(stdout);
#### Simple input
`jssort
const getInputString = () => { / ... / };
const {stdout} = await execa({input: getInputString()});`
console.log(stdout);
#### File input
`jsnpm run build
// Similar to: npm run build < input.txt
await execa({stdin: {file: 'input.txt'}});`
#### File output
`jsnpm run build
// Similar to: npm run build > output.txt
await execa({stdout: {file: 'output.txt'}});`
#### Split into text lines
`jsnpm run build
const {stdout} = await execa({lines: true});`
// Print first 10 lines
console.log(stdout.slice(0, 10).join('\n'));
#### Iterate over text lines
`jsnpm run build
for await (const line of execa) {`
if (line.includes('WARN')) {
console.warn(line);
}
}
#### Transform/filter output
`js
let count = 0;
// Filter out secret lines, then prepend the line number
const transform = function * (line) {
if (!line.includes('secret')) {
yield [${count++}] ${line};
}
};
await execa({stdout: transform})npm run build;`
#### Web streams
`jssort
const response = await fetch('https://example.com');
await execa({stdin: response.body});`
#### Convert to Duplex stream
`js
import {execa} from 'execa';
import {pipeline} from 'node:stream/promises';
import {createReadStream, createWriteStream} from 'node:fs';
await pipeline(
createReadStream('./input.txt'),
execanode ./transform.js.duplex(),`
createWriteStream('./output.txt'),
);
#### Exchange messages
`js
// parent.js
import {execaNode} from 'execa';
const subprocess = execaNodechild.js;`
await subprocess.sendMessage('Hello from parent');
const message = await subprocess.getOneMessage();
console.log(message); // 'Hello from child'
`js
// child.js
import {getOneMessage, sendMessage} from 'execa';
const message = await getOneMessage(); // 'Hello from parent'
const newMessage = message.replace('parent', 'child'); // 'Hello from child'
await sendMessage(newMessage);
`
#### Any input type
`js
// main.js
import {execaNode} from 'execa';
const ipcInput = [
{task: 'lint', ignore: /test\.js/},
{task: 'copy', files: new Set(['main.js', 'index.js']),
}];
await execaNode({ipcInput})build.js;`
`js
// build.js
import {getOneMessage} from 'execa';
const ipcInput = await getOneMessage();
`
#### Any output type
`js
// main.js
import {execaNode} from 'execa';
const {ipcOutput} = await execaNodebuild.js;`
console.log(ipcOutput[0]); // {kind: 'start', timestamp: date}
console.log(ipcOutput[1]); // {kind: 'stop', timestamp: date}
`js
// build.js
import {sendMessage} from 'execa';
const runBuild = () => { / ... / };
await sendMessage({kind: 'start', timestamp: new Date()});
await runBuild();
await sendMessage({kind: 'stop', timestamp: new Date()});
`
#### Graceful termination
`js
// main.js
import {execaNode} from 'execa';
const controller = new AbortController();
setTimeout(() => {
controller.abort();
}, 5000);
await execaNode({
cancelSignal: controller.signal,
gracefulCancel: true,
})build.js;`
`js
// build.js
import {getCancelSignal} from 'execa';
const cancelSignal = await getCancelSignal();
const url = 'https://example.com/build/info';
const response = await fetch(url, {signal: cancelSignal});
`
#### Detailed error
`js
import {execa, ExecaError} from 'execa';
try {
await execaunknown command;`
} catch (error) {
if (error instanceof ExecaError) {
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' ]
}
}
*/
}
#### Verbose mode
`jsnpm run build
await execa;npm run test
await execa;`

#### Custom logging
`js
import {execa as execa_} from 'execa';
import {createLogger, transports} from 'winston';
// Log to a file using Winston
const transport = new transports.File({filename: 'logs.txt'});
const logger = createLogger({transports: [transport]});
const LOG_LEVELS = {
command: 'info',
output: 'verbose',
ipc: 'verbose',
error: 'error',
duration: 'info',
};
const execa = execa_({
verbose(verboseLine, {message, ...verboseObject}) {
const level = LOG_LEVELS[verboseObject.type];
loggerlevel;
},
});
await execanpm run build;npm run test
await execa;``
- nano-spawn - Like Execa but smaller
- gulp-execa - Gulp plugin for Execa
- nvexeca - Run Execa using any Node.js version