This release enhances the coherence between the ProcessPromise and the Streams API, eliminating the need for certain script-level workarounds.
✨ New Features
unpipe()
— Selectively stop piping
You can now call .unpipe()
to stop data transfer from a source to a destination without closing any of the pair. #1302
const p1 = $`echo foo && sleep 0.1 && echo bar && sleep 0.1 && echo baz && sleep 0.1 && echo qux`
const p2 = $`echo 1 && sleep 0.15 && echo 2 && sleep 0.1 && echo 3`
const p3 = $`cat`
p1.pipe(p3)
p2.pipe(p3)
setTimeout(() => p1.unpipe(p3), 150)
const { stdout } = await p3
// 'foo\n1\nbar\n2\n3\n'
Many-to-one piping
Multiple sources can now stream into a single destination. All sources complete before the destination closes. #1300
const $h = $({ halt: true })
const p1 = $`echo foo`
const p2 = $h`echo a && sleep 0.1 && echo c && sleep 0.2 && echo e`
const p3 = $h`sleep 0.05 && echo b && sleep 0.1 && echo d`
const p4 = $`sleep 0.4 && echo bar`
const p5 = $h`cat`
await p1
p1.pipe(p5)
p2.pipe(p5)
p3.pipe(p5)
p4.pipe(p5)
const { stdout } = await p5.run()
// 'foo\na\nb\nc\nd\ne\nbar\n'
Piping from rejected processes
Processes that exit with errors can now still pipe their output. The internal recorder retains their stream, status, and exit code. #1296
const p1 = $({ nothrow: true })`echo foo && exit 1`
await p1
const p2 = p1.pipe($({ nothrow: true })`cat`)
await p2
p1.output.toString() // 'foo\n'
p1.output.ok // false
p1.output.exitCode // 1
p2.output.toString() // 'foo\n'
p2.output.ok // false
p2.output.exitCode // 1
Components versions
Since zx bundles third-party libraries without their package.jsons, their versions weren’t previously visible. You can now access them via the versions
static map — including zx itself. #1298 #1295
import { versions } from 'zx'
versions.zx // 8.7.2
versions.chalk // 5.4.1