Comparing version 6.1.0 to 8.0.1
549
index.d.ts
@@ -1,7 +0,8 @@ | ||
import {Buffer} from 'node:buffer'; | ||
import {ChildProcess} from 'node:child_process'; | ||
import {Stream, Readable as ReadableStream} from 'node:stream'; | ||
import {type Buffer} from 'node:buffer'; | ||
import {type ChildProcess} from 'node:child_process'; | ||
import {type Stream, type Readable as ReadableStream, type Writable as WritableStream} from 'node:stream'; | ||
export type StdioOption = | ||
| 'pipe' | ||
| 'overlapped' | ||
| 'ipc' | ||
@@ -14,3 +15,23 @@ | 'ignore' | ||
export interface CommonOptions<EncodingType> { | ||
type EncodingOption = | ||
| 'utf8' | ||
// eslint-disable-next-line unicorn/text-encoding-identifier-case | ||
| 'utf-8' | ||
| 'utf16le' | ||
| 'utf-16le' | ||
| 'ucs2' | ||
| 'ucs-2' | ||
| 'latin1' | ||
| 'binary' | ||
| 'ascii' | ||
| 'hex' | ||
| 'base64' | ||
| 'base64url' | ||
| 'buffer' | ||
| null | ||
| undefined; | ||
type DefaultEncodingOption = 'utf8'; | ||
type BufferEncodingOption = 'buffer' | null; | ||
export type CommonOptions<EncodingType extends EncodingOption = DefaultEncodingOption> = { | ||
/** | ||
@@ -30,3 +51,3 @@ Kill the spawned process when the parent process exits unless either: | ||
@default false | ||
@default `true` with `$`, `false` otherwise | ||
*/ | ||
@@ -38,4 +59,2 @@ readonly preferLocal?: boolean; | ||
Using a `URL` is only supported in Node.js `14.18.0`, `16.14.0` or above. | ||
@default process.cwd() | ||
@@ -70,3 +89,3 @@ */ | ||
@default 'pipe' | ||
@default `inherit` with `$`, `pipe` otherwise | ||
*/ | ||
@@ -120,4 +139,2 @@ readonly stdin?: StdioOption; | ||
Using a `URL` is only supported in Node.js `14.18.0`, `16.14.0` or above. | ||
@default process.cwd() | ||
@@ -144,3 +161,3 @@ */ | ||
*/ | ||
readonly stdio?: 'pipe' | 'ignore' | 'inherit' | readonly StdioOption[]; | ||
readonly stdio?: 'pipe' | 'overlapped' | 'ignore' | 'inherit' | readonly StdioOption[]; | ||
@@ -152,4 +169,2 @@ /** | ||
Requires Node.js `13.2.0` or later. | ||
[More info.](https://nodejs.org/api/child_process.html#child_process_advanced_serialization) | ||
@@ -191,3 +206,3 @@ | ||
/** | ||
Specify the character encoding used to decode the `stdout` and `stderr` output. If set to `null`, then `stdout` and `stderr` will be a `Buffer` instead of a string. | ||
Specify the character encoding used to decode the `stdout` and `stderr` output. If set to `'buffer'` or `null`, then `stdout` and `stderr` will be a `Buffer` instead of a string. | ||
@@ -222,8 +237,6 @@ @default 'utf8' | ||
When `AbortController.abort()` is called, [`.isCanceled`](https://github.com/sindresorhus/execa#iscanceled) becomes `false`. | ||
When `AbortController.abort()` is called, [`.isCanceled`](https://github.com/sindresorhus/execa#iscanceled) becomes `true`. | ||
*Requires Node.js 16 or later.* | ||
@example | ||
```js | ||
``` | ||
import {execa} from 'execa'; | ||
@@ -261,20 +274,47 @@ | ||
readonly windowsHide?: boolean; | ||
} | ||
export interface Options<EncodingType = string> extends CommonOptions<EncodingType> { | ||
/** | ||
Print each command on `stderr` before executing it. | ||
This can also be enabled by setting the `NODE_DEBUG=execa` environment variable in the current process. | ||
@default false | ||
*/ | ||
readonly verbose?: boolean; | ||
}; | ||
export type Options<EncodingType extends EncodingOption = DefaultEncodingOption> = { | ||
/** | ||
Write some input to the `stdin` of your binary. | ||
If the input is a file, use the `inputFile` option instead. | ||
*/ | ||
readonly input?: string | Buffer | ReadableStream; | ||
} | ||
export interface SyncOptions<EncodingType = string> extends CommonOptions<EncodingType> { | ||
/** | ||
Use a file as input to the the `stdin` of your binary. | ||
If the input is not a file, use the `input` option instead. | ||
*/ | ||
readonly inputFile?: string; | ||
} & CommonOptions<EncodingType>; | ||
export type SyncOptions<EncodingType extends EncodingOption = DefaultEncodingOption> = { | ||
/** | ||
Write some input to the `stdin` of your binary. | ||
If the input is a file, use the `inputFile` option instead. | ||
*/ | ||
readonly input?: string | Buffer; | ||
} | ||
export interface NodeOptions<EncodingType = string> extends Options<EncodingType> { | ||
/** | ||
Use a file as input to the the `stdin` of your binary. | ||
If the input is not a file, use the `input` option instead. | ||
*/ | ||
readonly inputFile?: string; | ||
} & CommonOptions<EncodingType>; | ||
export type NodeOptions<EncodingType extends EncodingOption = DefaultEncodingOption> = { | ||
/** | ||
The Node.js executable to use. | ||
@@ -292,5 +332,7 @@ | ||
readonly nodeOptions?: string[]; | ||
} | ||
} & Options<EncodingType>; | ||
export interface ExecaReturnBase<StdoutStderrType> { | ||
type StdoutStderrAll = string | Buffer | undefined; | ||
export type ExecaReturnBase<StdoutStderrType extends StdoutStderrAll> = { | ||
/** | ||
@@ -354,8 +396,12 @@ The file and arguments that were run, for logging purposes. | ||
signalDescription?: string; | ||
} | ||
export interface ExecaSyncReturnValue<StdoutErrorType = string> | ||
extends ExecaReturnBase<StdoutErrorType> { | ||
} | ||
/** | ||
The `cwd` of the command if provided in the command options. Otherwise it is `process.cwd()`. | ||
*/ | ||
cwd: string; | ||
}; | ||
export type ExecaSyncReturnValue<StdoutStderrType extends StdoutStderrAll = string> = { | ||
} & ExecaReturnBase<StdoutStderrType>; | ||
/** | ||
@@ -371,4 +417,3 @@ Result of a child process execution. On success this is a plain object. On failure this is also an `Error` instance. | ||
*/ | ||
export interface ExecaReturnValue<StdoutErrorType = string> | ||
extends ExecaSyncReturnValue<StdoutErrorType> { | ||
export type ExecaReturnValue<StdoutStderrType extends StdoutStderrAll = string> = { | ||
/** | ||
@@ -381,3 +426,3 @@ The output of the process with `stdout` and `stderr` interleaved. | ||
*/ | ||
all?: StdoutErrorType; | ||
all?: StdoutStderrType; | ||
@@ -390,7 +435,5 @@ /** | ||
isCanceled: boolean; | ||
} | ||
} & ExecaSyncReturnValue<StdoutStderrType>; | ||
export interface ExecaSyncError<StdoutErrorType = string> | ||
extends Error, | ||
ExecaReturnBase<StdoutErrorType> { | ||
export type ExecaSyncError<StdoutStderrType extends StdoutStderrAll = string> = { | ||
/** | ||
@@ -414,6 +457,5 @@ Error message when the child process failed to run. In addition to the underlying error message, it also contains some information related to why the child process errored. | ||
originalMessage?: string; | ||
} | ||
} & Error & ExecaReturnBase<StdoutStderrType>; | ||
export interface ExecaError<StdoutErrorType = string> | ||
extends ExecaSyncError<StdoutErrorType> { | ||
export type ExecaError<StdoutStderrType extends StdoutStderrAll = string> = { | ||
/** | ||
@@ -426,3 +468,3 @@ The output of the process with `stdout` and `stderr` interleaved. | ||
*/ | ||
all?: StdoutErrorType; | ||
all?: StdoutStderrType; | ||
@@ -433,5 +475,5 @@ /** | ||
isCanceled: boolean; | ||
} | ||
} & ExecaSyncError<StdoutStderrType>; | ||
export interface KillOptions { | ||
export type KillOptions = { | ||
/** | ||
@@ -445,5 +487,5 @@ Milliseconds to wait for the child process to terminate before sending `SIGKILL`. | ||
forceKillAfterTimeout?: number | false; | ||
} | ||
}; | ||
export interface ExecaChildPromise<StdoutErrorType> { | ||
export type ExecaChildPromise<StdoutStderrType extends StdoutStderrAll> = { | ||
/** | ||
@@ -459,7 +501,7 @@ Stream combining/interleaving [`stdout`](https://nodejs.org/api/child_process.html#child_process_subprocess_stdout) and [`stderr`](https://nodejs.org/api/child_process.html#child_process_subprocess_stderr). | ||
catch<ResultType = never>( | ||
onRejected?: (reason: ExecaError<StdoutErrorType>) => ResultType | PromiseLike<ResultType> | ||
): Promise<ExecaReturnValue<StdoutErrorType> | ResultType>; | ||
onRejected?: (reason: ExecaError<StdoutStderrType>) => ResultType | PromiseLike<ResultType> | ||
): Promise<ExecaReturnValue<StdoutStderrType> | ResultType>; | ||
/** | ||
Same as the original [`child_process#kill()`](https://nodejs.org/api/child_process.html#child_process_subprocess_kill_signal), except if `signal` is `SIGTERM` (the default value) and the child process is not terminated after 5 seconds, force it by sending `SIGKILL`. | ||
Same as the original [`child_process#kill()`](https://nodejs.org/api/child_process.html#child_process_subprocess_kill_signal), except if `signal` is `SIGTERM` (the default value) and the child process is not terminated after 5 seconds, force it by sending `SIGKILL`. Note that this graceful termination does not work on Windows, because Windows [doesn't support signals](https://nodejs.org/api/process.html#process_signal_events) (`SIGKILL` and `SIGTERM` has the same effect of force-killing the process immediately.) If you want to achieve graceful termination on Windows, you have to use other means, such as [`taskkill`](https://github.com/sindresorhus/taskkill). | ||
*/ | ||
@@ -469,21 +511,55 @@ kill(signal?: string, options?: KillOptions): void; | ||
/** | ||
Similar to [`childProcess.kill()`](https://nodejs.org/api/child_process.html#child_process_subprocess_kill_signal). This is preferred when cancelling the child process execution as the error is more descriptive and [`childProcessResult.isCanceled`](#iscanceled) is set to `true`. | ||
Similar to [`childProcess.kill()`](https://nodejs.org/api/child_process.html#child_process_subprocess_kill_signal). This used to be preferred when cancelling the child process execution as the error is more descriptive and [`childProcessResult.isCanceled`](#iscanceled) is set to `true`. But now this is deprecated and you should either use `.kill()` or the `signal` option when creating the child process. | ||
*/ | ||
cancel(): void; | ||
} | ||
export type ExecaChildProcess<StdoutErrorType = string> = ChildProcess & | ||
ExecaChildPromise<StdoutErrorType> & | ||
Promise<ExecaReturnValue<StdoutErrorType>>; | ||
/** | ||
[Pipe](https://nodejs.org/api/stream.html#readablepipedestination-options) the child process's `stdout` to `target`, which can be: | ||
- Another `execa()` return value | ||
- A writable stream | ||
- A file path string | ||
If the `target` is another `execa()` return value, it is returned. Otherwise, the original `execa()` return value is returned. This allows chaining `pipeStdout()` then `await`ing the final result. | ||
The `stdout` option] must be kept as `pipe`, its default value. | ||
*/ | ||
pipeStdout?<Target extends ExecaChildPromise<StdoutStderrAll>>(target: Target): Target; | ||
pipeStdout?(target: WritableStream | string): ExecaChildProcess<StdoutStderrType>; | ||
/** | ||
Like `pipeStdout()` but piping the child process's `stderr` instead. | ||
The `stderr` option must be kept as `pipe`, its default value. | ||
*/ | ||
pipeStderr?<Target extends ExecaChildPromise<StdoutStderrAll>>(target: Target): Target; | ||
pipeStderr?(target: WritableStream | string): ExecaChildProcess<StdoutStderrType>; | ||
/** | ||
Combines both `pipeStdout()` and `pipeStderr()`. | ||
Either the `stdout` option or the `stderr` option must be kept as `pipe`, their default value. Also, the `all` option must be set to `true`. | ||
*/ | ||
pipeAll?<Target extends ExecaChildPromise<StdoutStderrAll>>(target: Target): Target; | ||
pipeAll?(target: WritableStream | string): ExecaChildProcess<StdoutStderrType>; | ||
}; | ||
export type ExecaChildProcess<StdoutStderrType extends StdoutStderrAll = string> = ChildProcess & | ||
ExecaChildPromise<StdoutStderrType> & | ||
Promise<ExecaReturnValue<StdoutStderrType>>; | ||
/** | ||
Execute a file. | ||
Executes a command using `file ...arguments`. `arguments` are specified as an array of strings. Returns a `childProcess`. | ||
Think of this as a mix of `child_process.execFile` and `child_process.spawn`. | ||
Arguments are automatically escaped. They can contain any character, including spaces. | ||
This is the preferred method when executing single commands. | ||
@param file - The program/script to execute. | ||
@param arguments - Arguments to pass to `file` on execution. | ||
@returns A [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess), which is enhanced to also be a `Promise` for a result `Object` with `stdout` and `stderr` properties. | ||
@returns An `ExecaChildProcess` that is both: | ||
- a `Promise` resolving or rejecting with a `childProcessResult`. | ||
- a [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess) with some additional methods and properties. | ||
@throws A `childProcessResult` error | ||
@example | ||
@example <caption>Promise interface</caption> | ||
``` | ||
@@ -495,21 +571,91 @@ import {execa} from 'execa'; | ||
//=> 'unicorns' | ||
``` | ||
// Cancelling a spawned process | ||
@example <caption>Redirect output to a file</caption> | ||
``` | ||
import {execa} from 'execa'; | ||
const subprocess = execa('node'); | ||
// Similar to `echo unicorns > stdout.txt` in Bash | ||
await execa('echo', ['unicorns']).pipeStdout('stdout.txt'); | ||
setTimeout(() => { | ||
subprocess.cancel() | ||
}, 1000); | ||
// Similar to `echo unicorns 2> stdout.txt` in Bash | ||
await execa('echo', ['unicorns']).pipeStderr('stderr.txt'); | ||
// Similar to `echo unicorns &> stdout.txt` in Bash | ||
await execa('echo', ['unicorns'], {all: true}).pipeAll('all.txt'); | ||
``` | ||
@example <caption>Redirect input from a file</caption> | ||
``` | ||
import {execa} from 'execa'; | ||
// Similar to `cat < stdin.txt` in Bash | ||
const {stdout} = await execa('cat', {inputFile: 'stdin.txt'}); | ||
console.log(stdout); | ||
//=> 'unicorns' | ||
``` | ||
@example <caption>Save and pipe output from a child process</caption> | ||
``` | ||
import {execa} from 'execa'; | ||
const {stdout} = await execa('echo', ['unicorns']).pipeStdout(process.stdout); | ||
// Prints `unicorns` | ||
console.log(stdout); | ||
// Also returns 'unicorns' | ||
``` | ||
@example <caption>Pipe multiple processes</caption> | ||
``` | ||
import {execa} from 'execa'; | ||
// Similar to `echo unicorns | cat` in Bash | ||
const {stdout} = await execa('echo', ['unicorns']).pipeStdout(execa('cat')); | ||
console.log(stdout); | ||
//=> 'unicorns' | ||
``` | ||
@example <caption>Handling errors</caption> | ||
``` | ||
import {execa} from 'execa'; | ||
// Catching an error | ||
try { | ||
await subprocess; | ||
await execa('unknown', ['command']); | ||
} catch (error) { | ||
console.log(subprocess.killed); // true | ||
console.log(error.isCanceled); // true | ||
console.log(error); | ||
/* | ||
{ | ||
message: 'Command failed with ENOENT: unknown command spawn unknown ENOENT', | ||
errno: -2, | ||
code: 'ENOENT', | ||
syscall: 'spawn unknown', | ||
path: 'unknown', | ||
spawnargs: ['command'], | ||
originalMessage: 'spawn unknown ENOENT', | ||
shortMessage: 'Command failed with ENOENT: unknown command spawn unknown ENOENT', | ||
command: 'unknown command', | ||
escapedCommand: 'unknown command', | ||
stdout: '', | ||
stderr: '', | ||
failed: true, | ||
timedOut: false, | ||
isCanceled: false, | ||
killed: false, | ||
cwd: '/path/to/cwd' | ||
} | ||
\*\/ | ||
} | ||
``` | ||
// Pipe the child process stdout to the current stdout | ||
execa('echo', ['unicorns']).stdout.pipe(process.stdout); | ||
@example <caption>Graceful termination</caption> | ||
``` | ||
const subprocess = execa('node'); | ||
setTimeout(() => { | ||
subprocess.kill('SIGTERM', { | ||
forceKillAfterTimeout: 2000 | ||
}); | ||
}, 1000); | ||
``` | ||
*/ | ||
@@ -524,15 +670,66 @@ export function execa( | ||
arguments?: readonly string[], | ||
options?: Options<null> | ||
options?: Options<BufferEncodingOption> | ||
): ExecaChildProcess<Buffer>; | ||
export function execa(file: string, options?: Options): ExecaChildProcess; | ||
export function execa(file: string, options?: Options<null>): ExecaChildProcess<Buffer>; | ||
export function execa(file: string, options?: Options<BufferEncodingOption>): ExecaChildProcess<Buffer>; | ||
/** | ||
Execute a file synchronously. | ||
Same as `execa()` but synchronous. | ||
This method throws an `Error` if the command fails. | ||
@param file - The program/script to execute. | ||
@param arguments - Arguments to pass to `file` on execution. | ||
@returns A result `Object` with `stdout` and `stderr` properties. | ||
@returns A `childProcessResult` object | ||
@throws A `childProcessResult` error | ||
@example <caption>Promise interface</caption> | ||
``` | ||
import {execa} from 'execa'; | ||
const {stdout} = execaSync('echo', ['unicorns']); | ||
console.log(stdout); | ||
//=> 'unicorns' | ||
``` | ||
@example <caption>Redirect input from a file</caption> | ||
``` | ||
import {execa} from 'execa'; | ||
// Similar to `cat < stdin.txt` in Bash | ||
const {stdout} = execaSync('cat', {inputFile: 'stdin.txt'}); | ||
console.log(stdout); | ||
//=> 'unicorns' | ||
``` | ||
@example <caption>Handling errors</caption> | ||
``` | ||
import {execa} from 'execa'; | ||
// Catching an error | ||
try { | ||
execaSync('unknown', ['command']); | ||
} catch (error) { | ||
console.log(error); | ||
/* | ||
{ | ||
message: 'Command failed with ENOENT: unknown command spawnSync unknown ENOENT', | ||
errno: -2, | ||
code: 'ENOENT', | ||
syscall: 'spawnSync unknown', | ||
path: 'unknown', | ||
spawnargs: ['command'], | ||
originalMessage: 'spawnSync unknown ENOENT', | ||
shortMessage: 'Command failed with ENOENT: unknown command spawnSync unknown ENOENT', | ||
command: 'unknown command', | ||
escapedCommand: 'unknown command', | ||
stdout: '', | ||
stderr: '', | ||
failed: true, | ||
timedOut: false, | ||
isCanceled: false, | ||
killed: false, | ||
cwd: '/path/to/cwd' | ||
} | ||
\*\/ | ||
} | ||
``` | ||
*/ | ||
@@ -547,3 +744,3 @@ export function execaSync( | ||
arguments?: readonly string[], | ||
options?: SyncOptions<null> | ||
options?: SyncOptions<BufferEncodingOption> | ||
): ExecaSyncReturnValue<Buffer>; | ||
@@ -553,14 +750,17 @@ export function execaSync(file: string, options?: SyncOptions): ExecaSyncReturnValue; | ||
file: string, | ||
options?: SyncOptions<null> | ||
options?: SyncOptions<BufferEncodingOption> | ||
): ExecaSyncReturnValue<Buffer>; | ||
/** | ||
Same as `execa()` except both file and arguments are specified in a single `command` string. For example, `execa('echo', ['unicorns'])` is the same as `execaCommand('echo unicorns')`. | ||
Executes a command. The `command` string includes both the `file` and its `arguments`. Returns a `childProcess`. | ||
If the file or an argument contains spaces, they must be escaped with backslashes. This matters especially if `command` is not a constant but a variable, for example with `__dirname` or `process.cwd()`. Except for spaces, no escaping/quoting is needed. | ||
Arguments are automatically escaped. They can contain any character, but spaces must be escaped with a backslash like `execaCommand('echo has\\ space')`. | ||
The `shell` option must be used if the `command` uses shell-specific features (for example, `&&` or `||`), as opposed to being a simple `file` followed by its `arguments`. | ||
This is the preferred method when executing a user-supplied `command` string, such as in a REPL. | ||
@param command - The program/script to execute and its arguments. | ||
@returns A [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess), which is enhanced to also be a `Promise` for a result `Object` with `stdout` and `stderr` properties. | ||
@returns An `ExecaChildProcess` that is both: | ||
- a `Promise` resolving or rejecting with a `childProcessResult`. | ||
- a [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess) with some additional methods and properties. | ||
@throws A `childProcessResult` error | ||
@@ -577,3 +777,3 @@ @example | ||
export function execaCommand(command: string, options?: Options): ExecaChildProcess; | ||
export function execaCommand(command: string, options?: Options<null>): ExecaChildProcess<Buffer>; | ||
export function execaCommand(command: string, options?: Options<BufferEncodingOption>): ExecaChildProcess<Buffer>; | ||
@@ -584,18 +784,187 @@ /** | ||
@param command - The program/script to execute and its arguments. | ||
@returns A result `Object` with `stdout` and `stderr` properties. | ||
@returns A `childProcessResult` object | ||
@throws A `childProcessResult` error | ||
@example | ||
``` | ||
import {execaCommandSync} from 'execa'; | ||
const {stdout} = execaCommandSync('echo unicorns'); | ||
console.log(stdout); | ||
//=> 'unicorns' | ||
``` | ||
*/ | ||
export function execaCommandSync(command: string, options?: SyncOptions): ExecaSyncReturnValue; | ||
export function execaCommandSync(command: string, options?: SyncOptions<null>): ExecaSyncReturnValue<Buffer>; | ||
export function execaCommandSync(command: string, options?: SyncOptions<BufferEncodingOption>): ExecaSyncReturnValue<Buffer>; | ||
type TemplateExpression = | ||
| string | ||
| number | ||
| ExecaReturnValue<string | Buffer> | ||
| ExecaSyncReturnValue<string | Buffer> | ||
| Array<string | number | ExecaReturnValue<string | Buffer> | ExecaSyncReturnValue<string | Buffer>>; | ||
type Execa$<StdoutStderrType extends StdoutStderrAll = string> = { | ||
/** | ||
Returns a new instance of `$` but with different default `options`. Consecutive calls are merged to previous ones. | ||
This can be used to either: | ||
- Set options for a specific command: `` $(options)`command` `` | ||
- Share options for multiple commands: `` const $$ = $(options); $$`command`; $$`otherCommand` `` | ||
@param options - Options to set | ||
@returns A new instance of `$` with those `options` set | ||
@example | ||
``` | ||
import {$} from 'execa'; | ||
const $$ = $({stdio: 'inherit'}); | ||
await $$`echo unicorns`; | ||
//=> 'unicorns' | ||
await $$`echo rainbows`; | ||
//=> 'rainbows' | ||
``` | ||
*/ | ||
(options: Options<undefined>): Execa$<StdoutStderrType>; | ||
(options: Options): Execa$; | ||
(options: Options<BufferEncodingOption>): Execa$<Buffer>; | ||
( | ||
templates: TemplateStringsArray, | ||
...expressions: TemplateExpression[] | ||
): ExecaChildProcess<StdoutStderrType>; | ||
/** | ||
Same as $\`command\` but synchronous. | ||
@returns A `childProcessResult` object | ||
@throws A `childProcessResult` error | ||
@example <caption>Basic</caption> | ||
``` | ||
import {$} from 'execa'; | ||
const branch = $.sync`git branch --show-current`; | ||
$.sync`dep deploy --branch=${branch}`; | ||
``` | ||
@example <caption>Multiple arguments</caption> | ||
``` | ||
import {$} from 'execa'; | ||
const args = ['unicorns', '&', 'rainbows!']; | ||
const {stdout} = $.sync`echo ${args}`; | ||
console.log(stdout); | ||
//=> 'unicorns & rainbows!' | ||
``` | ||
@example <caption>With options</caption> | ||
``` | ||
import {$} from 'execa'; | ||
$.sync({stdio: 'inherit'})`echo unicorns`; | ||
//=> 'unicorns' | ||
``` | ||
@example <caption>Shared options</caption> | ||
``` | ||
import {$} from 'execa'; | ||
const $$ = $({stdio: 'inherit'}); | ||
$$.sync`echo unicorns`; | ||
//=> 'unicorns' | ||
$$.sync`echo rainbows`; | ||
//=> 'rainbows' | ||
``` | ||
*/ | ||
sync( | ||
templates: TemplateStringsArray, | ||
...expressions: TemplateExpression[] | ||
): ExecaSyncReturnValue<StdoutStderrType>; | ||
}; | ||
/** | ||
Executes a command. The `command` string includes both the `file` and its `arguments`. Returns a `childProcess`. | ||
Arguments are automatically escaped. They can contain any character, but spaces must use `${}` like `` $`echo ${'has space'}` ``. | ||
This is the preferred method when executing multiple commands in a script file. | ||
The `command` string can inject any `${value}` with the following types: string, number, `childProcess` or an array of those types. For example: `` $`echo one ${'two'} ${3} ${['four', 'five']}` ``. For `${childProcess}`, the process's `stdout` is used. | ||
@returns An `ExecaChildProcess` that is both: | ||
- a `Promise` resolving or rejecting with a `childProcessResult`. | ||
- a [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess) with some additional methods and properties. | ||
@throws A `childProcessResult` error | ||
@example <caption>Basic</caption> | ||
``` | ||
import {$} from 'execa'; | ||
const branch = await $`git branch --show-current`; | ||
await $`dep deploy --branch=${branch}`; | ||
``` | ||
@example <caption>Multiple arguments</caption> | ||
``` | ||
import {$} from 'execa'; | ||
const args = ['unicorns', '&', 'rainbows!']; | ||
const {stdout} = await $`echo ${args}`; | ||
console.log(stdout); | ||
//=> 'unicorns & rainbows!' | ||
``` | ||
@example <caption>With options</caption> | ||
``` | ||
import {$} from 'execa'; | ||
await $({stdio: 'inherit'})`echo unicorns`; | ||
//=> 'unicorns' | ||
``` | ||
@example <caption>Shared options</caption> | ||
``` | ||
import {$} from 'execa'; | ||
const $$ = $({stdio: 'inherit'}); | ||
await $$`echo unicorns`; | ||
//=> 'unicorns' | ||
await $$`echo rainbows`; | ||
//=> 'rainbows' | ||
``` | ||
*/ | ||
export const $: Execa$; | ||
/** | ||
Execute a Node.js script as a child process. | ||
Same as `execa('node', [scriptPath, ...arguments], options)` except (like [`child_process#fork()`](https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options)): | ||
- the current Node version and options are used. This can be overridden using the `nodePath` and `nodeArguments` options. | ||
- the `shell` option cannot be used | ||
- an extra channel [`ipc`](https://nodejs.org/api/child_process.html#child_process_options_stdio) is passed to [`stdio`](#stdio) | ||
Arguments are automatically escaped. They can contain any character, including spaces. | ||
This is the preferred method when executing Node.js files. | ||
Like [`child_process#fork()`](https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options): | ||
- the current Node version and options are used. This can be overridden using the `nodePath` and `nodeOptions` options. | ||
- the `shell` option cannot be used | ||
- an extra channel [`ipc`](https://nodejs.org/api/child_process.html#child_process_options_stdio) is passed to `stdio` | ||
@param scriptPath - Node.js script to execute. | ||
@param arguments - Arguments to pass to `scriptPath` on execution. | ||
@returns A [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess), which is enhanced to also be a `Promise` for a result `Object` with `stdout` and `stderr` properties. | ||
@returns An `ExecaChildProcess` that is both: | ||
- a `Promise` resolving or rejecting with a `childProcessResult`. | ||
- a [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess) with some additional methods and properties. | ||
@throws A `childProcessResult` error | ||
@example | ||
``` | ||
import {execa} from 'execa'; | ||
await execaNode('scriptPath', ['argument']); | ||
``` | ||
*/ | ||
@@ -610,5 +979,5 @@ export function execaNode( | ||
arguments?: readonly string[], | ||
options?: Options<null> | ||
options?: NodeOptions<BufferEncodingOption> | ||
): ExecaChildProcess<Buffer>; | ||
export function execaNode(scriptPath: string, options?: Options): ExecaChildProcess; | ||
export function execaNode(scriptPath: string, options?: Options<null>): ExecaChildProcess<Buffer>; | ||
export function execaNode(scriptPath: string, options?: NodeOptions): ExecaChildProcess; | ||
export function execaNode(scriptPath: string, options?: NodeOptions<BufferEncodingOption>): ExecaChildProcess<Buffer>; |
56
index.js
@@ -12,5 +12,7 @@ import {Buffer} from 'node:buffer'; | ||
import {spawnedKill, spawnedCancel, setupTimeout, validateTimeout, setExitHandler} from './lib/kill.js'; | ||
import {handleInput, getSpawnedResult, makeAllStream, validateInputSync} from './lib/stream.js'; | ||
import {addPipeMethods} from './lib/pipe.js'; | ||
import {handleInput, getSpawnedResult, makeAllStream, handleInputSync} from './lib/stream.js'; | ||
import {mergePromise, getSpawnedPromise} from './lib/promise.js'; | ||
import {joinCommand, parseCommand, getEscapedCommand} from './lib/command.js'; | ||
import {joinCommand, parseCommand, parseTemplates, getEscapedCommand} from './lib/command.js'; | ||
import {logCommand, verboseDefault} from './lib/verbose.js'; | ||
@@ -48,2 +50,3 @@ const DEFAULT_MAX_BUFFER = 1000 * 1000 * 100; | ||
windowsHide: true, | ||
verbose: verboseDefault, | ||
...options, | ||
@@ -81,2 +84,3 @@ }; | ||
const escapedCommand = getEscapedCommand(file, args); | ||
logCommand(escapedCommand, parsed.options); | ||
@@ -103,3 +107,4 @@ validateTimeout(parsed.options); | ||
})); | ||
return mergePromise(dummySpawned, errorPromise); | ||
mergePromise(dummySpawned, errorPromise); | ||
return dummySpawned; | ||
} | ||
@@ -161,7 +166,9 @@ | ||
handleInput(spawned, parsed.options.input); | ||
handleInput(spawned, parsed.options); | ||
spawned.all = makeAllStream(spawned, parsed.options); | ||
return mergePromise(spawned, handlePromiseOnce); | ||
addPipeMethods(spawned); | ||
mergePromise(spawned, handlePromiseOnce); | ||
return spawned; | ||
} | ||
@@ -173,8 +180,9 @@ | ||
const escapedCommand = getEscapedCommand(file, args); | ||
logCommand(escapedCommand, parsed.options); | ||
validateInputSync(parsed.options); | ||
const input = handleInputSync(parsed.options); | ||
let result; | ||
try { | ||
result = childProcess.spawnSync(parsed.file, parsed.args, parsed.options); | ||
result = childProcess.spawnSync(parsed.file, parsed.args, {...parsed.options, input}); | ||
} catch (error) { | ||
@@ -233,2 +241,36 @@ throw makeError({ | ||
const normalizeScriptStdin = ({input, inputFile, stdio}) => input === undefined && inputFile === undefined && stdio === undefined | ||
? {stdin: 'inherit'} | ||
: {}; | ||
const normalizeScriptOptions = (options = {}) => ({ | ||
preferLocal: true, | ||
...normalizeScriptStdin(options), | ||
...options, | ||
}); | ||
function create$(options) { | ||
function $(templatesOrOptions, ...expressions) { | ||
if (!Array.isArray(templatesOrOptions)) { | ||
return create$({...options, ...templatesOrOptions}); | ||
} | ||
const [file, ...args] = parseTemplates(templatesOrOptions, expressions); | ||
return execa(file, args, normalizeScriptOptions(options)); | ||
} | ||
$.sync = (templates, ...expressions) => { | ||
if (!Array.isArray(templates)) { | ||
throw new TypeError('Please use $(options).sync`command` instead of $.sync(options)`command`.'); | ||
} | ||
const [file, ...args] = parseTemplates(templates, expressions); | ||
return execaSync(file, args, normalizeScriptOptions(options)); | ||
}; | ||
return $; | ||
} | ||
export const $ = create$(); | ||
export function execaCommand(command, options) { | ||
@@ -235,0 +277,0 @@ const [file, ...args] = parseCommand(command); |
@@ -0,1 +1,4 @@ | ||
import {Buffer} from 'node:buffer'; | ||
import {ChildProcess} from 'node:child_process'; | ||
const normalizeArgs = (file, args = []) => { | ||
@@ -10,3 +13,2 @@ if (!Array.isArray(args)) { | ||
const NO_ESCAPE_REGEXP = /^[\w.-]+$/; | ||
const DOUBLE_QUOTES_REGEXP = /"/g; | ||
@@ -18,3 +20,3 @@ const escapeArg = arg => { | ||
return `"${arg.replace(DOUBLE_QUOTES_REGEXP, '\\"')}"`; | ||
return `"${arg.replaceAll('"', '\\"')}"`; | ||
}; | ||
@@ -33,3 +35,3 @@ | ||
// Allow spaces to be escaped by a backslash if not meant as a delimiter | ||
const previousToken = tokens[tokens.length - 1]; | ||
const previousToken = tokens.at(-1); | ||
if (previousToken && previousToken.endsWith('\\')) { | ||
@@ -45,1 +47,77 @@ // Merge previous token with current one | ||
}; | ||
const parseExpression = expression => { | ||
const typeOfExpression = typeof expression; | ||
if (typeOfExpression === 'string') { | ||
return expression; | ||
} | ||
if (typeOfExpression === 'number') { | ||
return String(expression); | ||
} | ||
if ( | ||
typeOfExpression === 'object' | ||
&& expression !== null | ||
&& !(expression instanceof ChildProcess) | ||
&& 'stdout' in expression | ||
) { | ||
const typeOfStdout = typeof expression.stdout; | ||
if (typeOfStdout === 'string') { | ||
return expression.stdout; | ||
} | ||
if (Buffer.isBuffer(expression.stdout)) { | ||
return expression.stdout.toString(); | ||
} | ||
throw new TypeError(`Unexpected "${typeOfStdout}" stdout in template expression`); | ||
} | ||
throw new TypeError(`Unexpected "${typeOfExpression}" in template expression`); | ||
}; | ||
const concatTokens = (tokens, nextTokens, isNew) => isNew || tokens.length === 0 || nextTokens.length === 0 | ||
? [...tokens, ...nextTokens] | ||
: [ | ||
...tokens.slice(0, -1), | ||
`${tokens.at(-1)}${nextTokens[0]}`, | ||
...nextTokens.slice(1), | ||
]; | ||
const parseTemplate = ({templates, expressions, tokens, index, template}) => { | ||
const templateString = template ?? templates.raw[index]; | ||
const templateTokens = templateString.split(SPACES_REGEXP).filter(Boolean); | ||
const newTokens = concatTokens( | ||
tokens, | ||
templateTokens, | ||
templateString.startsWith(' '), | ||
); | ||
if (index === expressions.length) { | ||
return newTokens; | ||
} | ||
const expression = expressions[index]; | ||
const expressionTokens = Array.isArray(expression) | ||
? expression.map(expression => parseExpression(expression)) | ||
: [parseExpression(expression)]; | ||
return concatTokens( | ||
newTokens, | ||
expressionTokens, | ||
templateString.endsWith(' '), | ||
); | ||
}; | ||
export const parseTemplates = (templates, expressions) => { | ||
let tokens = []; | ||
for (const [index, template] of templates.entries()) { | ||
tokens = parseTemplate({templates, expressions, tokens, index, template}); | ||
} | ||
return tokens; | ||
}; | ||
@@ -0,1 +1,2 @@ | ||
import process from 'node:process'; | ||
import {signalsByName} from 'human-signals'; | ||
@@ -39,3 +40,3 @@ | ||
killed, | ||
parsed: {options: {timeout}}, | ||
parsed: {options: {timeout, cwd = process.cwd()}}, | ||
}) => { | ||
@@ -71,2 +72,3 @@ // `signal` and `exitCode` emitted on `spawned.on('exit')` event can be `null`. | ||
error.stderr = stderr; | ||
error.cwd = cwd; | ||
@@ -73,0 +75,0 @@ if (all !== undefined) { |
import os from 'node:os'; | ||
import onExit from 'signal-exit'; | ||
import {onExit} from 'signal-exit'; | ||
@@ -4,0 +4,0 @@ const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5; |
@@ -0,2 +1,4 @@ | ||
// eslint-disable-next-line unicorn/prefer-top-level-await | ||
const nativePromisePrototype = (async () => {})().constructor.prototype; | ||
const descriptors = ['then', 'catch', 'finally'].map(property => [ | ||
@@ -17,4 +19,2 @@ property, | ||
} | ||
return spawned; | ||
}; | ||
@@ -21,0 +21,0 @@ |
@@ -0,10 +1,47 @@ | ||
import {createReadStream, readFileSync} from 'node:fs'; | ||
import {setTimeout} from 'node:timers/promises'; | ||
import {isStream} from 'is-stream'; | ||
import getStream from 'get-stream'; | ||
import getStream, {getStreamAsBuffer} from 'get-stream'; | ||
import mergeStream from 'merge-stream'; | ||
// `input` option | ||
export const handleInput = (spawned, input) => { | ||
// Checking for stdin is workaround for https://github.com/nodejs/node/issues/26852 | ||
// @todo remove `|| spawned.stdin === undefined` once we drop support for Node.js <=12.2.0 | ||
if (input === undefined || spawned.stdin === undefined) { | ||
const validateInputOptions = input => { | ||
if (input !== undefined) { | ||
throw new TypeError('The `input` and `inputFile` options cannot be both set.'); | ||
} | ||
}; | ||
const getInputSync = ({input, inputFile}) => { | ||
if (typeof inputFile !== 'string') { | ||
return input; | ||
} | ||
validateInputOptions(input); | ||
return readFileSync(inputFile); | ||
}; | ||
// `input` and `inputFile` option in sync mode | ||
export const handleInputSync = options => { | ||
const input = getInputSync(options); | ||
if (isStream(input)) { | ||
throw new TypeError('The `input` option cannot be a stream in sync mode'); | ||
} | ||
return input; | ||
}; | ||
const getInput = ({input, inputFile}) => { | ||
if (typeof inputFile !== 'string') { | ||
return input; | ||
} | ||
validateInputOptions(input); | ||
return createReadStream(inputFile); | ||
}; | ||
// `input` and `inputFile` option in async mode | ||
export const handleInput = (spawned, options) => { | ||
const input = getInput(options); | ||
if (input === undefined) { | ||
return; | ||
@@ -41,6 +78,10 @@ } | ||
const getBufferedData = async (stream, streamPromise) => { | ||
if (!stream) { | ||
// When `buffer` is `false`, `streamPromise` is `undefined` and there is no buffered data to retrieve | ||
if (!stream || streamPromise === undefined) { | ||
return; | ||
} | ||
// Wait for the `all` stream to receive the last chunk before destroying the stream | ||
await setTimeout(0); | ||
stream.destroy(); | ||
@@ -60,9 +101,19 @@ | ||
if (encoding) { | ||
return getStream(stream, {encoding, maxBuffer}); | ||
// eslint-disable-next-line unicorn/text-encoding-identifier-case | ||
if (encoding === 'utf8' || encoding === 'utf-8') { | ||
return getStream(stream, {maxBuffer}); | ||
} | ||
return getStream.buffer(stream, {maxBuffer}); | ||
if (encoding === null || encoding === 'buffer') { | ||
return getStreamAsBuffer(stream, {maxBuffer}); | ||
} | ||
return applyEncoding(stream, maxBuffer, encoding); | ||
}; | ||
const applyEncoding = async (stream, maxBuffer, encoding) => { | ||
const buffer = await getStreamAsBuffer(stream, {maxBuffer}); | ||
return buffer.toString(encoding); | ||
}; | ||
// Retrieve result of child process: exit code, signal, error, streams (stdout/stderr/all) | ||
@@ -85,7 +136,1 @@ export const getSpawnedResult = async ({stdout, stderr, all}, {encoding, buffer, maxBuffer}, processDone) => { | ||
}; | ||
export const validateInputSync = ({input}) => { | ||
if (isStream(input)) { | ||
throw new TypeError('The `input` option cannot be a stream in sync mode'); | ||
} | ||
}; |
{ | ||
"name": "execa", | ||
"version": "6.1.0", | ||
"version": "8.0.1", | ||
"description": "Process execution for humans", | ||
@@ -14,5 +14,8 @@ "license": "MIT", | ||
"type": "module", | ||
"exports": "./index.js", | ||
"exports": { | ||
"types": "./index.d.ts", | ||
"default": "./index.js" | ||
}, | ||
"engines": { | ||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0" | ||
"node": ">=16.17" | ||
}, | ||
@@ -42,8 +45,9 @@ "scripts": { | ||
"path", | ||
"local" | ||
"local", | ||
"zx" | ||
], | ||
"dependencies": { | ||
"cross-spawn": "^7.0.3", | ||
"get-stream": "^6.0.1", | ||
"human-signals": "^3.0.1", | ||
"get-stream": "^8.0.1", | ||
"human-signals": "^5.0.0", | ||
"is-stream": "^3.0.0", | ||
@@ -53,16 +57,16 @@ "merge-stream": "^2.0.0", | ||
"onetime": "^6.0.0", | ||
"signal-exit": "^3.0.7", | ||
"signal-exit": "^4.1.0", | ||
"strip-final-newline": "^3.0.0" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^17.0.17", | ||
"ava": "^4.0.1", | ||
"c8": "^7.11.0", | ||
"get-node": "^12.0.0", | ||
"@types/node": "^20.4.0", | ||
"ava": "^5.2.0", | ||
"c8": "^8.0.1", | ||
"get-node": "^14.2.0", | ||
"is-running": "^2.1.0", | ||
"p-event": "^5.0.1", | ||
"semver": "^7.3.5", | ||
"tempfile": "^4.0.0", | ||
"tsd": "^0.19.1", | ||
"xo": "^0.48.0" | ||
"p-event": "^6.0.0", | ||
"path-key": "^4.0.0", | ||
"tempfile": "^5.0.0", | ||
"tsd": "^0.28.1", | ||
"xo": "^0.55.0" | ||
}, | ||
@@ -80,2 +84,5 @@ "c8": { | ||
}, | ||
"ava": { | ||
"workerThreads": false | ||
}, | ||
"xo": { | ||
@@ -82,0 +89,0 @@ "rules": { |
407
readme.md
@@ -1,2 +0,5 @@ | ||
<img src="media/logo.svg" width="400"> | ||
<picture> | ||
<source media="(prefers-color-scheme: dark)" srcset="media/logo_dark.svg"> | ||
<img alt="execa logo" src="media/logo.svg" width="400"> | ||
</picture> | ||
<br> | ||
@@ -8,2 +11,32 @@ | ||
<br> | ||
--- | ||
<div align="center"> | ||
<p> | ||
<p> | ||
<sup> | ||
<a href="https://github.com/sponsors/sindresorhus">Sindre's open source work is supported by the community</a> | ||
</sup> | ||
</p> | ||
<sup>Special thanks to:</sup> | ||
<br> | ||
<br> | ||
<a href="https://transloadit.com?utm_source=sindresorhus&utm_medium=referral&utm_campaign=sponsorship&utm_content=execa"> | ||
<picture> | ||
<source width="360" media="(prefers-color-scheme: dark)" srcset="https://sindresorhus.com/assets/thanks/transloadit-logo-dark.svg"> | ||
<source width="360" media="(prefers-color-scheme: light)" srcset="https://sindresorhus.com/assets/thanks/transloadit-logo.svg"> | ||
<img width="360" src="https://sindresorhus.com/assets/thanks/transloadit-logo.svg" alt="Transloadit logo"> | ||
</picture> | ||
</a> | ||
<br> | ||
<br> | ||
</p> | ||
</div> | ||
--- | ||
<br> | ||
## Why | ||
@@ -13,12 +46,15 @@ | ||
- Promise interface. | ||
- [Promise interface](#execacommandcommand-options). | ||
- [Scripts interface](#scripts-interface), like `zx`. | ||
- Improved [Windows support](https://github.com/IndigoUnited/node-cross-spawn#why), including [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) binaries. | ||
- Executes [locally installed binaries](#preferlocal) without `npx`. | ||
- [Cleans up](#cleanup) child processes when the parent process ends. | ||
- [Graceful termination](#optionsforcekillaftertimeout). | ||
- Get [interleaved output](#all) from `stdout` and `stderr` similar to what is printed on the terminal. | ||
- [Strips the final newline](#stripfinalnewline) from the output so you don't have to do `stdout.trim()`. | ||
- Supports [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) binaries cross-platform. | ||
- [Improved Windows support.](https://github.com/IndigoUnited/node-cross-spawn#why) | ||
- Higher max buffer. 100 MB instead of 200 KB. | ||
- [Executes locally installed binaries by name.](#preferlocal) | ||
- [Cleans up spawned processes when the parent process dies.](#cleanup) | ||
- [Get interleaved output](#all) from `stdout` and `stderr` similar to what is printed on the terminal. [*(Async only)*](#execasyncfile-arguments-options) | ||
- [Can specify file and arguments as a single string without a shell](#execacommandcommand-options) | ||
- Convenience methods to pipe processes' [input](#input) and [output](#redirect-output-to-a-file). | ||
- Can specify file and arguments [as a single string](#execacommandcommand-options) without a shell. | ||
- [Verbose mode](#verbose-mode) for debugging. | ||
- More descriptive errors. | ||
- Higher max buffer: 100 MB instead of 1 MB. | ||
@@ -33,2 +69,4 @@ ## Install | ||
### Promise interface | ||
```js | ||
@@ -42,11 +80,81 @@ import {execa} from 'execa'; | ||
### Pipe the child process stdout to the parent | ||
### Scripts interface | ||
For more information about Execa scripts, please see [this page](docs/scripts.md). | ||
#### Basic | ||
```js | ||
import {$} from 'execa'; | ||
const branch = await $`git branch --show-current`; | ||
await $`dep deploy --branch=${branch}`; | ||
``` | ||
#### Multiple arguments | ||
```js | ||
import {$} from 'execa'; | ||
const args = ['unicorns', '&', 'rainbows!']; | ||
const {stdout} = await $`echo ${args}`; | ||
console.log(stdout); | ||
//=> 'unicorns & rainbows!' | ||
``` | ||
#### With options | ||
```js | ||
import {$} from 'execa'; | ||
await $({stdio: 'inherit'})`echo unicorns`; | ||
//=> 'unicorns' | ||
``` | ||
#### Shared options | ||
```js | ||
import {$} from 'execa'; | ||
const $$ = $({stdio: 'inherit'}); | ||
await $$`echo unicorns`; | ||
//=> 'unicorns' | ||
await $$`echo rainbows`; | ||
//=> 'rainbows' | ||
``` | ||
#### Verbose mode | ||
```sh | ||
> node file.js | ||
unicorns | ||
rainbows | ||
> NODE_DEBUG=execa node file.js | ||
[16:50:03.305] echo unicorns | ||
unicorns | ||
[16:50:03.308] echo rainbows | ||
rainbows | ||
``` | ||
### Input/output | ||
#### Redirect output to a file | ||
```js | ||
import {execa} from 'execa'; | ||
execa('echo', ['unicorns']).stdout.pipe(process.stdout); | ||
// Similar to `echo unicorns > stdout.txt` in Bash | ||
await execa('echo', ['unicorns']).pipeStdout('stdout.txt'); | ||
// Similar to `echo unicorns 2> stdout.txt` in Bash | ||
await execa('echo', ['unicorns']).pipeStderr('stderr.txt'); | ||
// Similar to `echo unicorns &> stdout.txt` in Bash | ||
await execa('echo', ['unicorns'], {all: true}).pipeAll('all.txt'); | ||
``` | ||
### Handling Errors | ||
#### Redirect input from a file | ||
@@ -56,32 +164,9 @@ ```js | ||
// Catching an error | ||
try { | ||
await execa('unknown', ['command']); | ||
} catch (error) { | ||
console.log(error); | ||
/* | ||
{ | ||
message: 'Command failed with ENOENT: unknown command spawn unknown ENOENT', | ||
errno: -2, | ||
code: 'ENOENT', | ||
syscall: 'spawn unknown', | ||
path: 'unknown', | ||
spawnargs: ['command'], | ||
originalMessage: 'spawn unknown ENOENT', | ||
shortMessage: 'Command failed with ENOENT: unknown command spawn unknown ENOENT', | ||
command: 'unknown command', | ||
escapedCommand: 'unknown command', | ||
stdout: '', | ||
stderr: '', | ||
all: '', | ||
failed: true, | ||
timedOut: false, | ||
isCanceled: false, | ||
killed: false | ||
} | ||
*/ | ||
} | ||
// Similar to `cat < stdin.txt` in Bash | ||
const {stdout} = await execa('cat', {inputFile: 'stdin.txt'}); | ||
console.log(stdout); | ||
//=> 'unicorns' | ||
``` | ||
### Cancelling a spawned process | ||
#### Save and pipe output from a child process | ||
@@ -91,24 +176,27 @@ ```js | ||
const abortController = new AbortController(); | ||
const subprocess = execa('node', [], {signal: abortController.signal}); | ||
const {stdout} = await execa('echo', ['unicorns']).pipeStdout(process.stdout); | ||
// Prints `unicorns` | ||
console.log(stdout); | ||
// Also returns 'unicorns' | ||
``` | ||
setTimeout(() => { | ||
abortController.abort(); | ||
}, 1000); | ||
#### Pipe multiple processes | ||
try { | ||
await subprocess; | ||
} catch (error) { | ||
console.log(subprocess.killed); // true | ||
console.log(error.isCanceled); // true | ||
} | ||
```js | ||
import {execa} from 'execa'; | ||
// Similar to `echo unicorns | cat` in Bash | ||
const {stdout} = await execa('echo', ['unicorns']).pipeStdout(execa('cat')); | ||
console.log(stdout); | ||
//=> 'unicorns' | ||
``` | ||
### Catching an error with the sync method | ||
### Handling Errors | ||
```js | ||
import {execaSync} from 'execa'; | ||
import {execa} from 'execa'; | ||
// Catching an error | ||
try { | ||
execaSync('unknown', ['command']); | ||
await execa('unknown', ['command']); | ||
} catch (error) { | ||
@@ -118,10 +206,10 @@ console.log(error); | ||
{ | ||
message: 'Command failed with ENOENT: unknown command spawnSync unknown ENOENT', | ||
message: 'Command failed with ENOENT: unknown command spawn unknown ENOENT', | ||
errno: -2, | ||
code: 'ENOENT', | ||
syscall: 'spawnSync unknown', | ||
syscall: 'spawn unknown', | ||
path: 'unknown', | ||
spawnargs: ['command'], | ||
originalMessage: 'spawnSync unknown ENOENT', | ||
shortMessage: 'Command failed with ENOENT: unknown command spawnSync unknown ENOENT', | ||
originalMessage: 'spawn unknown ENOENT', | ||
shortMessage: 'Command failed with ENOENT: unknown command spawn unknown ENOENT', | ||
command: 'unknown command', | ||
@@ -131,3 +219,2 @@ escapedCommand: 'unknown command', | ||
stderr: '', | ||
all: '', | ||
failed: true, | ||
@@ -142,3 +229,3 @@ timedOut: false, | ||
### Kill a process | ||
### Graceful termination | ||
@@ -159,14 +246,81 @@ Using SIGTERM, and after 2 seconds, kill it with SIGKILL. | ||
### execa(file, arguments, options?) | ||
### Methods | ||
Execute a file. Think of this as a mix of [`child_process.execFile()`](https://nodejs.org/api/child_process.html#child_process_child_process_execfile_file_args_options_callback) and [`child_process.spawn()`](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options). | ||
#### execa(file, arguments?, options?) | ||
No escaping/quoting is needed. | ||
Executes a command using `file ...arguments`. `arguments` are specified as an array of strings. Returns a [`childProcess`](#childprocess). | ||
Unless the [`shell`](#shell) option is used, no shell interpreter (Bash, `cmd.exe`, etc.) is used, so shell features such as variables substitution (`echo $PATH`) are not allowed. | ||
Arguments are [automatically escaped](#shell-syntax). They can contain any character, including spaces. | ||
Returns a [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess) which: | ||
- is also a `Promise` resolving or rejecting with a [`childProcessResult`](#childProcessResult). | ||
- exposes the following additional methods and properties. | ||
This is the preferred method when executing single commands. | ||
#### execaNode(scriptPath, arguments?, options?) | ||
Executes a Node.js file using `node scriptPath ...arguments`. `arguments` are specified as an array of strings. Returns a [`childProcess`](#childprocess). | ||
Arguments are [automatically escaped](#shell-syntax). They can contain any character, including spaces. | ||
This is the preferred method when executing Node.js files. | ||
Like [`child_process#fork()`](https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options): | ||
- the current Node version and options are used. This can be overridden using the [`nodePath`](#nodepath-for-node-only) and [`nodeOptions`](#nodeoptions-for-node-only) options. | ||
- the [`shell`](#shell) option cannot be used | ||
- an extra channel [`ipc`](https://nodejs.org/api/child_process.html#child_process_options_stdio) is passed to [`stdio`](#stdio) | ||
#### $\`command\` | ||
Executes a command. The `command` string includes both the `file` and its `arguments`. Returns a [`childProcess`](#childprocess). | ||
Arguments are [automatically escaped](#shell-syntax). They can contain any character, but spaces must use `${}` like `` $`echo ${'has space'}` ``. | ||
This is the preferred method when executing multiple commands in a script file. | ||
The `command` string can inject any `${value}` with the following types: string, number, [`childProcess`](#childprocess) or an array of those types. For example: `` $`echo one ${'two'} ${3} ${['four', 'five']}` ``. For `${childProcess}`, the process's `stdout` is used. | ||
For more information, please see [this section](#scripts-interface) and [this page](docs/scripts.md). | ||
#### $(options) | ||
Returns a new instance of [`$`](#command) but with different default `options`. Consecutive calls are merged to previous ones. | ||
This can be used to either: | ||
- Set options for a specific command: `` $(options)`command` `` | ||
- Share options for multiple commands: `` const $$ = $(options); $$`command`; $$`otherCommand`; `` | ||
#### execaCommand(command, options?) | ||
Executes a command. The `command` string includes both the `file` and its `arguments`. Returns a [`childProcess`](#childprocess). | ||
Arguments are [automatically escaped](#shell-syntax). They can contain any character, but spaces must be escaped with a backslash like `execaCommand('echo has\\ space')`. | ||
This is the preferred method when executing a user-supplied `command` string, such as in a REPL. | ||
### execaSync(file, arguments?, options?) | ||
Same as [`execa()`](#execacommandcommand-options) but synchronous. | ||
Returns or throws a [`childProcessResult`](#childProcessResult). | ||
### $.sync\`command\` | ||
Same as [$\`command\`](#command) but synchronous. | ||
Returns or throws a [`childProcessResult`](#childProcessResult). | ||
### execaCommandSync(command, options?) | ||
Same as [`execaCommand()`](#execacommand-command-options) but synchronous. | ||
Returns or throws a [`childProcessResult`](#childProcessResult). | ||
### Shell syntax | ||
For all the [methods above](#methods), no shell interpreter (Bash, cmd.exe, etc.) is used unless the [`shell` option](#shell) is set. This means shell-specific characters and expressions (`$variable`, `&&`, `||`, `;`, `|`, etc.) have no special meaning and do not need to be escaped. | ||
### childProcess | ||
The return value of all [asynchronous methods](#methods) is both: | ||
- a `Promise` resolving or rejecting with a [`childProcessResult`](#childProcessResult). | ||
- a [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess) with the following additional methods and properties. | ||
#### kill(signal?, options?) | ||
@@ -176,2 +330,4 @@ | ||
Note that this graceful termination does not work on Windows, because Windows [doesn't support signals](https://nodejs.org/api/process.html#process_signal_events) (`SIGKILL` and `SIGTERM` has the same effect of force-killing the process immediately.) If you want to achieve graceful termination on Windows, you have to use other means, such as [`taskkill`](https://github.com/sindresorhus/taskkill). | ||
##### options.forceKillAfterTimeout | ||
@@ -196,31 +352,25 @@ | ||
### execaSync(file, arguments?, options?) | ||
#### pipeStdout(target) | ||
Execute a file synchronously. | ||
[Pipe](https://nodejs.org/api/stream.html#readablepipedestination-options) the child process's `stdout` to `target`, which can be: | ||
- Another [`execa()` return value](#pipe-multiple-processes) | ||
- A [writable stream](#save-and-pipe-output-from-a-child-process) | ||
- A [file path string](#redirect-output-to-a-file) | ||
Returns or throws a [`childProcessResult`](#childProcessResult). | ||
If the `target` is another [`execa()` return value](#execacommandcommand-options), it is returned. Otherwise, the original `execa()` return value is returned. This allows chaining `pipeStdout()` then `await`ing the [final result](#childprocessresult). | ||
### execaCommand(command, options?) | ||
The [`stdout` option](#stdout-1) must be kept as `pipe`, its default value. | ||
Same as [`execa()`](#execafile-arguments-options) except both file and arguments are specified in a single `command` string. For example, `execa('echo', ['unicorns'])` is the same as `execaCommand('echo unicorns')`. | ||
#### pipeStderr(target) | ||
If the file or an argument contains spaces, they must be escaped with backslashes. This matters especially if `command` is not a constant but a variable, for example with `__dirname` or `process.cwd()`. Except for spaces, no escaping/quoting is needed. | ||
Like [`pipeStdout()`](#pipestdouttarget) but piping the child process's `stderr` instead. | ||
The [`shell` option](#shell) must be used if the `command` uses shell-specific features (for example, `&&` or `||`), as opposed to being a simple `file` followed by its `arguments`. | ||
The [`stderr` option](#stderr-1) must be kept as `pipe`, its default value. | ||
### execaCommandSync(command, options?) | ||
#### pipeAll(target) | ||
Same as [`execaCommand()`](#execacommand-command-options) but synchronous. | ||
Combines both [`pipeStdout()`](#pipestdouttarget) and [`pipeStderr()`](#pipestderrtarget). | ||
Returns or throws a [`childProcessResult`](#childProcessResult). | ||
Either the [`stdout` option](#stdout-1) or the [`stderr` option](#stderr-1) must be kept as `pipe`, their default value. Also, the [`all` option](#all-2) must be set to `true`. | ||
### execaNode(scriptPath, arguments?, options?) | ||
Execute a Node.js script as a child process. | ||
Same as `execa('node', [scriptPath, ...arguments], options)` except (like [`child_process#fork()`](https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options)): | ||
- the current Node version and options are used. This can be overridden using the [`nodePath`](#nodepath-for-node-only) and [`nodeOptions`](#nodeoptions-for-node-only) options. | ||
- the [`shell`](#shell) option cannot be used | ||
- an extra channel [`ipc`](https://nodejs.org/api/child_process.html#child_process_options_stdio) is passed to [`stdio`](#stdio) | ||
### childProcessResult | ||
@@ -251,3 +401,3 @@ | ||
Same as [`command`](#command) but escaped. | ||
Same as [`command`](#command-1) but escaped. | ||
@@ -327,2 +477,8 @@ This is meant to be copy and pasted into a shell, for debugging purposes. | ||
#### cwd | ||
Type: `string` | ||
The `cwd` of the command if provided in the [command options](#cwd-1). Otherwise it is `process.cwd()`. | ||
#### message | ||
@@ -366,3 +522,3 @@ | ||
Type: `boolean`\ | ||
Default: `false` | ||
Default: `true` with [`$`](#command), `false` otherwise | ||
@@ -379,4 +535,2 @@ Prefer locally installed binaries when looking for a binary to execute.\ | ||
Using a `URL` is only supported in Node.js `14.18.0`, `16.14.0` or above. | ||
#### execPath | ||
@@ -411,6 +565,16 @@ | ||
If the input is a file, use the [`inputFile` option](#inputfile) instead. | ||
#### inputFile | ||
Type: `string` | ||
Use a file as input to the the `stdin` of your binary. | ||
If the input is not a file, use the [`input` option](#input) instead. | ||
#### stdin | ||
Type: `string | number | Stream | undefined`\ | ||
Default: `pipe` | ||
Default: `inherit` with [`$`](#command), `pipe` otherwise | ||
@@ -472,4 +636,2 @@ Same options as [`stdio`](https://nodejs.org/dist/latest-v6.x/docs/api/child_process.html#child_process_options_stdio). | ||
Using a `URL` is only supported in Node.js `14.18.0`, `16.14.0` or above. | ||
#### env | ||
@@ -504,4 +666,2 @@ | ||
Requires Node.js `13.2.0` or later. | ||
[More info.](https://nodejs.org/api/child_process.html#child_process_advanced_serialization) | ||
@@ -544,3 +704,3 @@ | ||
Specify the character encoding used to decode the `stdout` and `stderr` output. If set to `null`, then `stdout` and `stderr` will be a `Buffer` instead of a string. | ||
Specify the character encoding used to decode the `stdout` and `stderr` output. If set to `'buffer'` or `null`, then `stdout` and `stderr` will be a `Buffer` instead of a string. | ||
@@ -574,6 +734,4 @@ #### timeout | ||
When `AbortController.abort()` is called, [`.isCanceled`](#iscanceled) becomes `false`. | ||
When `AbortController.abort()` is called, [`.isCanceled`](#iscanceled) becomes `true`. | ||
*Requires Node.js 16 or later.* | ||
#### windowsVerbatimArguments | ||
@@ -593,2 +751,11 @@ | ||
#### verbose | ||
Type: `boolean`\ | ||
Default: `false` | ||
[Print each command](#verbose-mode) on `stderr` before executing it. | ||
This can also be enabled by setting the `NODE_DEBUG=execa` environment variable in the current process. | ||
#### nodePath *(For `.node()` only)* | ||
@@ -625,41 +792,29 @@ | ||
### Save and pipe output from a child process | ||
### Cancelling a spawned process | ||
Let's say you want to show the output of a child process in real-time while also saving it to a variable. | ||
```js | ||
import {execa} from 'execa'; | ||
const subprocess = execa('echo', ['foo']); | ||
subprocess.stdout.pipe(process.stdout); | ||
const abortController = new AbortController(); | ||
const subprocess = execa('node', [], {signal: abortController.signal}); | ||
const {stdout} = await subprocess; | ||
console.log('child output:', stdout); | ||
``` | ||
setTimeout(() => { | ||
abortController.abort(); | ||
}, 1000); | ||
### Redirect output to a file | ||
```js | ||
import {execa} from 'execa'; | ||
const subprocess = execa('echo', ['foo']) | ||
subprocess.stdout.pipe(fs.createWriteStream('stdout.txt')) | ||
try { | ||
await subprocess; | ||
} catch (error) { | ||
console.log(subprocess.killed); // true | ||
console.log(error.isCanceled); // true | ||
} | ||
``` | ||
### Redirect input from a file | ||
```js | ||
import {execa} from 'execa'; | ||
const subprocess = execa('cat') | ||
fs.createReadStream('stdin.txt').pipe(subprocess.stdin) | ||
``` | ||
### Execute the current package's binary | ||
```js | ||
import {getBinPathSync} from 'get-bin-path'; | ||
import {getBinPath} from 'get-bin-path'; | ||
const binPath = getBinPathSync(); | ||
const subprocess = execa(binPath); | ||
const binPath = await getBinPath(); | ||
await execa(binPath); | ||
``` | ||
@@ -666,0 +821,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
80144
13
1475
823
+ Addedget-stream@8.0.1(transitive)
+ Addedhuman-signals@5.0.0(transitive)
+ Addedsignal-exit@4.1.0(transitive)
- Removedget-stream@6.0.1(transitive)
- Removedhuman-signals@3.0.1(transitive)
- Removedsignal-exit@3.0.7(transitive)
Updatedget-stream@^8.0.1
Updatedhuman-signals@^5.0.0
Updatedsignal-exit@^4.1.0