-
-
Notifications
You must be signed in to change notification settings - Fork 0
Node streams
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.
import {$, $$, $sh, shell, sh, spawn} from 'dollar-shell/node';$ is the default export, so import $ from 'dollar-shell/node' works too.
The entry always spawns through 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). Only the spawn mechanism is Node's: the runtime that executes your scripts and how dollar-shell re-launches it stay native (a child of Bun/Deno is still bun run … / deno run …), exactly as with the DSH_FORCE_NODE flag.
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, Duplex} from 'node:stream';
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: Duplex;
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 Node Duplex — so a process drops straight into a .pipe() chain or stream.pipeline() as a transform step. (On the main, web-streams entry these return a {readable, writable} pair, the shape ReadableStream.pipeThrough() consumes.)
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.
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);import {$} from 'dollar-shell/node';
import {pipeline} from 'node:stream/promises';
import {createReadStream, createWriteStream} from 'node:fs';
// `$.io` is a Node Duplex, so it slots into a pipeline as a transform step.
await pipeline(
createReadStream('in.txt'),
$.io`grep error`,
createWriteStream('out.txt')
);-
Cross-runtime notes — how Node, Bun, and Deno differ, and the related
DSH_FORCE_NODEflag, which forces the Node backend but keeps the web-streams API. (This page is the other half: always Node backend, Node streams.)