Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add multiple guides and documentation
- Loading branch information
Showing
32 changed files
with
2,975 additions
and
1,507 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<picture> | ||
<source media="(prefers-color-scheme: dark)" srcset="../media/logo_dark.svg"> | ||
<img alt="execa logo" src="media/logo.svg" width="400"> | ||
</picture> | ||
<br> | ||
|
||
# 🤖 Binary data | ||
|
||
## Binary output | ||
|
||
By default, the subprocess [output](../readme.md#resultstdout) is a UTF8 string. If it is binary, the [`encoding`](../readme.md#optionsencoding) option should be set to `'buffer'` instead. The output will be an [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array). | ||
|
||
```js | ||
import {execa} from 'execa'; | ||
|
||
const {stdout} = await execa({encoding: 'buffer'})`unzip file.zip`; | ||
console.log(stdout.byteLength); | ||
``` | ||
|
||
## Encoding | ||
|
||
When the output is binary, the [`encoding`](../readme.md#optionsencoding) option can also be set to `'hex'`, `'base64'` or `'base64url'`. The output will be a string then. | ||
|
||
```js | ||
const {stdout} = await execa({encoding: 'hex'})`unzip file.zip`; | ||
console.log(stdout); // Hexadecimal string | ||
``` | ||
|
||
## Iterable | ||
|
||
By default, the subprocess [iterates](lines.md#progressive-splitting) over line strings. However, if the [`encoding`](../readme.md#optionsencoding) subprocess option is binary, or if the [`binary`](../readme.md#readableoptionsbinary) iterable option is `true`, it iterates over arbitrary chunks of [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) data instead. | ||
|
||
```js | ||
for await (const data of execa({encoding: 'buffer'})`unzip file.zip`) { /* ... */ } | ||
``` | ||
|
||
## Streams | ||
|
||
[Streams produced](streams.md#converting-a-subprocess-to-a-stream) by [`subprocess.readable()`](../readme.md#subprocessreadablereadableoptions) and [`subprocess.duplex()`](../readme.md#subprocessduplexduplexoptions) are binary by default, which means they iterate over arbitrary [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer) chunks. However, if the [`binary`](../readme.md#readableoptionsbinary) option is `false`, they iterate over line strings instead, and the stream is [in object mode](https://nodejs.org/api/stream.html#object-mode). | ||
|
||
```js | ||
const readable = execa`npm run build`.readable({binary: false}); | ||
readable.on('data', lineString => { /* ... */ }) | ||
``` | ||
|
||
## Transforms | ||
|
||
The same applies to transforms. When the [`encoding`](../readme.md#optionsencoding) subprocess option is binary, or when the [`binary`](transform.md#transformoptionsbinary) transform option is `true`, it iterates over arbitrary chunks of [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) data instead. | ||
|
||
However, transforms can always `yield` either a `string` or an `Uint8Array`, regardless of whether the output is binary. | ||
|
||
```js | ||
const transform = function * (data) { /* ... */ } | ||
|
||
await execa({stdout: {transform, binary: true}})`unzip file.zip`; | ||
``` | ||
|
||
## Binary input | ||
|
||
There are multiple ways to pass binary input using the [`stdin`](../readme.md#optionsstdin), [`input`](../readme.md#optionsinput) or [`inputFile`](../readme.md#optionsinputfile) options. This includes using a `Uint8Array`, a file, or a stream. | ||
|
||
```js | ||
await execa({stdin: new Uint8Array([/* ... */])})`hexdump`; | ||
``` | ||
|
||
<hr> | ||
|
||
[**Next**: 🧙 Transforms](transform.md)\ | ||
[**Previous**: 📃 Text lines](lines.md)\ | ||
[**Top**: Table of contents](../readme.md#documentation) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
<picture> | ||
<source media="(prefers-color-scheme: dark)" srcset="../media/logo_dark.svg"> | ||
<img alt="execa logo" src="media/logo.svg" width="400"> | ||
</picture> | ||
<br> | ||
|
||
# 🐛 Debugging | ||
|
||
## Command | ||
|
||
[`error.command`](../readme.md#resultcommand) contains the file and arguments that were run. It is intended for logging or debugging. | ||
|
||
[`error.escapedCommand`](../readme.md#resultescapedcommand) is the same, except control characters are escaped. This makes it safe to either print or copy and paste in a terminal, for debugging purposes. | ||
|
||
Since the escaping is fairly basic, this should not be executed directly as a subprocess, including using [`execa()`](#execafile-arguments-options) or [`execaCommand()`](#execacommandcommand-options). | ||
|
||
```js | ||
import {execa} from 'execa'; | ||
|
||
try { | ||
await execa`npm run build\ntask`; | ||
} catch (error) { | ||
console.error(error.command); // 'npm run build\ntask' | ||
console.error(error.escapedCommand); // 'npm run build\\ntask' | ||
throw error; | ||
} | ||
``` | ||
|
||
## Duration | ||
|
||
```js | ||
try { | ||
const result = execa`npm run build`; | ||
console.log('Command duration:', result.durationMs); // 150 | ||
} catch (error) { | ||
console.error('Command duration:', error.durationMs); // 150 | ||
throw error; | ||
} | ||
``` | ||
|
||
## Verbose mode | ||
|
||
### Short mode | ||
|
||
When the [`verbose`](../readme.md#optionsverbose) option is `'short'`, the [command](#command), [duration](#duration) and [error messages](errors.md#error-message) are printed on `stderr`. | ||
|
||
```js | ||
// build.js | ||
await execa({verbose: 'short'})`npm run build`; | ||
``` | ||
|
||
```sh | ||
$ node build.js | ||
[20:36:11.043] [0] $ npm run build | ||
[20:36:11.885] [0] ✔ (done in 842ms) | ||
``` | ||
|
||
### Full mode | ||
|
||
When the [`verbose`](../readme.md#optionsverbose) option is `'full'`, the subprocess' [`stdout` and `stderr`](output.md) are also logged. Both are printed on `stderr`. | ||
|
||
The output is not logged if either: | ||
- the [`stdout`](../readme.md#optionsstdout)/[`stderr`](../readme.md#optionsstderr) option is [`ignore`](output.md#ignore-output) or [`inherit`](output.md#terminal-output). | ||
- the `stdout`/`stderr` is redirected to [a stream](streams.md#output), [a file](output.md#file-output), a [file descriptor](output.md#terminal-output), or [another subprocess](pipe.md). | ||
- the [`encoding`](../readme.md#optionsencoding) option is [binary](binary.md#binary-output). | ||
|
||
```js | ||
// build.js | ||
await execa({verbose: 'full'})`npm run build`; | ||
``` | ||
|
||
```sh | ||
$ node build.js | ||
[20:36:11.043] [0] $ npm run build | ||
Building application... | ||
Done building. | ||
[20:36:11.885] [0] ✔ (done in 842ms) | ||
``` | ||
|
||
### Global mode | ||
|
||
When the `NODE_DEBUG=execa` environment variable is set, the [`verbose`](../readme.md#optionsverbose) option defaults to `'full'` for all commands. | ||
|
||
```js | ||
// build.js | ||
|
||
// This is logged by default | ||
await execa`npm run build`; | ||
// This is not logged | ||
await execa({verbose: 'none'})`npm run test`; | ||
``` | ||
|
||
```sh | ||
$ NODE_DEBUG=execa node build.js | ||
[20:36:11.043] [0] $ npm run build | ||
Building application... | ||
Done building. | ||
[20:36:11.885] [0] ✔ (done in 842ms) | ||
``` | ||
|
||
<hr> | ||
|
||
[**Next**: 📎 Windows](windows.md)\ | ||
[**Previous**: 📞 Inter-process communication](ipc.md)\ | ||
[**Top**: Table of contents](../readme.md#documentation) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<picture> | ||
<source media="(prefers-color-scheme: dark)" srcset="../media/logo_dark.svg"> | ||
<img alt="execa logo" src="media/logo.svg" width="400"> | ||
</picture> | ||
<br> | ||
|
||
# 🌐 Environment | ||
|
||
## [Current directory](https://en.wikipedia.org/wiki/Working_directory) | ||
|
||
```js | ||
const {cwd} = await execa`npm run build`; | ||
console.log(cwd); // Current directory when running the command | ||
``` | ||
|
||
```js | ||
await execa({cwd: '/path/to/cwd'})`npm run build`; | ||
``` | ||
|
||
## Local binaries | ||
|
||
```js | ||
await execa('./node_modules/bin/eslint'); | ||
``` | ||
|
||
The [`preferLocal`](../readme.md#optionspreferlocal) option can be used to automatically find binaries installed in local `node_modules`. | ||
|
||
```js | ||
await execa('eslint', {preferLocal: true}); | ||
``` | ||
|
||
Those are searched in the current or any parent directory. The [`localDir`](../readme.md#optionslocaldir) option can select a different directory. | ||
|
||
```js | ||
await execa('eslint', {preferLocal: true, localDir: '/path/to/dir'}); | ||
``` | ||
|
||
## Current package's binary | ||
|
||
Execa can be combined with [`get-bin-path`](https://github.com/ehmicky/get-bin-path) to test the current package's binary. As opposed to hard-coding the path to the binary, this validates that the `package.json` `bin` field is correctly set up. | ||
|
||
```js | ||
import {execa} from 'execa'; | ||
import {getBinPath} from 'get-bin-path'; | ||
|
||
const binPath = await getBinPath(); | ||
await execa(binPath); | ||
``` | ||
|
||
## Background subprocess | ||
|
||
When the [`detached`](../readme.md#optionsdetached) option is `true`, the subprocess [runs independently](https://en.wikipedia.org/wiki/Background_process) from the current process. | ||
|
||
Specific behavior depends on the platform. [More info.](https://nodejs.org/api/child_process.html#child_process_options_detached). | ||
|
||
```js | ||
await execa({detached: true})`npm run start`; | ||
``` | ||
|
||
<hr> | ||
|
||
[**Next**: ❌ Errors](errors.md)\ | ||
[**Previous**: 🐢 Node.js files](node.md)\ | ||
[**Top**: Table of contents](../readme.md#documentation) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
<picture> | ||
<source media="(prefers-color-scheme: dark)" srcset="../media/logo_dark.svg"> | ||
<img alt="execa logo" src="media/logo.svg" width="400"> | ||
</picture> | ||
<br> | ||
|
||
# ❌ Errors | ||
|
||
## Subprocess failure | ||
|
||
When the subprocess fails, the returned promise is rejected with an [`ExecaError`](../readme.md#execaerror) instance. The `error` has the same shape as successful [results](../readme.md#result), with a few additional [error-specific fields](../readme.md#execaerror). `error.failed` is always `true`. | ||
|
||
```js | ||
import {execa, ExecaError} from 'execa'; | ||
|
||
try { | ||
const result = await execa`npm run build`; | ||
console.log(result.failed); // false | ||
} catch (error) { | ||
if (error instanceof ExecaError) { | ||
console.error(error.failed); // true | ||
} | ||
} | ||
``` | ||
|
||
## Preventing exceptions | ||
|
||
When the [`reject`](../readme.md#optionsreject) option is `false`, the `error` is returned instead. | ||
|
||
```js | ||
const resultOrError = await execa`npm run build`; | ||
if (resultOrError.failed) { | ||
console.error(resultOrError); | ||
} | ||
``` | ||
|
||
## Exit code | ||
|
||
The subprocess fails when its [exit code](https://en.wikipedia.org/wiki/Exit_status) is not `0`. The exit code is available as [`error.exitCode`](../readme.md#resultexitcode). It is `undefined` when the subprocess fails to spawn or when it was [terminated by a signal](termination.md#signal-termination). | ||
|
||
```js | ||
try { | ||
await execa`npm run build`; | ||
} catch (error) { | ||
// Either non-0 integer or undefined | ||
console.error(error.exitCode); | ||
} | ||
``` | ||
|
||
## Failure reason | ||
|
||
The subprocess can fail for other reasons. Each reason can be detected using a specific boolean property: | ||
- [`error.isTerminated`](../readme.md#resultisterminated): [signal termination](termination.md#signal-termination), including [`subprocess.kill()`](termination.md#signal-termination) and the [`cancelSignal`](termination.md#canceling) option. | ||
- [`error.timedOut`](../readme.md#resulttimedout): [`timeout`](termination.md#timeout) option. | ||
- [`error.isCanceled`](../readme.md#resultiscanceled): [`cancelSignal`](termination.md#canceling) option. | ||
- [`error.isMaxBuffer`](../readme.md#resultismaxbuffer): [`maxBuffer`](output.md#big-output) option. | ||
- Otherwise, it could not be spawned because of either: | ||
- The command's binary not found. | ||
- An invalid argument or [option](../readme.md#options) was passed. | ||
- Not enough memory or too many subprocesses. | ||
|
||
```js | ||
try { | ||
await execa`npm run build`; | ||
} catch (error) { | ||
if (error.timedOut) { | ||
handleTimeout(error); | ||
} | ||
|
||
throw error | ||
} | ||
``` | ||
|
||
## Error message | ||
|
||
For better debugging, [`error.message`](../readme.md#errormessage) includes both: | ||
- The command and the [reason it failed](#failure-reason). | ||
- Its [`stdout`, `stderr`](output.md#stdout-and-stderr) and other [file descriptor' output](output.md#additional-file-descriptors), separated with newlines and not interleaved. | ||
|
||
[`error.shortMessage`](../readme.md#errorshortmessage) is the same but without `stdout`/`stderr`. [`error.originalMessage`](../readme.md#errororiginalmessage) is the same but also without the command. | ||
|
||
```js | ||
try { | ||
await execa`npm run build`; | ||
} catch (error) { | ||
console.error(error.originalMessage); | ||
// The task "build" does not exist. | ||
|
||
console.error(error.shortMessage); | ||
// Command failed with exit code 3: npm run build | ||
// The task "build" does not exist. | ||
|
||
console.error(error.message); | ||
// Command failed with exit code 3: npm run build | ||
// The task "build" does not exist. | ||
// [stderr contents...] | ||
// [stdout contents...] | ||
} | ||
``` | ||
|
||
## Retry on error | ||
|
||
Safely handle failures by using automatic retries and exponential backoff with the [`p-retry`](https://github.com/sindresorhus/p-retry) package. | ||
|
||
```js | ||
import pRetry from 'p-retry'; | ||
import {execa} from 'execa'; | ||
|
||
const run = () => execa`curl -sSL https://sindresorhus.com/unicorn`; | ||
console.log(await pRetry(run, {retries: 5})); | ||
``` | ||
|
||
<hr> | ||
|
||
[**Next**: 🏁 Termination](termination.md)\ | ||
[**Previous**: 🌐 Environment](environment.md)\ | ||
[**Top**: Table of contents](../readme.md#documentation) |
Oops, something went wrong.