mrkeyoor.com_
Fri 16 Oct 18:54 UTC
Dev Toolsevaluationupdated 04 Oct 2026

zx review

zx lets you write shell automation in JavaScript or TypeScript while keeping commands visible as shell commands. It adds argument escaping, promises, process output helpers, and normal JavaScript control flow to scripts that have outgrown plain Bash.

+5stars / 7d
Verdict

Our run of zx installed 531 packages in 14 seconds and built in 19 seconds, but its test command failed on Node 22 before unit tests ran. Use it when JavaScript control flow will make a growing shell script easier to own, and pin both zx and the runtime in CI. Skip it for tiny automation images or any workflow that cannot test its chosen shell on every target platform.

We ran it

Lab card: what happened when we ran zxScreenshot of zx (google.github.io/zx)
Install✓ · 14s531 packages · 247 MB
Build✓ · 19s
Tests✗ · 21sran, no count parsed
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)
Repo180 files~36,772 lines of source · 1.8 MB · 8 CI workflows · tests dir

Answers from our run

Does zx build from source?

Dependencies installed in 14 seconds (531 packages), and the build succeeded in 19 seconds. We cloned commit 65fc542 into a clean Debian container with 3 CPUs and no project-specific setup.

Do zx's tests pass?

The test command failed in our container, and its output did not report a pass or fail count.

Does zx have known vulnerabilities in its dependencies?

npm audit found none in the dependency tree at the time of our run.

Who should not use zx?

Contributors who require a green suite on Node 22: our test command stopped with exit 9 because Node rejected --experimental-transform-types.

What are the alternatives to zx?

Execa, ShellJS, dax. Our run of zx installed 531 packages in 14 seconds and built in 19 seconds, but its test command failed on Node 22 before unit tests ran.

Setup3/514-second install and 19-second build, but Node 22 tests stopped
Docs5/5Separate guides cover shells, quoting, output, TypeScript, and setup
Community5/520 open issues, 46 open PRs, and October 2026 activity
Maturity4/5Version 8 API is established, with current edge cases still open

Discussed on

  1. hnJavaScript for Shell Scripting385 points
  2. hnZx 3.0189 points
  3. hnZX – A tool for writing better scripts162 points
  4. hnZx: A tool for writing better scritps4 points
  5. hnGoogle/Zx v8.13 points

Who it’s for

JavaScript or TypeScript teams whose release and maintenance scripts need loops, structured data, or parallel work.
Developers who want shell commands to return stdout, stderr, exit status, and JSON through a small process API.
Cross-platform teams willing to choose Bash or PowerShell explicitly and test each supported operating system.
CI maintainers who prefer automation code to use the same package manager and types as the application.

Who it’s NOT for

Contributors who require a green suite on Node 22: our test command stopped with exit 9 because Node rejected --experimental-transform-types.
Locked-down machines without Bash or PowerShell: the setup guide requires one of those shells even when zx runs through Node, Bun, Deno, or GraalVM.
Teams expecting --install to behave like a fully managed dependency lock: open issue #525 documents duplicate installs, awkward version control, and monorepo resolution problems.
Code that passes untrusted names into tempfile or prefixes into tempdir: open issue #1519 reports path traversal in zx 8.9.0, and its fix was still an open pull request on October 4, 2026.
Minimal CI images where a 531-package, 247 MB installed tree is too much for a scripting helper.

Setup reality

Our sandbox installed commit 65fc542 in 14 seconds, adding 531 packages and using 247 MB. The build passed in 19 seconds. Tests failed with exit 9 after 21 seconds when Node rejected --experimental-transform-types; npm audit found 0 known vulnerabilities.

zx itself needs no account, secret, database, or hosted service. It does need a JavaScript runtime and either Bash or PowerShell. Commands inside your script may still need their own binaries, credentials, permissions, and environment variables.

The README lists Linux, macOS, and Windows plus Node, Bun, Deno, and GraalVM Node.js. Deno requires read, system, environment, and process-run permissions. TypeScript on Node needs extra setup, while the default command shell remains Bash unless you switch it.

Version 8.9.0 puts JavaScript around visible shell commands

At commit 65fc542, zx declared version 8.9.0 and supported Node.js 12.17 or newer, Bun 1.0 or newer, Deno 1.x and 2.x, and GraalVM Node.js. Its $ tagged template sends a command to a shell while escaping interpolated values. You keep the familiar command line, then use JavaScript for arrays, conditions, error handling, and parallel jobs. That is a sensible step when a twenty-line Bash script has started behaving like an application.

Each command returns a ProcessPromise, which resolves to a ProcessOutput with stdout, stderr, and the exit code. The API can return text, lines, buffers, or parsed JSON, and its pipes accept another command, a writable stream, or a file path. Ordinary Promise.all runs independent commands together. zx earns its place when those features remove homemade process wrappers; a short sequence of fixed commands may still be clearer in a package script.

What happened when we ran it

Our measurement setup cloned commit 65fc542, a 1.8 MB checkout with 180 files and about 36,772 lines of source. On 3 CPUs and 8 GB of RAM, npm installed 531 packages in 14 seconds and occupied 247 MB. The build succeeded in another 19 seconds. We used the lab-node:22 image in an unprivileged Debian container with no secrets, so these figures describe repository setup rather than command speed.

The test command failed with exit 9 after 21 seconds. Its size check passed at 944.52 kB against a 944.6 kB limit, and every checked file matched the Prettier style. The next step invoked node --experimental-transform-types ./test/all.test.js; Node printed bad option: --experimental-transform-types. The log shows no unit-test results, so claiming a test count or blaming a particular test would go beyond the evidence.

On our box, npm audit reported 0 known vulnerabilities across critical, high, moderate, and low severities. The repository contains 8 CI workflow files and a tests directory, though it has no top-level Dockerfile. Those signals show that maintainers have automated checks. They do not turn our failed Node 22 run green. A contributor using the measured commit should pin a runtime that accepts the repository's test command or expect the same stop before unit tests begin.

Bash or PowerShell remains required across four runtimes

zx 8.9.0 lists four runtime families, yet its setup guide still requires some form of Bash or PowerShell. Bash is the default. Windows users can choose WSL, Git Bash, Windows PowerShell, or PowerShell 7 through the supplied helpers. That choice controls the commands and quoting rules below zx, so support for Windows does not make grep, Unix paths, or Bash syntax portable to PowerShell by itself.

Deno installation asks for read, system, environment, and process-run permissions. TypeScript definitions ship with the package, while Node users need a compiler, loader, or native type-stripping route and the documented module settings. The setup guide also tells TypeScript consumers to install Node and fs-extra type packages. None of this is difficult, but it makes the runtime, module format, shell, and script dependencies part of the deployment contract.

Escaped arguments help, while remote execution raises the trust bar

Version 8.9.0 escapes values inserted through ${...} and treats array items as separate arguments. This is safer than building one command string from raw input, provided executable syntax stays outside the interpolation. The CLI can also execute a script directly from an HTTPS URL and can install missing imports with --install. Both are convenient on a trusted workstation. In CI, pin the source and dependency versions because those modes fetch code during execution.

Open issue #525 explains why --install is a poor substitute for a normal locked dependency setup. The report says it may install a dependency even when the project already has it, puts versions in import comments that update tools may miss, and handles workspace packages incorrectly. The issue remained open after an update on September 25, 2026. Use the flag for disposable scripts, not for a release job that must reproduce the same dependency graph months later.

An open 8.9.0 temp-path report needs attention

zx 8.9.0 has an open report that tempfile(name) and tempdir(prefix) can accept path segments outside the intended temporary directory. Issue #1519 was filed October 2, 2026, after the reporter demonstrated the behavior and warned about callers that pass download names, command-line input, or archive entries. Pull request #1520 proposed a fix and was still open on October 4. This is a user report, not a result from our sandbox.

The practical boundary is narrow: do not send untrusted filenames or prefixes into those helpers while the report remains unresolved. Many zx scripts never call either function, so the issue does not condemn the whole process API. It does matter for automation that downloads or unpacks user-controlled material under a service account. Check the issue and the released code you plan to deploy instead of assuming a pending pull request is already protection.

August code and October issue work show active maintenance

GitHub listed 45,776 stars, 20 open issues, and 46 open pull requests on October 4, 2026. The last repository push was August 14, and issue and pull-request activity continued into October. The latest tagged release was 8.8.5 from October 19, 2025, while the measured commit identified itself as 8.9.0. That older release date alone is weak evidence of project health because code and triage continued afterward.

zx is easiest to recommend when a script already needs promises, structured output, or shared TypeScript code. Our 14-second install and 19-second build make evaluation cheap, but the 531-package tree and Node 22 test stop belong in the decision. Pin the runtime, keep interpolated values separate from shell syntax, and run the real scripts on every shell you claim to support. If those conditions sound excessive, Execa or a small package script is probably the cleaner fit.

Alternatives

ProjectWhat it isPick it when
ExecaA Node.js subprocess library with an explicit API for commands, streams, cancellation, and errors.pick this instead when process execution lives inside an application and a shell-script style would hide too much control flow.
ShellJSPortable Unix-style commands exposed as JavaScript methods.pick this instead when you want file and shell utilities without writing command strings for an external shell.
daxA cross-platform shell library for Deno and Node.js inspired by zx.pick this instead when Deno is your primary runtime or its command builder fits your portability needs better.

What people are saying

  1. [hackernews] A graphical desktop for the ZX Spectrum
  2. [hackernews] ZX Spectrum: Experimenting with 1-Bit Sound

Sources

  1. google/zx repository
  2. zx setup guide
  3. zx process promise guide
  4. zx CLI guide
  5. Open issue 525: Improve --install
  6. Open issue 1519: tempfile and tempdir path traversal report
  7. zx 8.8.5 release

More dev tools reviews

compose-samples · librepods · sqlmap · coucou · world-execute-me-dsh-pv · mcp-extensions · the whole board →