From bfaf2ff9ee3709f29df33c3c4f6768306582ad80 Mon Sep 17 00:00:00 2001 From: Anton Golub Date: Mon, 13 May 2024 23:44:33 +0300 Subject: [PATCH] Docs v8.1.0 (#808) --- .vitepress/config.mts | 7 ++- api.md | 128 +++++++++++++++++++++++++++++++++++++++++- cli.md | 40 +++++++++++++ configuration.md | 20 ++++++- contribution.md | 52 +++++++++++++++++ getting-started.md | 11 +++- migration-from-v7.md | 44 +++++++++++++++ process-output.md | 25 +++++++++ process-promise.md | 6 +- setup.md | 87 ++++++++++++++++++++++++++++ typescript.md | 55 ++++++++++++++---- 11 files changed, 454 insertions(+), 21 deletions(-) create mode 100644 contribution.md create mode 100644 migration-from-v7.md create mode 100644 process-output.md create mode 100644 setup.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 849fa26a2e..920ebacb9b 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -37,10 +37,13 @@ export default defineConfig({ text: 'Docs', items: [ {text: 'Getting Started', link: '/getting-started'}, - {text: 'Process Promise', link: '/process-promise'}, + {text: 'Setup', link: '/setup'}, {text: 'API Reference', link: '/api'}, - {text: 'Configuration', link: '/configuration'}, {text: 'CLI Usage', link: '/cli'}, + {text: 'Configuration', link: '/configuration'}, + {text: 'Process Promise', link: '/process-promise'}, + {text: 'Contribution Guide', link: '/contribution'}, + {text: 'Migration from v7', link: '/migration-from-v7'}, ], }, { diff --git a/api.md b/api.md index e7a9609ae1..c9fd549f08 100644 --- a/api.md +++ b/api.md @@ -1,5 +1,86 @@ # API Reference +## $.sync +Zx provides both synchronous and asynchronous command executions, returns [`ProcessOutput`](./process-output) or [`ProcessPromise`](./process-promise) respectively. + +```js +const list = await $`ls -la` +const dir = $.sync`pwd` +``` + +## $({...}) + +`$` object holds the default zx [configuration](./configuration), which is used for all execution. To specify a custom preset use `$` as factory: + +```js +const $$ = $({ + verbose: false, + env: {NODE_ENV: 'production'}, +}) + +const env = await $$`node -e 'console.log(process.env.NODE_ENV)'` +const pwd = $$.sync`pwd` +const hello = $({quiet: true})`echo "Hello!"` +``` + +### $({input}) + +The input option passes the specified `stdin` to the command. + +```js +const p1 = $({ input: 'foo' })`cat` +const p2 = $({ input: Readable.from('bar') })`cat` +const p3 = $({ input: Buffer.from('baz') })`cat` +const p4 = $({ input: p3 })`cat` +const p5 = $({ input: await p3 })`cat` +``` + +### $({signal}) + +The signal option makes the process abortable. + +```js +const {signal} = new AbortController() +const p = $({ signal })`sleep 9999` + +setTimeout(() => signal.abort('reason'), 1000) +``` + +### $({timeout}) + +The timeout option makes the process autokillable after the specified delay. + +```js +const p = $({timeout: '1s'})`sleep 999` +``` + +The full options list: +```ts +interface Options { + cwd: string + ac: AbortController + signal: AbortSignal + input: string | Buffer | Readable | ProcessOutput | ProcessPromise + timeout: Duration + timeoutSignal: string + stdio: StdioOptions + verbose: boolean + sync: boolean + env: NodeJS.ProcessEnv + shell: string | boolean + nothrow: boolean + prefix: string + postfix: string + quote: typeof quote + quiet: boolean + detached: boolean + spawn: typeof spawn + spawnSync: typeof spawnSync + log: typeof log + kill: typeof kill +} +``` + ## cd() Changes the current working directory. @@ -133,10 +214,55 @@ The [which](https://github.com/npm/node-which) package. const node = await which('node') ``` -## argv +## ps() + +The [@webpod/ps](https://github.com/webpod/ps) package to provide a cross-platform way to list processes. + +```js +const all = await ps.lookup() +const nodejs = await ps.lookup({ command: 'node' }) +const children = await ps.tree({ pid: 123 }) +const fulltree = await ps.tree({ pid: 123, recursive: true }) +``` + +## kill() + +A process killer. + +```js +await kill(123) +await kill(123, 'SIGKILL') +``` + +## tmpdir() + +Creates a temporary directory. + +```js +t1 = tmpdir() // /os/based/tmp/zx-1ra1iofojgg/ +t2 = tmpdir('foo') // /os/based/tmp/zx-1ra1iofojgg/foo/ +``` + +## tmpfile() + +Temp file factory. + +```js +f1 = tmpfile() // /os/based/tmp/zx-1ra1iofojgg +f2 = tmpfile('f.txt') // /os/based/tmp/zx-1ra1iofojgg/foo.txt +f3 = tmpfile('f.txt', 'string or buffer') +``` + +## minimist The [minimist](https://www.npmjs.com/package/minimist) package. +```js +const argv = minimist(process.argv.slice(2), {}) +``` + +## argv + A minimist-parsed version of the `process.argv` as `argv`. ```js diff --git a/cli.md b/cli.md index 8dda69ecdc..27c3259b5d 100644 --- a/cli.md +++ b/cli.md @@ -67,6 +67,46 @@ the import. import sh from 'tinysh' // @^1 ``` +## --quiet + +Suppress any outputs. + +## --verbose + +Enable verbose mode. + +## --shell + +Specify a custom shell binary. + +```bash +zx --shell=/bin/bash script.mjs +``` + +## --prefix & --postfix + +Attach a command to the beginning or the end of every command. + +```bash +zx --prefix='echo foo;' --postfix='; echo bar' script.mjs +``` + +## --cwd + +Set the current working directory. + +```bash +zx --cwd=/foo/bar script.mjs +``` + +## --version + +Print the current version of `zx`. + +## --help + +Print help. + ## __filename & __dirname In [ESM](https://nodejs.org/api/esm.html) modules, Node.js does not provide diff --git a/configuration.md b/configuration.md index ad5b968543..4e9da25eb9 100644 --- a/configuration.md +++ b/configuration.md @@ -14,6 +14,8 @@ Or use a CLI argument: `--shell=/bin/bash` Specifies a `spawn` api. Defaults to `require('child_process').spawn`. +To override a sync API implementation, set `$.spawnSync` correspondingly. + ## $.prefix Specifies the command that will be prefixed to all commands run. @@ -22,6 +24,14 @@ Default is `set -euo pipefail;`. Or use a CLI argument: `--prefix='set -e;'` +## $.postfix + +Like a `$.prefix`, but for the end of the command. + +```js +$.postfix = '; exit $LastExitCode' // for PowerShell compatibility +``` + ## $.quote Specifies a function for escaping special characters during @@ -29,12 +39,18 @@ command substitution. ## $.verbose -Specifies verbosity. Default is `true`. +Specifies verbosity. Default is `false`. In verbose mode, `zx` prints all executed commands alongside with their outputs. -Or use the CLI argument `--quiet` to set `$.verbose = false`. +Or use the CLI argument: `--verbose` to set `true`. + +## $.quiet + +Suppresses all output. Default is `false`. + +Via CLI argument: `--quiet` sets `$.quiet = true`. ## $.env diff --git a/contribution.md b/contribution.md new file mode 100644 index 0000000000..5cf90b8fd7 --- /dev/null +++ b/contribution.md @@ -0,0 +1,52 @@ +# Contribution Guide + +zx is a fully [open-source project](https://github.com/google/zx), which is developing by the community for the community. +We welcome contributions of any kind, including but not limited to: +* Bug reports +* Feature requests +* Code contributions +* Documentation improvements +* Discussions + +## Community Guidelines + +This project follows [Google's Open Source Community Guidelines](https://opensource.google/conduct/). +In short: all contributors are treated with respect and fairness. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution; +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## How to Contribute +Before proposing changes, look for similar ones in the project's [issues](https://github.com/google/zx/issues) and [pull requests](https://github.com/google/zx/pulls). If you can't decide, create a new [discussion](https://github.com/google/zx/discussions) topic, and we will help you figure it out. When ready to move on: +* Prepare your development environment. + * Ensure you have Node.js 20+ installed. + * Bash is essential for running zx scripts. Linux and macOS users usually have it installed by default. Consider using [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install) or [Git Bash](https://git-scm.com/downloads) if you are on Windows. +* Fork [the repository](https://github.com/google/zx). +* Create a new branch. +* Make your changes. + * If you are adding a new feature, please include additional tests. The coverage threshold is 98%. + * Create a [conventional-commits](https://www.conventionalcommits.org/en/v1.0.0/) compliant messages. +* Ensure that everything is working: + * `npm run fmt` to format your code. + * `npm run cov` to run the tests. +* Push the changes to your fork. +* Create a pull request. + * Describe your changes in detail. + * Reference any related issues if applicable. + +## Code Reviews + +All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on using pull requests. + +## License + +The project is licensed under the [Apache-2.0](https://github.com/google/zx?tab=Apache-2.0-1-ov-file#readme) diff --git a/getting-started.md b/getting-started.md index 7adc04cb6c..65cb8ec6f3 100644 --- a/getting-started.md +++ b/getting-started.md @@ -35,6 +35,10 @@ gives sensible defaults. npm install zx ``` +```bash [bun] +bun install zx +``` + ```bash [deno] deno install -A npm:zx ``` @@ -82,7 +86,12 @@ import 'zx/globals' ### ``$`command` `` Executes a given command using the `spawn` func -and returns [`ProcessPromise`](process-promise.md). +and returns [`ProcessPromise`](process-promise.md). It supports both sync and async modes. + +```js +const list = await $`ls -la` +const dir = $.sync`pwd` +``` Everything passed through `${...}` will be automatically escaped and quoted. diff --git a/migration-from-v7.md b/migration-from-v7.md new file mode 100644 index 0000000000..01370ae9c4 --- /dev/null +++ b/migration-from-v7.md @@ -0,0 +1,44 @@ +# Migration from v7 to v8 + +[v8.0.0 release](https://github.com/google/zx/releases/tag/8.0.0) brought many features, improvements and fixes, but also has introduced a few breaking changes. + +1. `$.verbose` is set to `false` by default, but errors are still printed to `stderr`. Set `$.quiet = true` to suppress any output. +```js +$.verbose = true // everything works like in v7 + +$.quiet = true // to completely turn off logging +``` + +2. `ssh` API was dropped. Install [webpod](https://github.com/webpod/webpod) package instead. +```js +// import {ssh} from 'zx' ↓ +import {ssh} from 'webpod' + +const remote = ssh('user@host') +await remote`echo foo` +``` + +3. zx is not looking for `PowerShell` anymore, on Windows by default. If you still need it, use the `usePowerShell` helper to enable: + +```js +import { usePowerShell, useBash } from 'zx' + +usePowerShell() // to enable powershell +useBash() // switch to bash, the default +``` + +To look for modern [PowerShell v7+](https://github.com/google/zx/pull/790), invoke `usePwsh()` helper instead: + +```js +import { usePwsh } from 'zx' + +usePwsh() +``` + +4. Process cwd synchronization between `$` invocations is now disabled by default. This functionality is provided via an async hook and can now be controlled directly. + +```js +import { syncProcessCwd } from 'zx' + +syncProcessCwd() // restores legacy v7 behavior +``` diff --git a/process-output.md b/process-output.md new file mode 100644 index 0000000000..1160cc7b46 --- /dev/null +++ b/process-output.md @@ -0,0 +1,25 @@ +# Process Output + +Represents a cmd execution result. + +```ts +interface ProcessOutput { + // Exit code of the process: 0 for success, non-zero for failure + exitCode: number + + // Signal that caused the process to exit: SIGTERM, SIGKILL, etc. + signal: NodeJS.Signals | null + + // Holds the stdout of the process + stdout: string + + // Process errors are written to stderr + stderr: string + + // combined stdout and stderr + toString(): string + + // Same as toString() but trimmed + valueOf(): string +} +``` diff --git a/process-promise.md b/process-promise.md index d355511127..63722add8f 100644 --- a/process-promise.md +++ b/process-promise.md @@ -1,11 +1,11 @@ # Process Promise -The `$` returns a `ProcessPromise` instance. +The `$` returns a `ProcessPromise` instance. When resolved, it becomes a [`ProcessOutput`](./process-output.md). ```js -const p = $`command` +const p = $`command` // ProcessPromise -await p +const o = await p // ProcessOutput ``` ## `stdin` diff --git a/setup.md b/setup.md new file mode 100644 index 0000000000..7103f1ef89 --- /dev/null +++ b/setup.md @@ -0,0 +1,87 @@ +# Setup + +## Requirements +* Linux, macOS, or Windows +* JavaScript Runtime: + * Node.js 12.17.0 or later + * Bun 1.0.0 or later + * Deno 1.x +* Some kind of bash or PowerShell + +## Install + +::: code-group + +```bash [node] +npm install zx +``` + +```bash [bun] +bun install zx +``` + +```bash [deno] +deno install -A npm:zx + +# zx requires additional permissions: --allow-read --allow-sys --allow-env --allow-run +``` + +```bash [brew] +brew install zx +``` + +::: + +Dev snapshot versions are published to npm under the [`dev` tag](https://www.npmjs.com/package/zx?activeTab=versions): `npm i zx@dev`. + +## Bash + +zx mostly relies on bash, so make sure it's available in your environment. If you're on Windows, consider using [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install) or [Git Bash](https://git-scm.com/downloads). +By default, zx looks for bash binary, but you can switch to PowerShell by invoking `usePowerShell()` or `usePwsh()`. + +```js +import { useBash, usePowerShell, usePwsh } from 'zx' + +usePowerShell() // Use PowerShell.exe +usePwsh() // Rely on pwsh binary (PowerShell v7+) +useBash() // Switch back to bash +``` + +## Package + +### Hybrid +zx is distributed as a [hybrid package](https://2ality.com/2019/10/hybrid-npm-packages.html): it provides both CJS an ESM entry points. + +```js +import { $ } from 'zx' + +const { $ } = require('zx') +``` + +It has also built-in TypeScript libdefs. + +```ts +import { type Options } from 'zx' + +const opts: Options = { + quiet: true, + timeout: '5s' +} +``` + +### Bundled + +We use [esbuild](https://dev.to/antongolub/how-and-why-do-we-bundle-zx-1ca6) to produce a static build that allows us to solve several issues at once: +* Reduce the pkg size and install time. +* Make npx (yarn dlx / bunx) invocations reproducible. +* Provide support for wide range of Node.js versions: from 12 to 22. +* Make auditing easier: complete code in one place. + +### Composite + +zx exports several entry points adapted for different use cases: +* `zx` – the main entry point, provides all the features. +* `zx/global` – to populate the global scope with zx functions. +* `zx/cli` – to run zx scripts from the command line. +* `zx/core` – to use zx template spawner as part of 3rd party libraries with alternating set of utilities. + diff --git a/typescript.md b/typescript.md index 83a08c2ba7..dd1b62f590 100644 --- a/typescript.md +++ b/typescript.md @@ -1,28 +1,59 @@ # TypeScript -Configure your project to use [ES modules](https://nodejs.org/api/packages.html#packages_type): +zx is written in TypeScript and provides the corresponding libdefs out of box. Typings are TS 4+ compatible. + +```ts +// script.ts +import { $ } from 'zx' + +const list = await $`ls -la` +``` + +Some runtimes like [Bun](https://bun.sh/) or [Deno](https://deno.com/) have built-in TS support. Node.js requires additional setup. Configure your project according to the [ES modules contract](https://nodejs.org/api/packages.html#packages_type): - Set [`"type": "module"`](https://nodejs.org/api/packages.html#packages_type) in **package.json** - Set [`"module": "ESNext"`](https://www.typescriptlang.org/tsconfig/#module) in **tsconfig.json**. -It is possible to make use of `$` and other functions via explicit imports: +Using TypeScript compiler is the most straightforward way. -```ts -import { $ } from 'zx' +::: code-group + +```bash [tsc] +npm install typescript + +tsc script.ts + +node script.js ``` -Or import globals explicitly: +```bash [ts-node] +npm install ts-node -```ts -import 'zx/globals' +ts-node script.ts +# or via node loader +node --loader ts-node/esm script.ts ``` -Wrap your code in an async function and call it immediately: +```bash [swc-node] +npm install swc-node -```ts -void async function () { - await $`ls -la` -}() +swc-node script.ts +``` + +```bash [tsx] +npm install tsx + +tsx script.ts ``` + +```bash [bun] +bun script.ts +``` + +```bash [deno] +deno run --allow-read --allow-sys --allow-env --allow-run script.ts +``` + +:::