Comparing version 7.0.0 to 7.1.0
448
index.d.ts
import {type Buffer} from 'node:buffer'; | ||
import {type ChildProcess} from 'node:child_process'; | ||
import {type Stream, type Readable as ReadableStream} from 'node:stream'; | ||
import {type Stream, type Readable as ReadableStream, type Writable as WritableStream} from 'node:stream'; | ||
@@ -30,3 +30,3 @@ export type StdioOption = | ||
@default false | ||
@default `true` with `$`/`$.sync`, `false` otherwise | ||
*/ | ||
@@ -248,2 +248,11 @@ readonly preferLocal?: boolean; | ||
readonly windowsHide?: boolean; | ||
/** | ||
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; | ||
}; | ||
@@ -254,4 +263,13 @@ | ||
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; | ||
/** | ||
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>; | ||
@@ -262,4 +280,13 @@ | ||
Write some input to the `stdin` of your binary. | ||
If the input is a file, use the `inputFile` option instead. | ||
*/ | ||
readonly input?: string | Buffer; | ||
/** | ||
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>; | ||
@@ -283,3 +310,5 @@ | ||
export type ExecaReturnBase<StdoutStderrType> = { | ||
type StdoutStderrAll = string | Buffer | undefined; | ||
export type ExecaReturnBase<StdoutStderrType extends StdoutStderrAll> = { | ||
/** | ||
@@ -345,4 +374,4 @@ The file and arguments that were run, for logging purposes. | ||
export type ExecaSyncReturnValue<StdoutErrorType = string> = { | ||
} & ExecaReturnBase<StdoutErrorType>; | ||
export type ExecaSyncReturnValue<StdoutStderrType extends StdoutStderrAll = string> = { | ||
} & ExecaReturnBase<StdoutStderrType>; | ||
@@ -359,3 +388,3 @@ /** | ||
*/ | ||
export type ExecaReturnValue<StdoutErrorType = string> = { | ||
export type ExecaReturnValue<StdoutStderrType extends StdoutStderrAll = string> = { | ||
/** | ||
@@ -368,3 +397,3 @@ The output of the process with `stdout` and `stderr` interleaved. | ||
*/ | ||
all?: StdoutErrorType; | ||
all?: StdoutStderrType; | ||
@@ -377,5 +406,5 @@ /** | ||
isCanceled: boolean; | ||
} & ExecaSyncReturnValue<StdoutErrorType>; | ||
} & ExecaSyncReturnValue<StdoutStderrType>; | ||
export type ExecaSyncError<StdoutErrorType = string> = { | ||
export type ExecaSyncError<StdoutStderrType extends StdoutStderrAll = string> = { | ||
/** | ||
@@ -399,5 +428,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<StdoutErrorType>; | ||
} & Error & ExecaReturnBase<StdoutStderrType>; | ||
export type ExecaError<StdoutErrorType = string> = { | ||
export type ExecaError<StdoutStderrType extends StdoutStderrAll = string> = { | ||
/** | ||
@@ -410,3 +439,3 @@ The output of the process with `stdout` and `stderr` interleaved. | ||
*/ | ||
all?: StdoutErrorType; | ||
all?: StdoutStderrType; | ||
@@ -417,3 +446,3 @@ /** | ||
isCanceled: boolean; | ||
} & ExecaSyncError<StdoutErrorType>; | ||
} & ExecaSyncError<StdoutStderrType>; | ||
@@ -431,3 +460,3 @@ export type KillOptions = { | ||
export type ExecaChildPromise<StdoutErrorType> = { | ||
export type ExecaChildPromise<StdoutStderrType extends StdoutStderrAll> = { | ||
/** | ||
@@ -443,4 +472,4 @@ 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>; | ||
@@ -456,18 +485,52 @@ /** | ||
cancel(): void; | ||
/** | ||
[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<StdoutErrorType = string> = ChildProcess & | ||
ExecaChildPromise<StdoutErrorType> & | ||
Promise<ExecaReturnValue<StdoutErrorType>>; | ||
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> | ||
``` | ||
@@ -479,21 +542,90 @@ 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 | ||
} | ||
\*\/ | ||
} | ||
``` | ||
// 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); | ||
``` | ||
*/ | ||
@@ -514,9 +646,59 @@ export function execa( | ||
/** | ||
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 | ||
} | ||
\*\/ | ||
} | ||
``` | ||
*/ | ||
@@ -540,10 +722,13 @@ export function execaSync( | ||
/** | ||
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 | ||
@@ -566,3 +751,13 @@ @example | ||
@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' | ||
``` | ||
*/ | ||
@@ -572,13 +767,172 @@ export function execaCommandSync(command: string, options?: SyncOptions): ExecaSyncReturnValue; | ||
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<null>): 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']); | ||
``` | ||
*/ | ||
@@ -585,0 +939,0 @@ export function execaNode( |
42
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,22 @@ throw makeError({ | ||
function create$(options) { | ||
function $(templatesOrOptions, ...expressions) { | ||
if (Array.isArray(templatesOrOptions)) { | ||
const [file, ...args] = parseTemplates(templatesOrOptions, expressions); | ||
return execa(file, args, options); | ||
} | ||
return create$({...options, ...templatesOrOptions}); | ||
} | ||
$.sync = (templates, ...expressions) => { | ||
const [file, ...args] = parseTemplates(templates, expressions); | ||
return execaSync(file, args, options); | ||
}; | ||
return $; | ||
} | ||
export const $ = create$({preferLocal: true}); | ||
export function execaCommand(command, options) { | ||
@@ -235,0 +263,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 = []) => { | ||
@@ -42,1 +45,53 @@ if (!Array.isArray(args)) { | ||
}; | ||
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 parseTemplate = (template, index, templates, expressions) => { | ||
const templateString = template ?? templates.raw[index]; | ||
const templateTokens = templateString.split(SPACES_REGEXP).filter(Boolean); | ||
if (index === expressions.length) { | ||
return templateTokens; | ||
} | ||
const expression = expressions[index]; | ||
return Array.isArray(expression) | ||
? [...templateTokens, ...expression.map(expression => parseExpression(expression))] | ||
: [...templateTokens, parseExpression(expression)]; | ||
}; | ||
export const parseTemplates = (templates, expressions) => templates.flatMap( | ||
(template, index) => parseTemplate(template, index, templates, expressions), | ||
); |
@@ -19,4 +19,2 @@ // eslint-disable-next-line unicorn/prefer-top-level-await | ||
} | ||
return spawned; | ||
}; | ||
@@ -23,0 +21,0 @@ |
@@ -0,1 +1,2 @@ | ||
import {createReadStream, readFileSync} from 'node:fs'; | ||
import {isStream} from 'is-stream'; | ||
@@ -5,4 +6,41 @@ import getStream from 'get-stream'; | ||
// `input` option | ||
export const handleInput = (spawned, input) => { | ||
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) { | ||
@@ -83,7 +121,1 @@ return; | ||
}; | ||
export const validateInputSync = ({input}) => { | ||
if (isStream(input)) { | ||
throw new TypeError('The `input` option cannot be a stream in sync mode'); | ||
} | ||
}; |
{ | ||
"name": "execa", | ||
"version": "7.0.0", | ||
"version": "7.1.0", | ||
"description": "Process execution for humans", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
350
readme.md
@@ -15,12 +15,15 @@ <picture> | ||
- 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. | ||
@@ -35,2 +38,4 @@ ## Install | ||
### Promise interface | ||
```js | ||
@@ -44,11 +49,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 | ||
@@ -58,32 +133,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 | ||
@@ -93,24 +145,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) { | ||
@@ -120,10 +175,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', | ||
@@ -133,3 +188,2 @@ escapedCommand: 'unknown command', | ||
stderr: '', | ||
all: '', | ||
failed: true, | ||
@@ -144,3 +198,3 @@ timedOut: false, | ||
### Kill a process | ||
### Graceful termination | ||
@@ -161,14 +215,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?) | ||
@@ -197,31 +318,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 | ||
@@ -252,3 +367,3 @@ | ||
Same as [`command`](#command) but escaped. | ||
Same as [`command`](#command-1) but escaped. | ||
@@ -366,3 +481,3 @@ This is meant to be copy and pasted into a shell, for debugging purposes. | ||
Type: `boolean`\ | ||
Default: `false` | ||
Default: `true` with [`$`](#command)/[`$.sync`](#synccommand), `false` otherwise | ||
@@ -408,2 +523,12 @@ Prefer locally installed binaries when looking for a binary to execute.\ | ||
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 | ||
@@ -582,2 +707,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)* | ||
@@ -614,41 +748,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); | ||
``` | ||
@@ -655,0 +777,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
76053
13
1405
787