Skip to content

Node streams

Eugene Lazutkin edited this page Jun 1, 2026 · 3 revisions

A Node-streams facade for dollar-shell. It exposes the same API as the main entry, except the standard streams are Node streams instead of web streams.

Reach for it when you want to pipe a process straight into Node's ecosystem (fs, zlib, readline, …) without a web↔Node adapter, or to skip the web-stream conversion entirely.

Usage

import {$, $$, $sh, shell, sh, spawn} from 'dollar-shell/node';

$ is the default export, so import $ from 'dollar-shell/node' works too.

The entry always uses the Node backend (node:child_process). On Bun and Deno it runs through their Node compatibility layer, so it works there as well — process spawning still needs the usual permissions (e.g., Deno's --allow-run).

Docs

Everything is identical to the main dollar-shell API — $, $$, $sh, shell / sh, spawn(), and all the utilities — with $/$$/$sh/shell carrying the same .from, .to, .through/.io helpers.

The single difference is the stream type on the sub-process object:

import type {Readable, Writable} from 'node:stream';

interface DuplexPair {
  readable: Readable;
  writable: Writable;
}

interface Subprocess {
  readonly command: string[];
  readonly options: SpawnOptions | undefined;
  readonly exited: Promise<number>;
  readonly finished: boolean;
  readonly killed: boolean;
  readonly exitCode: number | null;
  readonly signalCode: string | null;
  readonly stdin: Writable | null; // a Node Writable (was a WritableStream)
  readonly stdout: Readable | null; // a Node Readable (was a ReadableStream)
  readonly stderr: Readable | null; // a Node Readable (was a ReadableStream)
  readonly asDuplex: DuplexPair;
  kill(): void;
}

Correspondingly, $.from / $sh.from return a Node Readable, $.to / $sh.to return a Node Writable, and $.io/$.through (and the $sh equivalents) return a {readable, writable} pair of Node streams.

SpawnOptions, ShellOptions, DollarResult, and the rest of the non-stream types are exactly the same as the main entry. The TypeScript declarations live in src/node/index.d.ts.

Examples

import {spawn} from 'dollar-shell/node';
import {createWriteStream} from 'node:fs';

// Pipe a process straight into a file with Node streams — no adapter.
const sp = spawn(['ls', '-l'], {stdout: 'pipe'});
sp.stdout.pipe(createWriteStream('listing.txt'));
await sp.exited;
import {$} from 'dollar-shell/node';

// `$.from` is a Node Readable here.
const lines = $.from`ls -l`;
lines.setEncoding('utf8');
for await (const chunk of lines) process.stdout.write(chunk);

See also

  • Cross-runtime notes — how Node, Bun, and Deno differ, and the related DSH_FORCE_NODE flag, which forces the Node backend but keeps the web-streams API. (This page is the other half: always Node backend, Node streams.)

Clone this wiki locally