Convert Figma logo to code with AI

sindresorhus logoexeca

Process execution for humans

6,738
215
6,738
15

Top Related Projects

106,466

Node.js JavaScript runtime ✨🐢🚀✨

14,265

:shell: Portable Unix shell commands for Node.js

Node.js: extra methods for the fs object like copy(), remove(), mkdirs()

glob functionality for node.js

node.js command-line interfaces made easy

Quick Overview

Execa is a Node.js library that provides a better, more robust alternative to the built-in child_process methods. It offers a promise-based API for executing external commands, with improved error handling, streaming, and cross-platform support.

Pros

  • Simplified API with promise support and easier error handling
  • Cross-platform compatibility, including better Windows support
  • Built-in streaming capabilities for stdout and stderr
  • Automatic escaping of command arguments for improved security

Cons

  • Adds an external dependency to your project
  • May have a slight performance overhead compared to native child_process methods
  • Learning curve for developers used to the built-in Node.js methods
  • Limited to Node.js environments (not usable in browsers)

Code Examples

  1. Basic usage:
import execa from 'execa';

const {stdout} = await execa('echo', ['Hello world']);
console.log(stdout);
// Output: Hello world
  1. Handling errors:
import execa from 'execa';

try {
    await execa('unknown-command');
} catch (error) {
    console.log(error);
    // Error: Command failed with ENOENT: unknown-command
}
  1. Streaming output:
import execa from 'execa';

const stream = execa('echo', ['Hello', 'world']).stdout;
stream.pipe(process.stdout);
// Output: Hello world
  1. Shell commands:
import execa from 'execa';

const {stdout} = await execa('echo $HOME', {shell: true});
console.log(stdout);
// Output: /home/username

Getting Started

To use Execa in your project, follow these steps:

  1. Install the package:
npm install execa
  1. Import and use in your code:
import execa from 'execa';

async function runCommand() {
    try {
        const {stdout} = await execa('ls', ['-l']);
        console.log('Command output:', stdout);
    } catch (error) {
        console.error('Error:', error);
    }
}

runCommand();

This example demonstrates how to execute a simple ls -l command using Execa and handle its output or potential errors.

Competitor Comparisons

106,466

Node.js JavaScript runtime ✨🐢🚀✨

Pros of Node

  • Comprehensive JavaScript runtime environment with built-in modules
  • Extensive ecosystem and community support
  • Native performance optimizations and low-level system access

Cons of Node

  • Larger codebase and more complex setup for simple tasks
  • Steeper learning curve for beginners
  • Less focused on cross-platform child process management

Code Comparison

Node:

const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

Execa:

const execa = require('execa');

(async () => {
  const {stdout} = await execa('ls', ['-lh', '/usr']);
  console.log(stdout);
})();

Key Differences

  • Execa provides a simpler, promise-based API for child process management
  • Node offers more granular control over process execution and I/O
  • Execa includes built-in features like improved Windows support and automatic escaping
  • Node provides a broader set of tools and APIs for various system-level operations

Use Cases

  • Choose Node for building full-scale applications or when low-level control is needed
  • Opt for Execa when focusing on cross-platform child process execution with a clean API
14,265

:shell: Portable Unix shell commands for Node.js

Pros of ShellJS

  • Cross-platform compatibility: Provides a unified interface for shell commands across different operating systems
  • Synchronous API: Offers a more straightforward approach for sequential operations
  • Built-in command set: Includes a wide range of pre-implemented shell commands

Cons of ShellJS

  • Performance: Generally slower than native shell commands or Execa
  • Limited async support: Primarily focuses on synchronous operations
  • Larger package size: Includes more dependencies and a larger codebase

Code Comparison

ShellJS:

const shell = require('shelljs');

if (shell.exec('npm run build').code !== 0) {
  shell.echo('Error: Build failed');
  shell.exit(1);
}

Execa:

const execa = require('execa');

try {
  await execa('npm', ['run', 'build']);
} catch (error) {
  console.error('Error: Build failed');
  process.exit(1);
}

Summary

ShellJS provides a more shell-like syntax and cross-platform compatibility, making it easier for developers familiar with shell scripting. However, Execa offers better performance, native Promise support, and a more modern JavaScript approach. The choice between the two depends on specific project requirements and developer preferences.

Node.js: extra methods for the fs object like copy(), remove(), mkdirs()

Pros of fs-extra

  • Provides a comprehensive set of file system operations, including promise-based versions
  • Offers additional utility functions not found in Node.js core fs module
  • Maintains compatibility with the native fs module, allowing for easy integration

Cons of fs-extra

  • Focused solely on file system operations, unlike execa's process execution capabilities
  • May have a larger footprint due to its extensive feature set
  • Lacks the advanced child process management features of execa

Code Comparison

fs-extra:

const fs = require('fs-extra')

async function copyFiles() {
  await fs.copy('/path/to/source', '/path/to/destination')
  console.log('Files copied successfully')
}

execa:

const execa = require('execa')

async function runCommand() {
  const {stdout} = await execa('echo', ['Hello, world!'])
  console.log(stdout)
}

Summary

fs-extra is a powerful library for file system operations, extending Node.js's native fs module with additional functionality and promise-based versions of methods. It's ideal for projects requiring extensive file manipulation.

execa, on the other hand, focuses on process execution and child process management, offering features like better Windows support and improved error handling for shell commands.

While both libraries serve different primary purposes, they can be complementary in projects requiring both file system operations and advanced process execution capabilities.

glob functionality for node.js

Pros of glob

  • Specialized for file and directory pattern matching
  • Supports advanced globbing patterns and options
  • Widely used and battle-tested in many projects

Cons of glob

  • Limited to file system operations
  • Not designed for general-purpose command execution
  • May require additional libraries for complex file manipulations

Code Comparison

glob:

const glob = require('glob');

glob('**/*.js', (err, files) => {
  console.log(files);
});

execa:

const execa = require('execa');

(async () => {
  const {stdout} = await execa('ls', ['-l']);
  console.log(stdout);
})();

Key Differences

  • glob focuses on file pattern matching, while execa is for running shell commands
  • glob operates synchronously or with callbacks, execa supports promises and async/await
  • execa provides more control over child processes, including stdio handling and killing processes

Use Cases

  • Use glob for file searching, listing, and pattern matching tasks
  • Choose execa for running shell commands, scripts, or external programs with advanced process control

Community and Maintenance

Both projects are well-maintained and have large user bases. glob has been around longer and is more specialized, while execa is part of a larger ecosystem of utilities by Sindre Sorhus.

node.js command-line interfaces made easy

Pros of Commander.js

  • Specifically designed for building command-line interfaces (CLIs)
  • Provides a rich set of features for parsing arguments and options
  • Offers built-in help generation and version information

Cons of Commander.js

  • Limited to CLI applications, not suitable for general process execution
  • Requires more setup and configuration for basic command execution
  • Heavier dependency compared to Execa for simple use cases

Code Comparison

Commander.js:

const { program } = require('commander');

program
  .option('-d, --debug', 'output extra debugging')
  .option('-s, --small', 'small pizza size')
  .parse(process.argv);

const options = program.opts();
console.log(options);

Execa:

const execa = require('execa');

(async () => {
  const { stdout } = await execa('echo', ['hello world']);
  console.log(stdout);
})();

Summary

Commander.js is tailored for building complex CLI applications with advanced argument parsing and help generation. It excels in creating user-friendly command-line interfaces but may be overkill for simple command execution tasks.

Execa, on the other hand, focuses on streamlined process execution and is more suitable for running external commands or scripts within Node.js applications. It offers a simpler API for basic command execution but lacks the CLI-specific features of Commander.js.

Choose Commander.js for building full-fledged CLI tools, and Execa for general-purpose command execution in Node.js projects.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

execa logo

Coverage Status

Process execution for humans





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.


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!


Features

Install

npm install execa

Documentation

Execution:

Input/output:

Advanced usage:

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

NPM DownloadsLast 30 Days