MCPcopy
hub / github.com/sindresorhus/execa

github.com/sindresorhus/execa @v9.6.1 sqlite

repository ↗ · DeepWiki ↗ · release v9.6.1 ↗
1,262 symbols 3,684 edges 581 files 0 documented · 0%
README
<img alt="execa logo" src="https://github.com/sindresorhus/execa/raw/v9.6.1/media/logo.svg" width="400">

Coverage Status

Process execution for humans


        <sup>
            <a href="https://github.com/sponsors/sindresorhus">Sindre's open source work is supported by the community</a>
        </sup>



    <sup>Special thanks to:</sup>









    <a href="https://coderabbit.ai?utm_source=sindre&utm_medium=execa">
        <img width="300" src="https://sindresorhus.com/assets/thanks/coderabbit-logo.png" alt="CodeRabbit logo">
    </a>









    <a href="https://kruu.com">









            <img src="https://sindresorhus.com/assets/thanks/kruu-logo-light.svg?y" width="260"alt="KRUU logo">



    </a>









    <a href="https://depot.dev?utm_source=github&utm_medium=sindresorhus">












                <img width="180" src="https://sindresorhus.com/assets/thanks/depot-logo-light.svg" alt="Depot logo">






        <b>Fast remote container builds and GitHub Actions runners.</b>
    </a>

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.

Features

Install

npm install execa

Documentation

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

Examples

Execution

Simple syntax

import {execa} from 'execa';

const {stdout} = await execa`npm run build`;
// Print command's output
console.log(stdout);

Script

import {$} from 'execa';

const {stdout: name} = await $`cat package.json`.pipe`grep name`;
console.log(name);

const branch = await $`git branch --show-current`;
await $`dep deploy --branch=${branch}`;

await Promise.all([
    $`sleep 1`,
    $`sleep 2`,
    $`sleep 3`,
]);

const directoryName = 'foo bar';
await $`mkdir /tmp/${directoryName}`;

Local binaries

$ npm install -D eslint
await execa({preferLocal: true})`eslint`;

Pipe multiple subprocesses

const {stdout, pipedFrom} = await execa`npm run build`
    .pipe`sort`
    .pipe`head -n 2`;

// Output of `npm run build | sort | head -n 2`
console.log(stdout);
// Output of `npm run build | sort`
console.log(pipedFrom[0].stdout);
// Output of `npm run build`
console.log(pipedFrom[0].pipedFrom[0].stdout);

Input/output

Interleaved output

const {all} = await execa({all: true})`npm run build`;
// stdout + stderr, interleaved
console.log(all);

Programmatic + terminal output

const {stdout} = await execa({stdout: ['pipe', 'inherit']})`npm run build`;
// stdout is also printed to the terminal
console.log(stdout);

Simple input

const getInputString = () => { /* ... */ };
const {stdout} = await execa({input: getInputString()})`sort`;
console.log(stdout);

File input

// Similar to: npm run build < input.txt
await execa({stdin: {file: 'input.txt'}})`npm run build`;

File output

// Similar to: npm run build > output.txt
await execa({stdout: {file: 'output.txt'}})`npm run build`;

Split into text lines

const {stdout} = await execa({lines: true})`npm run build`;
// Print first 10 lines
console.log(stdout.slice(0, 10).join('\n'));

Streaming

Iterate over text lines

for await (const line of execa`npm run build`) {
    if (line.includes('WARN')) {
        console.warn(line);
    }
}

Transform/filter output

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

const response = await fetch('https://example.com');
await execa({stdin: response.body})`sort`;

Convert to Duplex stream

import {execa} from 'execa';
import {pipeline} from 'node:stream/promises';
import {createReadStream, createWriteStream} from 'node:fs';

await pipeline(
    createReadStream('./input.txt'),
    execa`node ./transform.js`.duplex(),
    createWriteStream('./output.txt'),
);

IPC

Exchange messages

// parent.js
import {execaNode} from 'execa';

const subprocess = execaNode`child.js`;
await subprocess.sendMessage('Hello from parent');
const message = await subprocess.getOneMessage();
console.log(message); // 'Hello from child'
// 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

// 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`;
// build.js
import {getOneMessage} from 'execa';

const ipcInput = await getOneMessage();

Any output type

// main.js
import {execaNode} from 'execa';

const {ipcOutput} = await execaNode`build.js`;
console.log(ipcOutput[0]); // {kind: 'start', timestamp: date}
console.log(ipcOutput[1]); // {kind: 'stop', timestamp: date}
// 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

// main.js
import {execaNode} from 'execa';

const controller = new AbortController();
setTimeout(() => {
    controller.abort();
}, 5000);

await execaNode({
    cancelSignal: controller.signal,
    gracefulCancel: true,
})`build.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});

Debugging

Detailed error

import {execa, ExecaError} from 'execa';

try {
    await execa`unknown 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

await execa`npm run build`;
await execa`npm run test`;

execa verbose output

Custom logging

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];
        logger[level](message, verboseObject);
    },
});

await execa`npm run build`;
await execa`npm run test`;

Related

Maintainers

Extension points exported contracts — how you extend this code

ProcessEnv (Interface)
(no doc)
test-d/arguments/env.test-d.ts

Core symbols most depended-on inside this repo

setFixtureDirectory
called by 144
test/helpers/fixtures-directory.js
sendMessage
called by 120
lib/ipc/send.js
getStdio
called by 110
test/helpers/stdio.js
nestedSubprocess
called by 80
test/helpers/nested.js
getOneMessage
called by 63
lib/ipc/get-one.js
assertSubprocessOutput
called by 56
test/helpers/convert.js
noopWritable
called by 56
test/helpers/stream.js
noopDuplex
called by 56
test/helpers/stream.js

Shape

Function 1,241
Class 18
Method 2
Interface 1

Languages

TypeScript100%

Modules by API surface

test/helpers/verbose.js26 symbols
test/methods/node.js24 symbols
lib/stdio/type.js24 symbols
test/stdio/duplicate.js21 symbols
test/io/max-buffer.js17 symbols
test/helpers/convert.js17 symbols
lib/ipc/validation.js17 symbols
lib/stdio/handle.js13 symbols
test/stdio/node-stream-native.js11 symbols
test/stdio/node-stream-custom.js11 symbols
test/resolve/stdio.js11 symbols
test/resolve/no-buffer.js11 symbols

Dependencies from manifests, versioned

@sindresorhus/merge-streams4.0.0 · 1×
@types/node22.15.21 · 1×
ava6.3.0 · 1×
c810.1.3 · 1×
cross-spawn7.0.6 · 1×
figures6.1.0 · 1×
get-node15.0.3 · 1×
get-stream9.0.0 · 1×
human-signals8.0.1 · 1×
is-in-ci1.0.0 · 1×
is-plain-obj4.1.0 · 1×
is-running2.1.0 · 1×

For agents

$ claude mcp add execa \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact