dollar-shell 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/llms-full.txt +4 -2
- package/llms.txt +2 -2
- package/package.json +2 -2
- package/src/index.js +18 -8
- package/src/node/index.js +17 -4
package/README.md
CHANGED
|
@@ -223,9 +223,11 @@ The rest is identical to `$`: `$sh`, `$sh.from`, `$sh.to` and `$sh.io`/`$sh.thro
|
|
|
223
223
|
|
|
224
224
|
Each runtime uses its own backend by default (`node:child_process` on Node, `Bun.spawn` on Bun,
|
|
225
225
|
`Deno.Command` on Deno). Set the **`DSH_FORCE_NODE` environment variable** (e.g. `DSH_FORCE_NODE=1`) to
|
|
226
|
-
|
|
227
|
-
compatibility layer. This
|
|
228
|
-
|
|
226
|
+
make every runtime spawn through the Node backend — Bun and Deno then run `node:child_process` on
|
|
227
|
+
their compatibility layer. This swaps **only the spawn mechanism**: the runtime that runs your code and how
|
|
228
|
+
it's re-launched stay native, so a forced child of Bun/Deno is still `bun run …` / `deno run …`, never a
|
|
229
|
+
bare `node`. Handy for sidestepping runtime-specific quirks (e.g. Bun intermittently dropping the last
|
|
230
|
+
chunk of a child's piped output).
|
|
229
231
|
|
|
230
232
|
Because dollar-shell spawns children with `env` defaulting to `process.env`, the variable is inherited by
|
|
231
233
|
those children — so it forces the Node backend across the whole process tree. To force **only the
|
|
@@ -248,8 +250,8 @@ sp.stdout.pipe(process.stdout); // sp.stdout is a Node Readable
|
|
|
248
250
|
|
|
249
251
|
The API is identical to the main entry — same `$`, `$$`, `$sh`, `shell`, helpers, and
|
|
250
252
|
`.from`/`.to`/`.through`/`.io` — only the stream types differ (`stdin` is a Node `Writable`, `stdout`/`stderr`
|
|
251
|
-
are Node `Readable`s, and `asDuplex` / `.io` / `.through` return a Node `Duplex`). It always
|
|
252
|
-
`node:child_process` compatibility layer.
|
|
253
|
+
are Node `Readable`s, and `asDuplex` / `.io` / `.through` return a Node `Duplex`). It always spawns through the Node backend, so it also runs on Bun and Deno through their
|
|
254
|
+
`node:child_process` compatibility layer (only the spawn mechanism changes — the runtime launch stays native).
|
|
253
255
|
|
|
254
256
|
## For AI Agents
|
|
255
257
|
|
|
@@ -269,6 +271,7 @@ BSD-3-Clause
|
|
|
269
271
|
|
|
270
272
|
## Release History
|
|
271
273
|
|
|
274
|
+
- 1.2.1 _Bugfix: `DSH_FORCE_NODE` and `dollar-shell/node` now switch only the spawn mechanism — spawned children stay native (`bun run …` / `deno run …`)._
|
|
272
275
|
- 1.2.0 _Added `dollar-shell/node` with Node streams and a `DSH_FORCE_NODE` flag to force the Node backend on any runtime._
|
|
273
276
|
- 1.1.14 _Fixed Bun stdin abort path, added js-check, Bun + Deno wired into CI._
|
|
274
277
|
- 1.1.13 _Updated dev dependencies._
|
package/llms-full.txt
CHANGED
|
@@ -318,12 +318,14 @@ Works on Node.js, Deno, and Bun. The appropriate spawn implementation is selecte
|
|
|
318
318
|
|
|
319
319
|
### Forcing the Node backend
|
|
320
320
|
|
|
321
|
-
By default each runtime uses its own backend (`node:child_process` on Node, `Bun.spawn` on Bun, `Deno.Command` on Deno).
|
|
321
|
+
By default each runtime uses its own backend (`node:child_process` on Node, `Bun.spawn` on Bun, `Deno.Command` on Deno). Make every runtime spawn through the Node backend — so Bun and Deno run `node:child_process` on their compatibility layer — with the **`DSH_FORCE_NODE` environment variable** (any value except `''`, `0`, `false`):
|
|
322
322
|
|
|
323
323
|
```bash
|
|
324
324
|
DSH_FORCE_NODE=1 node app.js
|
|
325
325
|
```
|
|
326
326
|
|
|
327
|
+
This swaps **only the spawn mechanism**. The runtime that executes your scripts and the way dollar-shell re-launches the current runtime (`currentExecPath` / `runFileArgs` / `cwd`) stay native — a forced child of Bun or Deno is still launched as `bun run …` / `deno run …`, never a bare `node`.
|
|
328
|
+
|
|
327
329
|
dollar-shell's `env` option defaults to `process.env`, so spawned children inherit the variable — the environment variable therefore forces the Node backend across the **whole process tree** (this process and every dollar-shell child it spawns), which is usually what you want for a build script or wrapper.
|
|
328
330
|
|
|
329
331
|
To force **only the current process** without leaking into the children it spawns, set the process-local flag instead. It requires a dynamic `import()`, because the backend is chosen once, when the module first loads:
|
|
@@ -345,4 +347,4 @@ The package also ships a Node-streams facade at `dollar-shell/node`:
|
|
|
345
347
|
import {$, $$, $sh, shell, sh, spawn} from 'dollar-shell/node';
|
|
346
348
|
```
|
|
347
349
|
|
|
348
|
-
The API is identical to the main entry, except the streams are Node streams (from `node:stream`) instead of Web streams: `stdin`/`stdout`/`stderr` are a Node `Writable`/`Readable`, and `.io`/`.through`/`asDuplex` return a Node `Duplex` (so a process drops straight into a `.pipe()` chain or `stream.pipeline()`). This is convenient for direct Node-ecosystem interop — pipe straight into `fs`/`zlib`/etc. with no Web↔Node adapter, and skip the conversion. It always
|
|
350
|
+
The API is identical to the main entry, except the streams are Node streams (from `node:stream`) instead of Web streams: `stdin`/`stdout`/`stderr` are a Node `Writable`/`Readable`, and `.io`/`.through`/`asDuplex` return a Node `Duplex` (so a process drops straight into a `.pipe()` chain or `stream.pipeline()`). This is convenient for direct Node-ecosystem interop — pipe straight into `fs`/`zlib`/etc. with no Web↔Node adapter, and skip the conversion. It always spawns through the Node backend, so on Bun and Deno it runs `node:child_process` through their compatibility layer; like `DSH_FORCE_NODE`, this changes only the spawn mechanism — the runtime that executes your scripts and how it is re-launched stay native. Type declarations live in `src/node/index.d.ts`.
|
package/llms.txt
CHANGED
|
@@ -65,11 +65,11 @@ await $sh({stdout: 'inherit'})`echo ${raw('"hello"')}`;
|
|
|
65
65
|
|
|
66
66
|
## Forcing the Node backend
|
|
67
67
|
|
|
68
|
-
By default each runtime uses its native backend. Set the `DSH_FORCE_NODE` environment variable (e.g. `DSH_FORCE_NODE=1`) to
|
|
68
|
+
By default each runtime uses its native backend. Set the `DSH_FORCE_NODE` environment variable (e.g. `DSH_FORCE_NODE=1`) to make every runtime spawn through the Node backend (`node:child_process`; Bun and Deno run it on their Node compat). This swaps **only the spawn mechanism** — the runtime that runs your code and how dollar-shell re-launches it stay native, so a forced child of Bun/Deno is still `bun run …` / `deno run …`, never a bare `node`. Because dollar-shell's `env` option defaults to `process.env`, spawned children inherit the variable, so it forces the whole process tree; to force only the current process (no leak to children), set `globalThis.DSH_FORCE_NODE = true` before a dynamic `import()` instead (process-local). Useful to sidestep runtime-specific quirks. The backend is selected once, at import time. Treated as off: unset, `''`, `0`, `false`.
|
|
69
69
|
|
|
70
70
|
## Node streams (`dollar-shell/node`)
|
|
71
71
|
|
|
72
|
-
`import {$, $$, $sh, shell, spawn} from 'dollar-shell/node'` gives the identical API, but `stdin`/`stdout`/`stderr` are Node `Readable`/`Writable` (and `.io`/`.through`/`asDuplex` a Node `Duplex`) instead of Web streams — for direct Node-ecosystem piping with no adapter. Always
|
|
72
|
+
`import {$, $$, $sh, shell, spawn} from 'dollar-shell/node'` gives the identical API, but `stdin`/`stdout`/`stderr` are Node `Readable`/`Writable` (and `.io`/`.through`/`asDuplex` a Node `Duplex`) instead of Web streams — for direct Node-ecosystem piping with no adapter. Always spawns through the Node backend (`node:child_process`, run on Bun/Deno via their Node compat); only the spawn mechanism changes — the runtime launch stays native.
|
|
73
73
|
|
|
74
74
|
## Docs
|
|
75
75
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dollar-shell",
|
|
3
3
|
"description": "Run OS and shell commands using template tag functions. Same API in Node, Deno, and Bun. Web streams, TypeScript typings, zero dependencies.",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"types": "./src/index.d.ts",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"@types/deno": "^2.7.0",
|
|
78
78
|
"@types/node": "^25.9.1",
|
|
79
79
|
"prettier": "^3.8.3",
|
|
80
|
-
"tape-six": "^1.10.
|
|
80
|
+
"tape-six": "^1.10.1",
|
|
81
81
|
"typescript": "^6.0.3"
|
|
82
82
|
},
|
|
83
83
|
"tape6": {
|
package/src/index.js
CHANGED
|
@@ -35,16 +35,21 @@ const envForceNode = () => {
|
|
|
35
35
|
const forceNode =
|
|
36
36
|
isFlagOn(/** @type {any} */ (globalThis).DSH_FORCE_NODE) || isFlagOn(envForceNode());
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
// The runtime-native backend defines how to launch *this* runtime — `currentExecPath`,
|
|
39
|
+
// `runFileArgs`, `cwd`. DSH_FORCE_NODE forces only the spawn *implementation*
|
|
40
|
+
// (`node:child_process`); it must not change which runtime we target or how it is
|
|
41
|
+
// invoked, so a forced child of Bun/Deno is still `bun run …` / `deno run …`, never a
|
|
42
|
+
// bare `node`. (`process.execPath` is already the real runtime under Bun/Deno node
|
|
43
|
+
// compat; `runFileArgs` is what actually differs — `[]` for node vs `['run']`.)
|
|
44
|
+
let modRuntime;
|
|
45
|
+
if (typeof Deno !== 'undefined') {
|
|
46
|
+
modRuntime = await import('./spawn/deno.js');
|
|
43
47
|
} else if (typeof Bun !== 'undefined') {
|
|
44
|
-
|
|
48
|
+
modRuntime = await import('./spawn/bun.js');
|
|
45
49
|
} else {
|
|
46
|
-
|
|
50
|
+
modRuntime = await import('./spawn/node.js');
|
|
47
51
|
}
|
|
52
|
+
const modSpawn = forceNode ? await import('./spawn/node.js') : modRuntime;
|
|
48
53
|
|
|
49
54
|
export const {
|
|
50
55
|
spawn,
|
|
@@ -59,6 +64,11 @@ export const {
|
|
|
59
64
|
shell,
|
|
60
65
|
sh,
|
|
61
66
|
$sh
|
|
62
|
-
} = await buildApi(
|
|
67
|
+
} = await buildApi({
|
|
68
|
+
spawn: modSpawn.spawn,
|
|
69
|
+
cwd: modRuntime.cwd,
|
|
70
|
+
currentExecPath: modRuntime.currentExecPath,
|
|
71
|
+
runFileArgs: modRuntime.runFileArgs
|
|
72
|
+
});
|
|
63
73
|
|
|
64
74
|
export default $;
|
package/src/node/index.js
CHANGED
|
@@ -2,13 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
// The Node-streams facade: identical API to the main entry, but stdin/stdout/stderr
|
|
4
4
|
// (and .from/.to/.through/.io/asDuplex) are Node streams instead of Web streams.
|
|
5
|
-
//
|
|
5
|
+
// Spawning always goes through node:child_process (on Bun/Deno via their node compat) —
|
|
6
|
+
// that is the point of this entry. But, like DSH_FORCE_NODE on the main entry, this must
|
|
7
|
+
// affect only the spawn implementation: `currentExecPath` / `runFileArgs` / `cwd` stay
|
|
8
|
+
// runtime-native, so a child of Bun/Deno is still launched as `bun run …` / `deno run …`,
|
|
9
|
+
// never a bare `node`.
|
|
6
10
|
|
|
7
11
|
import {buildApi} from '../build.js';
|
|
8
12
|
import * as backend from '../spawn/node.js';
|
|
9
13
|
|
|
10
14
|
export {isWindows, raw, winCmdEscape} from '../utils.js';
|
|
11
15
|
|
|
16
|
+
let modRuntime;
|
|
17
|
+
if (typeof Deno !== 'undefined') {
|
|
18
|
+
modRuntime = await import('../spawn/deno.js');
|
|
19
|
+
} else if (typeof Bun !== 'undefined') {
|
|
20
|
+
modRuntime = await import('../spawn/bun.js');
|
|
21
|
+
} else {
|
|
22
|
+
modRuntime = backend;
|
|
23
|
+
}
|
|
24
|
+
|
|
12
25
|
export const {
|
|
13
26
|
spawn,
|
|
14
27
|
cwd,
|
|
@@ -24,9 +37,9 @@ export const {
|
|
|
24
37
|
$sh
|
|
25
38
|
} = await buildApi({
|
|
26
39
|
spawn: backend.nodeStreamSpawn,
|
|
27
|
-
cwd:
|
|
28
|
-
currentExecPath:
|
|
29
|
-
runFileArgs:
|
|
40
|
+
cwd: modRuntime.cwd,
|
|
41
|
+
currentExecPath: modRuntime.currentExecPath,
|
|
42
|
+
runFileArgs: modRuntime.runFileArgs
|
|
30
43
|
});
|
|
31
44
|
|
|
32
45
|
export default $;
|