dollar-shell 1.1.8 → 1.1.10
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/AGENTS.md +97 -0
- package/CLAUDE.md +3 -0
- package/CONTRIBUTING.md +34 -0
- package/README.md +39 -3
- package/llms-full.txt +317 -0
- package/llms.txt +79 -0
- package/package.json +27 -5
- package/src/bq-shell.js +5 -3
- package/src/bq-spawn.js +5 -3
- package/src/index.d.ts +5 -0
- package/src/index.js +27 -19
- package/src/shell/unix.js +0 -2
- package/src/shell/windows.js +0 -2
- package/src/spawn/bun.js +20 -14
- package/src/spawn/deno.js +19 -8
- package/src/spawn/node.js +18 -8
- package/src/utils.js +1 -4
package/AGENTS.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# AGENTS.md — dollar-shell
|
|
2
|
+
|
|
3
|
+
> `dollar-shell` is a micro-library for running OS and shell commands from JavaScript/TypeScript using template tag functions. It works on Node, Deno, and Bun with the same API. All streams are web streams. Zero dependencies.
|
|
4
|
+
|
|
5
|
+
For detailed usage docs and API references see the [wiki](https://github.com/uhop/dollar-shell/wiki).
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone --recursive git@github.com:uhop/dollar-shell.git
|
|
11
|
+
cd dollar-shell
|
|
12
|
+
npm install
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The wiki is a git submodule in `wiki/`.
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
- **Test (Node):** `npm test` (runs `tape6 --flags FO`)
|
|
20
|
+
- **Test (Bun):** `npm run test:bun`
|
|
21
|
+
- **Test (Deno):** `npm run test:deno`
|
|
22
|
+
- **TypeScript check:** `npm run ts-check` (`tsc --noEmit`)
|
|
23
|
+
- **Lint:** `npm run lint` (Prettier check)
|
|
24
|
+
- **Lint fix:** `npm run lint:fix` (Prettier write)
|
|
25
|
+
|
|
26
|
+
## Project structure
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
dollar-shell/
|
|
30
|
+
├── package.json # Package config
|
|
31
|
+
├── tsconfig.json # TypeScript config (noEmit check only)
|
|
32
|
+
├── src/ # Source code
|
|
33
|
+
│ ├── index.js # Main entry point, wires everything together
|
|
34
|
+
│ ├── index.d.ts # TypeScript declarations for the full public API
|
|
35
|
+
│ ├── bq-spawn.js # Template tag factory for spawn-based functions ($, $$)
|
|
36
|
+
│ ├── bq-shell.js # Template tag factory for shell-based functions ($sh, shell)
|
|
37
|
+
│ ├── utils.js # Shared utilities (raw, isWindows, winCmdEscape, etc.)
|
|
38
|
+
│ ├── spawn/ # Runtime-specific Subprocess implementations
|
|
39
|
+
│ │ ├── node.js # Node.js: uses child_process + stream.Writable/Readable
|
|
40
|
+
│ │ ├── deno.js # Deno: uses Deno.Command
|
|
41
|
+
│ │ └── bun.js # Bun: uses Bun.spawn
|
|
42
|
+
│ └── shell/ # Platform-specific shell escaping and command building
|
|
43
|
+
│ ├── unix.js # Unix: single-quote escaping, $SHELL detection
|
|
44
|
+
│ └── windows.js # Windows: cmd.exe and PowerShell escaping
|
|
45
|
+
├── tests/ # Automated tests (tape-six)
|
|
46
|
+
├── tests/manual/ # Manual verification scripts
|
|
47
|
+
├── ts-check/ # TypeScript usage examples (compiled but not executed)
|
|
48
|
+
└── wiki/ # GitHub wiki documentation (git submodule)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick reference
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
import $, {$$, $sh, shell, sh, spawn} from 'dollar-shell';
|
|
55
|
+
|
|
56
|
+
// Run a command, get exit info
|
|
57
|
+
const result = await $`echo hello`;
|
|
58
|
+
// result: {code: 0, signal: null, killed: false}
|
|
59
|
+
|
|
60
|
+
// Run a command, get full Subprocess
|
|
61
|
+
const sp = $$`sleep 5`;
|
|
62
|
+
sp.kill();
|
|
63
|
+
await sp.exited;
|
|
64
|
+
|
|
65
|
+
// Shell command (supports pipes, aliases)
|
|
66
|
+
await $sh`ls -l . | grep LICENSE | wc`;
|
|
67
|
+
|
|
68
|
+
// Stream pipelines
|
|
69
|
+
$.from`ls -l .`.pipeThrough($.io`grep LIC`).pipeTo($.to({stdout: 'inherit'})`wc`);
|
|
70
|
+
|
|
71
|
+
// Custom options (returns new tag function with updated defaults)
|
|
72
|
+
const $verbose = $({stdout: 'inherit', stderr: 'inherit'});
|
|
73
|
+
await $verbose`ls -l .`;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Code style
|
|
77
|
+
|
|
78
|
+
- **ES modules** throughout (`"type": "module"` in package.json).
|
|
79
|
+
- **No transpilation** — code runs directly in all target runtimes.
|
|
80
|
+
- **Prettier** for formatting — run `npm run lint:fix` before committing.
|
|
81
|
+
- Imports at the top of files, using `import` syntax.
|
|
82
|
+
|
|
83
|
+
## Architecture
|
|
84
|
+
|
|
85
|
+
- `src/index.js` is the main entry point. It dynamically imports the correct `spawn` implementation (Node/Deno/Bun) and the correct `shell` implementation (Unix/Windows) at import time.
|
|
86
|
+
- All tag functions (`$`, `$$`, `$sh`, `shell`) are built by factory functions in `bq-spawn.js` and `bq-shell.js`.
|
|
87
|
+
- **Tag function + options pattern**: calling a tag function with an options object returns a new tag function with updated defaults while preserving `.from`, `.to`, `.io`, `.through` properties.
|
|
88
|
+
- **raw()**: wraps a value to bypass escaping (shell) or argument splitting (spawn).
|
|
89
|
+
- **Platform detection**: `isWindows` boolean is exported. Runtime detection (Node/Deno/Bun) happens at import via dynamic imports.
|
|
90
|
+
|
|
91
|
+
## Key conventions
|
|
92
|
+
|
|
93
|
+
- Do not add dependencies — the library is intentionally zero-dependency.
|
|
94
|
+
- All public API is exported from `src/index.js` and typed in `src/index.d.ts`. Keep them in sync.
|
|
95
|
+
- Wiki documentation lives in the `wiki/` submodule — update it alongside code changes.
|
|
96
|
+
- Tests are in `tests/` (automated, tape-six) and `tests/manual/` (manual verification scripts).
|
|
97
|
+
- TypeScript usage examples are in `ts-check/` — they are compiled but not executed during `npm run ts-check`.
|
package/CLAUDE.md
ADDED
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Contributing to dollar-shell
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing!
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
This project uses git submodules. Clone and set up:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone --recursive git@github.com:uhop/dollar-shell.git
|
|
11
|
+
cd dollar-shell
|
|
12
|
+
npm install
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
See the [wiki](https://github.com/uhop/dollar-shell/wiki) for API documentation.
|
|
16
|
+
|
|
17
|
+
## Development workflow
|
|
18
|
+
|
|
19
|
+
1. Make your changes.
|
|
20
|
+
2. Format: `npm run lint:fix`
|
|
21
|
+
3. Test: `npm test`
|
|
22
|
+
4. Type-check: `npm run ts-check`
|
|
23
|
+
|
|
24
|
+
## Code style
|
|
25
|
+
|
|
26
|
+
- ES modules (`import`/`export`), no CommonJS in source.
|
|
27
|
+
- Formatted with Prettier — run `npm run lint:fix` before committing.
|
|
28
|
+
- No dependencies — the library is intentionally zero-dependency.
|
|
29
|
+
- Keep `src/index.js` and `src/index.d.ts` in sync.
|
|
30
|
+
- Update wiki documentation alongside code changes.
|
|
31
|
+
|
|
32
|
+
## AI agents
|
|
33
|
+
|
|
34
|
+
If you are an AI coding agent, see [AGENTS.md](./AGENTS.md) for detailed project conventions, commands, and architecture.
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[npm-image]: https://img.shields.io/npm/v/dollar-shell.svg
|
|
4
4
|
[npm-url]: https://npmjs.org/package/dollar-shell
|
|
5
5
|
|
|
6
|
-
`dollar-shell` is a micro-library for running shell commands
|
|
6
|
+
`dollar-shell` is a micro-library for running OS and shell commands from JavaScript/TypeScript using template tag functions. It works in [Node](https://nodejs.org/), [Deno](https://deno.land/), [Bun](https://bun.sh/) with the same API. **Web streams, TypeScript typings, zero dependencies.**
|
|
7
7
|
|
|
8
8
|
The idea is to run OS/shell commands and/or use them in stream pipelines as sources, sinks,
|
|
9
9
|
and transformation steps using [web streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API).
|
|
@@ -85,10 +85,32 @@ chain([
|
|
|
85
85
|
npm i --save dollar-shell
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
+
## Project structure
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
dollar-shell/
|
|
92
|
+
├── src/ # Source code
|
|
93
|
+
│ ├── index.js # Main entry point, wires everything together
|
|
94
|
+
│ ├── index.d.ts # TypeScript declarations for the full public API
|
|
95
|
+
│ ├── bq-spawn.js # Template tag factory for spawn-based functions ($, $$)
|
|
96
|
+
│ ├── bq-shell.js # Template tag factory for shell-based functions ($sh, shell)
|
|
97
|
+
│ ├── utils.js # Shared utilities (raw, isWindows, winCmdEscape, etc.)
|
|
98
|
+
│ ├── spawn/ # Runtime-specific Subprocess implementations
|
|
99
|
+
│ └── shell/ # Platform-specific shell escaping and command building
|
|
100
|
+
├── tests/ # Automated tests (tape-six)
|
|
101
|
+
├── tests/manual/ # Manual verification scripts
|
|
102
|
+
├── ts-check/ # TypeScript usage examples (compiled but not executed)
|
|
103
|
+
└── wiki/ # GitHub wiki documentation (git submodule)
|
|
104
|
+
```
|
|
105
|
+
|
|
88
106
|
## Documentation
|
|
89
107
|
|
|
108
|
+
The documentation can be found in the [wiki](https://github.com/uhop/dollar-shell/wiki).
|
|
109
|
+
See how it can be used in [tests/](https://github.com/uhop/dollar-shell/tree/main/tests).
|
|
110
|
+
|
|
111
|
+
For AI assistants: see [llms.txt](https://github.com/uhop/dollar-shell/blob/main/llms.txt) and [llms-full.txt](https://github.com/uhop/dollar-shell/blob/main/llms-full.txt) for LLM-optimized documentation.
|
|
112
|
+
|
|
90
113
|
Below is the documentation for the main components: `spawn()`, `$$`, `$` and `$sh`.
|
|
91
|
-
Additional information can be found in the [wiki](https://github.com/uhop/dollar-shell/wiki).
|
|
92
114
|
|
|
93
115
|
### `spawn()`
|
|
94
116
|
|
|
@@ -194,16 +216,30 @@ the spawn options with the following properties:
|
|
|
194
216
|
- `shellArgs` — an array of strings that are passed to the shell as arguments.
|
|
195
217
|
- On Unix-like systems it defaults to `['-c']`.
|
|
196
218
|
- On Windows it defaults to `['/d', '/s', '/c']` for `cmd.exe`
|
|
197
|
-
or `['-
|
|
219
|
+
or `['-c']` for `pwsh.exe` or `powershell.exe`.
|
|
198
220
|
|
|
199
221
|
The rest is identical to `$`: `$sh`, `$sh.from`, `$sh.to` and `$sh.io`/`$sh.through`.
|
|
200
222
|
|
|
223
|
+
## For AI Agents
|
|
224
|
+
|
|
225
|
+
This package ships with files to help AI coding agents and LLMs find, understand, and use it:
|
|
226
|
+
|
|
227
|
+
- **[AGENTS.md](./AGENTS.md)** — Project conventions, architecture, commands, and coding guidelines for AI agents.
|
|
228
|
+
- **[CLAUDE.md](./CLAUDE.md)** — Claude Code specific instructions (redirects to AGENTS.md).
|
|
229
|
+
- **[CONTRIBUTING.md](./CONTRIBUTING.md)** — Contribution guidelines for humans and AI agents.
|
|
230
|
+
- **[llms.txt](./llms.txt)** — Concise project overview following the [llms.txt standard](https://llmstxt.org/).
|
|
231
|
+
- **[llms-full.txt](./llms-full.txt)** — Self-contained complete API reference (no external links needed).
|
|
232
|
+
|
|
233
|
+
All files are included in the npm package.
|
|
234
|
+
|
|
201
235
|
## License
|
|
202
236
|
|
|
203
237
|
BSD-3-Clause
|
|
204
238
|
|
|
205
239
|
## Release History
|
|
206
240
|
|
|
241
|
+
- 1.1.10 _Fixed a bug with options chaining for attached functions, fixed Bun spawn on invalid commands, Windows-compatible tests, updated dev dependencies._
|
|
242
|
+
- 1.1.9 _Updated dev dependencies, cleaned up docs, added info for AI agents._
|
|
207
243
|
- 1.1.8 _Updated dev dependencies._
|
|
208
244
|
- 1.1.7 _Updated dev dependencies._
|
|
209
245
|
- 1.1.6 _Updated dev dependencies._
|
package/llms-full.txt
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# dollar-shell
|
|
2
|
+
|
|
3
|
+
> A micro-library for running OS and shell commands from JavaScript/TypeScript using template tag functions. Works in Node, Deno, and Bun with the same API. Web streams, TypeScript typings, zero dependencies.
|
|
4
|
+
|
|
5
|
+
The package provides template-tag functions to spawn OS processes (`$`, `$$`) and shell processes (`$sh`, `shell`/`sh`). Each has variants for stream pipelines: `.from` (stdout as ReadableStream), `.to` (stdin as WritableStream), `.io`/`.through` (duplex). All tag functions accept an options object to customize spawn/shell behavior and return a new tag function with updated defaults.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i --save dollar-shell
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import $, {$$, $sh, shell, sh, spawn} from 'dollar-shell';
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## spawn()
|
|
20
|
+
|
|
21
|
+
Low-level function to spawn a process with full control.
|
|
22
|
+
|
|
23
|
+
Signature: `spawn(command: string[], options?: SpawnOptions): Subprocess`
|
|
24
|
+
|
|
25
|
+
### SpawnOptions
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
type SpawnStreamState = 'pipe' | 'ignore' | 'inherit' | 'piped' | null;
|
|
29
|
+
|
|
30
|
+
interface SpawnOptions {
|
|
31
|
+
cwd?: string; // working directory, defaults to process.cwd()
|
|
32
|
+
env?: {[key: string]: string | undefined}; // environment variables
|
|
33
|
+
stdin?: SpawnStreamState; // default: null (ignored)
|
|
34
|
+
stdout?: SpawnStreamState; // default: null (ignored)
|
|
35
|
+
stderr?: SpawnStreamState; // default: null (ignored)
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Stream states: `'pipe'`/`'piped'` makes the stream available, `'inherit'` inherits from parent, `'ignore'`/`null` ignores it.
|
|
40
|
+
|
|
41
|
+
### Subprocess
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
interface Subprocess<R = any> {
|
|
45
|
+
readonly command: string[];
|
|
46
|
+
readonly options: SpawnOptions | undefined;
|
|
47
|
+
readonly exited: Promise<number>; // resolves to exit code
|
|
48
|
+
readonly finished: boolean;
|
|
49
|
+
readonly killed: boolean;
|
|
50
|
+
readonly exitCode: number | null;
|
|
51
|
+
readonly signalCode: string | null;
|
|
52
|
+
readonly stdin: WritableStream<R> | null; // non-null when stdin is 'pipe'
|
|
53
|
+
readonly stdout: ReadableStream<R> | null; // non-null when stdout is 'pipe'
|
|
54
|
+
readonly stderr: ReadableStream<R> | null; // non-null when stderr is 'pipe'
|
|
55
|
+
readonly asDuplex: {readable: ReadableStream<R>, writable: WritableStream<R>};
|
|
56
|
+
kill(): void;
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
All streams are web streams (ReadableStream/WritableStream).
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
```js
|
|
64
|
+
import {spawn} from 'dollar-shell';
|
|
65
|
+
|
|
66
|
+
const sp = spawn(['sleep', '5']);
|
|
67
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
68
|
+
sp.kill();
|
|
69
|
+
await sp.exited;
|
|
70
|
+
// sp.finished === true, sp.killed === true
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## $$ (double dollar)
|
|
74
|
+
|
|
75
|
+
Template tag function wrapping `spawn()`. Parses the template string into an array of arguments (split by whitespace). Interpolated values are kept as separate arguments when surrounded by whitespace.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
type Backticks<R> = (strings: TemplateStringsArray, ...args: unknown[]) => R;
|
|
79
|
+
|
|
80
|
+
interface Dollar<R, O = SpawnOptions> extends Backticks<R> {
|
|
81
|
+
(options: O): Dollar<R, O>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
declare const $$: Dollar<Subprocess>;
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Signatures:
|
|
88
|
+
```js
|
|
89
|
+
import {$$} from 'dollar-shell';
|
|
90
|
+
|
|
91
|
+
const sp = $$`ls -l ${myFile}`; // returns Subprocess
|
|
92
|
+
const sp2 = $$(options)`ls -l .`; // with custom options
|
|
93
|
+
const $tag = $$(options); // returns reusable tag function
|
|
94
|
+
const sp3 = $tag`ls -l .`;
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## $ (dollar)
|
|
98
|
+
|
|
99
|
+
Simpler interface than `$$`. Returns a promise with exit info instead of a Subprocess.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
interface DollarResult {
|
|
103
|
+
code: number | null;
|
|
104
|
+
signal: string | null;
|
|
105
|
+
killed: boolean;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
interface DollarImpl<R = any> extends Dollar<Promise<DollarResult>> {
|
|
109
|
+
from: Dollar<ReadableStream<R>>; // stdout as source stream
|
|
110
|
+
to: Dollar<WritableStream<R>>; // stdin as sink stream
|
|
111
|
+
through: Dollar<DuplexPair<R>>; // {readable, writable} pair
|
|
112
|
+
io: Dollar<DuplexPair<R>>; // alias of through
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
declare const $: DollarImpl;
|
|
116
|
+
export default $;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`$` is the default export.
|
|
120
|
+
|
|
121
|
+
Examples:
|
|
122
|
+
```js
|
|
123
|
+
import $ from 'dollar-shell';
|
|
124
|
+
|
|
125
|
+
// Run a command
|
|
126
|
+
const result = await $`echo hello`;
|
|
127
|
+
console.log(result.code, result.signal, result.killed);
|
|
128
|
+
|
|
129
|
+
// With custom options
|
|
130
|
+
const result2 = await $({stdout: 'inherit'})`ls -l .`;
|
|
131
|
+
|
|
132
|
+
// Stream pipeline
|
|
133
|
+
import chain from 'stream-chain';
|
|
134
|
+
|
|
135
|
+
chain([
|
|
136
|
+
$.from`ls -l .`,
|
|
137
|
+
$.io`grep LICENSE`,
|
|
138
|
+
$.io`wc`,
|
|
139
|
+
$.to({stdout: 'inherit'})`tee output.txt`
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
// Using $.from with web streams
|
|
143
|
+
$.from`ls -l .`
|
|
144
|
+
.pipeThrough($.io`grep LIC`)
|
|
145
|
+
.pipeTo($.to({stdout: 'inherit'})`wc`);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
When `$` is called with an options object, it returns a new `$` with updated defaults while preserving `.from`, `.to`, `.through`, `.io`:
|
|
149
|
+
```js
|
|
150
|
+
const $custom = $({stdout: 'inherit', stderr: 'inherit'});
|
|
151
|
+
typeof $custom.from === 'function'; // true
|
|
152
|
+
typeof $custom.io === 'function'; // true
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## shell / sh
|
|
156
|
+
|
|
157
|
+
Like `$$` but executes the command through a shell. Supports shell-specific features like pipes, aliases, and functions.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
interface ShellOptions extends SpawnOptions {
|
|
161
|
+
shellPath?: string; // path to shell executable
|
|
162
|
+
shellArgs?: string[]; // arguments passed to the shell
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
declare const shell: Dollar<Subprocess, ShellOptions>;
|
|
166
|
+
declare const sh = shell; // alias
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Shell defaults:
|
|
170
|
+
- Unix: shell = `$SHELL` or `/bin/sh` (or `/system/bin/sh` on Android), args = `['-c']`
|
|
171
|
+
- Windows cmd.exe: shell = `%ComSpec%` or `cmd.exe`, args = `['/d', '/s', '/c']`
|
|
172
|
+
- Windows PowerShell: shell = `pwsh.exe`/`powershell.exe`, args = `['-c']`
|
|
173
|
+
|
|
174
|
+
Example:
|
|
175
|
+
```js
|
|
176
|
+
import {shell, sh} from 'dollar-shell';
|
|
177
|
+
|
|
178
|
+
const sp = sh`sleep 5`;
|
|
179
|
+
sp.kill();
|
|
180
|
+
await sp.exited;
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## $sh (dollar shell)
|
|
184
|
+
|
|
185
|
+
Mirrors `$` but runs commands through a shell. Same relationship as `shell` to `$$`.
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
interface ShellImpl<R = any> extends Dollar<Promise<DollarResult>, ShellOptions> {
|
|
189
|
+
from: Dollar<ReadableStream<R>, ShellOptions>;
|
|
190
|
+
to: Dollar<WritableStream<R>, ShellOptions>;
|
|
191
|
+
through: Dollar<DuplexPair<R>, ShellOptions>;
|
|
192
|
+
io: Dollar<DuplexPair<R>, ShellOptions>;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
declare const $sh: ShellImpl;
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Examples:
|
|
199
|
+
```js
|
|
200
|
+
import {$sh} from 'dollar-shell';
|
|
201
|
+
|
|
202
|
+
const result = await $sh`ls .`;
|
|
203
|
+
console.log(result.code, result.signal, result.killed);
|
|
204
|
+
|
|
205
|
+
// Interactive shell for aliases/functions
|
|
206
|
+
const $p = $sh({shellArgs: ['-ic'], stdout: 'inherit'});
|
|
207
|
+
await $p`nvm ls`;
|
|
208
|
+
|
|
209
|
+
// Shell pipes
|
|
210
|
+
await $sh({stdout: 'inherit'})`ls -l . | grep LICENSE | wc`;
|
|
211
|
+
|
|
212
|
+
// Stream pipeline
|
|
213
|
+
$sh.from`ls -l .`
|
|
214
|
+
.pipeThrough($sh.io`grep LIC`)
|
|
215
|
+
.pipeTo($sh.to({stdout: 'inherit'})`wc`);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Utilities
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
import {
|
|
222
|
+
isWindows, raw, winCmdEscape,
|
|
223
|
+
cwd, currentExecPath, runFileArgs,
|
|
224
|
+
shellEscape, currentShellPath, buildShellCommand
|
|
225
|
+
} from 'dollar-shell';
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### isWindows
|
|
229
|
+
|
|
230
|
+
`const isWindows: boolean` — `true` if the current platform is Windows.
|
|
231
|
+
|
|
232
|
+
### raw(value)
|
|
233
|
+
|
|
234
|
+
Marks a value to bypass shell escaping or spawn argument splitting. The value is passed as-is.
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
import {$sh, raw} from 'dollar-shell';
|
|
238
|
+
await $sh({stdout: 'inherit'})`echo ${raw('"hello"')}`;
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
For spawn (`$`/`$$`), `raw()` values are split by whitespace like template string literals.
|
|
242
|
+
|
|
243
|
+
### winCmdEscape(value)
|
|
244
|
+
|
|
245
|
+
Escapes a value for Windows `cmd.exe` using caret (`^`) escaping. On non-Windows, returns the value as a string. Returns a `raw()`-wrapped object on Windows.
|
|
246
|
+
|
|
247
|
+
```js
|
|
248
|
+
import {$sh, winCmdEscape} from 'dollar-shell';
|
|
249
|
+
await $sh`echo ${winCmdEscape('hello "world"')}`;
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### cwd()
|
|
253
|
+
|
|
254
|
+
Returns the current working directory (platform-agnostic).
|
|
255
|
+
|
|
256
|
+
### currentExecPath()
|
|
257
|
+
|
|
258
|
+
Returns the path of the current JS/TS runtime executable (Node, Deno, or Bun).
|
|
259
|
+
|
|
260
|
+
### runFileArgs
|
|
261
|
+
|
|
262
|
+
Array of default arguments for the current runtime:
|
|
263
|
+
- Node: `[]`
|
|
264
|
+
- Deno: `['run']`
|
|
265
|
+
- Bun: `['run']`
|
|
266
|
+
|
|
267
|
+
Note: Deno may require additional permission flags (e.g., `--allow-read`).
|
|
268
|
+
|
|
269
|
+
### shellEscape(value, options?)
|
|
270
|
+
|
|
271
|
+
Escapes a value for the current shell. Takes an optional `{shellPath?: string}` options object.
|
|
272
|
+
- Unix: wraps in single quotes
|
|
273
|
+
- Windows cmd.exe: quotes and escapes per cmd.exe rules
|
|
274
|
+
- Windows PowerShell: escapes control characters and non-alphanumeric characters
|
|
275
|
+
|
|
276
|
+
### currentShellPath()
|
|
277
|
+
|
|
278
|
+
Returns the current shell path:
|
|
279
|
+
- Unix: `$SHELL` or `/bin/sh` (or `/system/bin/sh` on Android)
|
|
280
|
+
- Windows: `%ComSpec%` or `cmd.exe`
|
|
281
|
+
|
|
282
|
+
### buildShellCommand(shell, args, command)
|
|
283
|
+
|
|
284
|
+
Builds a command array for `spawn()` from shell path, args, and command string.
|
|
285
|
+
- `shell`: string or undefined (defaults to `currentShellPath()`)
|
|
286
|
+
- `args`: string[] or undefined (defaults to shell-specific args)
|
|
287
|
+
- `command`: the shell command string
|
|
288
|
+
|
|
289
|
+
Returns `string[]` suitable for `spawn()`.
|
|
290
|
+
|
|
291
|
+
## Template String Behavior
|
|
292
|
+
|
|
293
|
+
### Spawn functions ($, $$)
|
|
294
|
+
|
|
295
|
+
Template strings are parsed into arrays of arguments by splitting on whitespace. Interpolated values:
|
|
296
|
+
- If surrounded by whitespace: added as a separate argument (preserving spaces within the value)
|
|
297
|
+
- If adjacent to text: concatenated with surrounding text
|
|
298
|
+
- Empty strings are skipped
|
|
299
|
+
- `raw()` values are split by whitespace like literal template text
|
|
300
|
+
|
|
301
|
+
```js
|
|
302
|
+
$`ls -l ${'.'}`; // command: ['ls', '-l', '.']
|
|
303
|
+
$`${'l'}s -l a${'.'}b`; // command: ['ls', '-l', 'a.b']
|
|
304
|
+
$`ls ${'x y'}`; // command: ['ls', 'x y'] (preserved as one arg)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Shell functions ($sh, shell)
|
|
308
|
+
|
|
309
|
+
Template strings are concatenated into a single command string. Interpolated values are escaped using `shellEscape()` unless wrapped with `raw()`.
|
|
310
|
+
|
|
311
|
+
## TypeScript
|
|
312
|
+
|
|
313
|
+
Full TypeScript declarations are provided in `src/index.d.ts`. The package uses `"types": "./src/index.d.ts"` in package.json.
|
|
314
|
+
|
|
315
|
+
## Platform Support
|
|
316
|
+
|
|
317
|
+
Works on Node.js, Deno, and Bun. The appropriate spawn implementation is selected automatically at import time. All streams use the web streams API (ReadableStream/WritableStream).
|
package/llms.txt
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# dollar-shell
|
|
2
|
+
|
|
3
|
+
> A micro-library for running OS and shell commands from JavaScript/TypeScript using template tag functions. Works in Node, Deno, and Bun with the same API. Web streams, TypeScript typings, zero dependencies.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
npm i --save dollar-shell
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import $ from 'dollar-shell';
|
|
13
|
+
|
|
14
|
+
const result = await $`echo hello`;
|
|
15
|
+
// result: {code: 0, signal: null, killed: false}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Run: `node my-script.js`
|
|
19
|
+
|
|
20
|
+
## API
|
|
21
|
+
|
|
22
|
+
- `$` — run a command, returns `Promise<{code, signal, killed}>`. Default export.
|
|
23
|
+
- `$$` — run a command, returns a `Subprocess` object (full control: kill, streams, exit code).
|
|
24
|
+
- `$sh` — run a shell command, returns `Promise<{code, signal, killed}>`.
|
|
25
|
+
- `shell` / `sh` — run a shell command, returns a `Subprocess` object.
|
|
26
|
+
- `spawn()` — low-level process spawning with full configuration.
|
|
27
|
+
|
|
28
|
+
Stream pipeline helpers (available on `$` and `$sh`):
|
|
29
|
+
- `.from` — stdout as a ReadableStream (source).
|
|
30
|
+
- `.to` — stdin as a WritableStream (sink).
|
|
31
|
+
- `.io` / `.through` — {readable, writable} duplex pair (transform step).
|
|
32
|
+
|
|
33
|
+
All tag functions accept an options object to return a new tag function with updated defaults.
|
|
34
|
+
|
|
35
|
+
Utilities: `raw()`, `winCmdEscape()`, `isWindows`, `cwd()`, `currentExecPath()`, `runFileArgs`, `shellEscape()`, `currentShellPath()`, `buildShellCommand()`.
|
|
36
|
+
|
|
37
|
+
## Common patterns
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
import $, {$$, $sh, raw} from 'dollar-shell';
|
|
41
|
+
|
|
42
|
+
// Simple command
|
|
43
|
+
const result = await $`echo hello`;
|
|
44
|
+
|
|
45
|
+
// Shell command (pipes, aliases, globbing)
|
|
46
|
+
await $sh({stdout: 'inherit'})`ls -l . | grep LICENSE | wc`;
|
|
47
|
+
|
|
48
|
+
// Full Subprocess control
|
|
49
|
+
const sp = $$`sleep 5`;
|
|
50
|
+
sp.kill();
|
|
51
|
+
await sp.exited;
|
|
52
|
+
|
|
53
|
+
// Custom options (returns new tag function)
|
|
54
|
+
const $verbose = $({stdout: 'inherit', stderr: 'inherit'});
|
|
55
|
+
await $verbose`ls -l .`;
|
|
56
|
+
|
|
57
|
+
// Web stream pipeline
|
|
58
|
+
$.from`ls -l .`
|
|
59
|
+
.pipeThrough($.io`grep LIC`)
|
|
60
|
+
.pipeTo($.to({stdout: 'inherit'})`wc`);
|
|
61
|
+
|
|
62
|
+
// Bypass escaping with raw()
|
|
63
|
+
await $sh({stdout: 'inherit'})`echo ${raw('"hello"')}`;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Docs
|
|
67
|
+
|
|
68
|
+
- [$ (dollar)](https://github.com/uhop/dollar-shell/wiki/$): Spawn processes with simple return values, includes $.from, $.to, $.io/$.through
|
|
69
|
+
- [$$ (double dollar)](https://github.com/uhop/dollar-shell/wiki/$$): Spawn processes returning full Subprocess objects
|
|
70
|
+
- [$sh (dollar shell)](https://github.com/uhop/dollar-shell/wiki/$sh): Run shell commands with simple return values, includes $sh.from, $sh.to, $sh.io/$sh.through
|
|
71
|
+
- [shell / sh](https://github.com/uhop/dollar-shell/wiki/shell): Run shell commands returning full Subprocess objects
|
|
72
|
+
- [spawn()](https://github.com/uhop/dollar-shell/wiki/spawn): Low-level process spawning with full configuration
|
|
73
|
+
- [Utilities](https://github.com/uhop/dollar-shell/wiki/Utilities): Helper functions — raw(), winCmdEscape(), isWindows, cwd(), currentExecPath(), runFileArgs, shellEscape(), currentShellPath(), buildShellCommand()
|
|
74
|
+
|
|
75
|
+
## Links
|
|
76
|
+
|
|
77
|
+
- Docs: https://github.com/uhop/dollar-shell/wiki
|
|
78
|
+
- npm: https://www.npmjs.com/package/dollar-shell
|
|
79
|
+
- Full LLM reference: https://github.com/uhop/dollar-shell/blob/main/llms-full.txt
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dollar-shell",
|
|
3
|
-
"description": "Run shell commands and use them in
|
|
4
|
-
"version": "1.1.
|
|
3
|
+
"description": "Run shell commands and use them in stream pipelines with ease in Node, Deno, Bun. Template tag API, web streams, TypeScript typings, zero dependencies.",
|
|
4
|
+
"version": "1.1.10",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"types": "./src/index.d.ts",
|
|
@@ -26,7 +26,12 @@
|
|
|
26
26
|
"files": [
|
|
27
27
|
"/src",
|
|
28
28
|
"LICENSE",
|
|
29
|
-
"README.md"
|
|
29
|
+
"README.md",
|
|
30
|
+
"AGENTS.md",
|
|
31
|
+
"CLAUDE.md",
|
|
32
|
+
"CONTRIBUTING.md",
|
|
33
|
+
"llms.txt",
|
|
34
|
+
"llms-full.txt"
|
|
30
35
|
],
|
|
31
36
|
"repository": {
|
|
32
37
|
"type": "git",
|
|
@@ -36,12 +41,28 @@
|
|
|
36
41
|
"url": "https://github.com/uhop/dollar-shell/issues"
|
|
37
42
|
},
|
|
38
43
|
"homepage": "https://github.com/uhop/dollar-shell#readme",
|
|
44
|
+
"llms": "https://raw.githubusercontent.com/uhop/dollar-shell/main/llms.txt",
|
|
45
|
+
"llmsFull": "https://raw.githubusercontent.com/uhop/dollar-shell/main/llms-full.txt",
|
|
39
46
|
"keywords": [
|
|
40
47
|
"shell",
|
|
41
48
|
"spawn",
|
|
42
49
|
"$",
|
|
43
50
|
"dollar",
|
|
44
|
-
"stream"
|
|
51
|
+
"stream",
|
|
52
|
+
"process",
|
|
53
|
+
"exec",
|
|
54
|
+
"command",
|
|
55
|
+
"web-streams",
|
|
56
|
+
"template-tag",
|
|
57
|
+
"pipeline",
|
|
58
|
+
"typescript",
|
|
59
|
+
"esm",
|
|
60
|
+
"es-modules",
|
|
61
|
+
"cross-runtime",
|
|
62
|
+
"zero-dependency",
|
|
63
|
+
"nodejs",
|
|
64
|
+
"deno",
|
|
65
|
+
"bun"
|
|
45
66
|
],
|
|
46
67
|
"author": "Eugene Lazutkin <eugene.lazutkin@gmail.com> (http://www.lazutkin.com/)",
|
|
47
68
|
"funding": {
|
|
@@ -50,7 +71,8 @@
|
|
|
50
71
|
},
|
|
51
72
|
"license": "BSD-3-Clause",
|
|
52
73
|
"devDependencies": {
|
|
53
|
-
"
|
|
74
|
+
"prettier": "^3.8.1",
|
|
75
|
+
"tape-six": "^1.7.4",
|
|
54
76
|
"typescript": "^5.9.3"
|
|
55
77
|
},
|
|
56
78
|
"tape6": {
|
package/src/bq-shell.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
import {isRawValue, getRawValue, verifyStrings} from './utils.js';
|
|
4
2
|
|
|
5
3
|
const impl =
|
|
@@ -28,7 +26,11 @@ const impl =
|
|
|
28
26
|
const bqShell = (shellEscape, shell, options = {}) => {
|
|
29
27
|
const bq = (strings, ...args) => {
|
|
30
28
|
if (verifyStrings(strings)) return impl(shellEscape, shell, options)(strings, ...args);
|
|
31
|
-
|
|
29
|
+
const derived = bqShell(shellEscape, shell, {...options, ...strings});
|
|
30
|
+
for (const [key, value] of Object.entries(bq)) {
|
|
31
|
+
derived[key] = typeof value === 'function' ? value(strings) : value;
|
|
32
|
+
}
|
|
33
|
+
return derived;
|
|
32
34
|
};
|
|
33
35
|
return bq;
|
|
34
36
|
};
|
package/src/bq-spawn.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
import {verifyStrings, isRawValue, getRawValue} from './utils.js';
|
|
4
2
|
|
|
5
3
|
const appendString = (s, previousSpace, result) => {
|
|
@@ -69,7 +67,11 @@ const impl =
|
|
|
69
67
|
const bqSpawn = (spawn, options = {}) => {
|
|
70
68
|
const bq = (strings, ...args) => {
|
|
71
69
|
if (verifyStrings(strings)) return impl(spawn, options)(strings, ...args);
|
|
72
|
-
|
|
70
|
+
const derived = bqSpawn(spawn, {...options, ...strings});
|
|
71
|
+
for (const [key, value] of Object.entries(bq)) {
|
|
72
|
+
derived[key] = typeof value === 'function' ? value(strings) : value;
|
|
73
|
+
}
|
|
74
|
+
return derived;
|
|
73
75
|
};
|
|
74
76
|
return bq;
|
|
75
77
|
};
|
package/src/index.d.ts
CHANGED
|
@@ -123,6 +123,11 @@ export declare function currentExecPath(): string;
|
|
|
123
123
|
*/
|
|
124
124
|
export declare const runFileArgs: string[];
|
|
125
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Whether the current platform is Windows.
|
|
128
|
+
*/
|
|
129
|
+
export declare const isWindows: boolean;
|
|
130
|
+
|
|
126
131
|
/**
|
|
127
132
|
* The function marks a value as raw. The value will be passed as-is to the shell.
|
|
128
133
|
* It is used to bypass the default shell escaping rules.
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
// @ts-self-types="./index.d.ts"
|
|
2
2
|
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
3
|
// load dependencies
|
|
6
4
|
|
|
7
5
|
import {isWindows, raw, winCmdEscape} from './utils.js';
|
|
@@ -39,17 +37,17 @@ export const $ = bqSpawn((command, options) => {
|
|
|
39
37
|
});
|
|
40
38
|
|
|
41
39
|
const fromProcess = bqSpawn((command, options) => {
|
|
42
|
-
const sp = spawn(command,
|
|
40
|
+
const sp = spawn(command, {...options, stdout: 'pipe'});
|
|
43
41
|
return sp.stdout;
|
|
44
42
|
});
|
|
45
43
|
|
|
46
44
|
const toProcess = bqSpawn((command, options) => {
|
|
47
|
-
const sp = spawn(command,
|
|
45
|
+
const sp = spawn(command, {...options, stdin: 'pipe'});
|
|
48
46
|
return sp.stdin;
|
|
49
47
|
});
|
|
50
48
|
|
|
51
49
|
const throughProcess = bqSpawn((command, options) => {
|
|
52
|
-
const sp = spawn(command,
|
|
50
|
+
const sp = spawn(command, {...options, stdin: 'pipe', stdout: 'pipe'});
|
|
53
51
|
return sp.asDuplex;
|
|
54
52
|
});
|
|
55
53
|
|
|
@@ -60,36 +58,46 @@ $.through = $.io = throughProcess;
|
|
|
60
58
|
// define shell functions
|
|
61
59
|
|
|
62
60
|
export const shell = bqShell(shellEscape, (command, options) =>
|
|
63
|
-
spawn(buildShellCommand(options?.shellPath, options?.shellArgs, command),
|
|
61
|
+
spawn(buildShellCommand(options?.shellPath, options?.shellArgs, command), {
|
|
62
|
+
...options,
|
|
63
|
+
windowsVerbatimArguments: true
|
|
64
|
+
})
|
|
64
65
|
);
|
|
65
66
|
export {shell as sh};
|
|
66
67
|
|
|
67
68
|
export const $sh = bqShell(shellEscape, (command, options) => {
|
|
68
|
-
const sp = spawn(buildShellCommand(options?.shellPath, options?.shellArgs, command),
|
|
69
|
+
const sp = spawn(buildShellCommand(options?.shellPath, options?.shellArgs, command), {
|
|
70
|
+
...options,
|
|
71
|
+
windowsVerbatimArguments: true
|
|
72
|
+
});
|
|
69
73
|
return sp.exited.then(() => ({code: sp.exitCode, signal: sp.signalCode, killed: sp.killed}));
|
|
70
74
|
});
|
|
71
75
|
|
|
72
76
|
const fromShell = bqShell(shellEscape, (command, options) => {
|
|
73
|
-
const sp = spawn(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
const sp = spawn(buildShellCommand(options?.shellPath, options?.shellArgs, command), {
|
|
78
|
+
...options,
|
|
79
|
+
stdout: 'pipe',
|
|
80
|
+
windowsVerbatimArguments: true
|
|
81
|
+
});
|
|
77
82
|
return sp.stdout;
|
|
78
83
|
});
|
|
79
84
|
|
|
80
85
|
const toShell = bqShell(shellEscape, (command, options) => {
|
|
81
|
-
const sp = spawn(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
const sp = spawn(buildShellCommand(options?.shellPath, options?.shellArgs, command), {
|
|
87
|
+
...options,
|
|
88
|
+
stdin: 'pipe',
|
|
89
|
+
windowsVerbatimArguments: true
|
|
90
|
+
});
|
|
85
91
|
return sp.stdin;
|
|
86
92
|
});
|
|
87
93
|
|
|
88
94
|
const throughShell = bqShell(shellEscape, (command, options) => {
|
|
89
|
-
const sp = spawn(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
const sp = spawn(buildShellCommand(options?.shellPath, options?.shellArgs, command), {
|
|
96
|
+
...options,
|
|
97
|
+
stdin: 'pipe',
|
|
98
|
+
stdout: 'pipe',
|
|
99
|
+
windowsVerbatimArguments: true
|
|
100
|
+
});
|
|
93
101
|
return sp.asDuplex;
|
|
94
102
|
});
|
|
95
103
|
|
package/src/shell/unix.js
CHANGED
package/src/shell/windows.js
CHANGED
package/src/spawn/bun.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const sanitize = (value, defaultValue = 'ignore') => {
|
|
4
2
|
switch (value) {
|
|
5
3
|
case 'pipe':
|
|
@@ -20,8 +18,10 @@ class Subprocess {
|
|
|
20
18
|
this.options = options;
|
|
21
19
|
|
|
22
20
|
this.killed = false;
|
|
21
|
+
this.finished = false;
|
|
23
22
|
|
|
24
|
-
const spawnOptions = {
|
|
23
|
+
const spawnOptions = {};
|
|
24
|
+
if (options.windowsVerbatimArguments) spawnOptions.windowsVerbatimArguments = true;
|
|
25
25
|
options.cwd && (spawnOptions.cwd = options.cwd);
|
|
26
26
|
options.env && (spawnOptions.env = options.env);
|
|
27
27
|
|
|
@@ -31,27 +31,33 @@ class Subprocess {
|
|
|
31
31
|
|
|
32
32
|
this.spawnOptions = spawnOptions;
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
try {
|
|
35
|
+
this.childProcess = Bun.spawn(command, spawnOptions);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
this.childProcess = null;
|
|
38
|
+
this.finished = true;
|
|
39
|
+
this.exited = Promise.reject(error);
|
|
40
|
+
this.stdin = null;
|
|
41
|
+
this.stdout = null;
|
|
42
|
+
this.stderr = null;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
this.exited = this.childProcess.exited.then(code => {
|
|
46
|
+
this.finished = true;
|
|
47
|
+
return code;
|
|
48
|
+
});
|
|
35
49
|
|
|
36
50
|
this.stdin = this.childProcess.stdin ? new WritableStream(this.childProcess.stdin) : null;
|
|
37
51
|
this.stdout = this.childProcess.stdout || null;
|
|
38
52
|
this.stderr = this.childProcess.stderr || null;
|
|
39
53
|
}
|
|
40
54
|
|
|
41
|
-
get exited() {
|
|
42
|
-
return this.childProcess.exited;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
get finished() {
|
|
46
|
-
return this.childProcess.killed;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
55
|
get exitCode() {
|
|
50
|
-
return this.childProcess.exitCode;
|
|
56
|
+
return this.childProcess ? this.childProcess.exitCode : null;
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
get signalCode() {
|
|
54
|
-
return this.childProcess.signalCode;
|
|
60
|
+
return this.childProcess ? this.childProcess.signalCode : null;
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
get asDuplex() {
|
package/src/spawn/deno.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const sanitize = (value, defaultValue = 'null') => {
|
|
4
2
|
switch (value) {
|
|
5
3
|
case 'pipe':
|
|
@@ -28,9 +26,9 @@ class Subprocess {
|
|
|
28
26
|
|
|
29
27
|
const spawnOptions = {
|
|
30
28
|
signal: this.controller.signal,
|
|
31
|
-
args: command.slice(1)
|
|
32
|
-
windowsRawArguments: true
|
|
29
|
+
args: command.slice(1)
|
|
33
30
|
};
|
|
31
|
+
if (options.windowsVerbatimArguments) spawnOptions.windowsRawArguments = true;
|
|
34
32
|
options.cwd && (spawnOptions.cwd = options.cwd);
|
|
35
33
|
options.env && (spawnOptions.env = options.env);
|
|
36
34
|
|
|
@@ -40,7 +38,14 @@ class Subprocess {
|
|
|
40
38
|
|
|
41
39
|
this.spawnOptions = spawnOptions;
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
try {
|
|
42
|
+
this.childProcess = new Deno.Command(command[0], spawnOptions).spawn();
|
|
43
|
+
} catch (error) {
|
|
44
|
+
this.childProcess = null;
|
|
45
|
+
this.finished = true;
|
|
46
|
+
this.exited = Promise.reject(error);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
44
49
|
|
|
45
50
|
this.exited = this.childProcess.status
|
|
46
51
|
.then(status => {
|
|
@@ -56,15 +61,21 @@ class Subprocess {
|
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
get stdin() {
|
|
59
|
-
return this.spawnOptions.stdin === 'piped'
|
|
64
|
+
return this.childProcess && this.spawnOptions.stdin === 'piped'
|
|
65
|
+
? this.childProcess.stdin
|
|
66
|
+
: null;
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
get stdout() {
|
|
63
|
-
return this.spawnOptions.stdout === 'piped'
|
|
70
|
+
return this.childProcess && this.spawnOptions.stdout === 'piped'
|
|
71
|
+
? this.childProcess.stdout
|
|
72
|
+
: null;
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
get stderr() {
|
|
67
|
-
return this.spawnOptions.stderr === 'piped'
|
|
76
|
+
return this.childProcess && this.spawnOptions.stderr === 'piped'
|
|
77
|
+
? this.childProcess.stderr
|
|
78
|
+
: null;
|
|
68
79
|
}
|
|
69
80
|
|
|
70
81
|
get asDuplex() {
|
package/src/spawn/node.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
import {spawn} from 'node:child_process';
|
|
4
2
|
import {Readable, Writable} from 'node:stream';
|
|
5
3
|
|
|
@@ -29,7 +27,8 @@ class Subprocess {
|
|
|
29
27
|
this.killed = false;
|
|
30
28
|
this.finished = false;
|
|
31
29
|
|
|
32
|
-
const spawnOptions = {stdio: ['ignore', 'ignore', 'ignore']
|
|
30
|
+
const spawnOptions = {stdio: ['ignore', 'ignore', 'ignore']};
|
|
31
|
+
if (options.windowsVerbatimArguments) spawnOptions.windowsVerbatimArguments = true;
|
|
33
32
|
options.cwd && (spawnOptions.cwd = options.cwd);
|
|
34
33
|
options.env && (spawnOptions.env = options.env);
|
|
35
34
|
|
|
@@ -39,6 +38,22 @@ class Subprocess {
|
|
|
39
38
|
|
|
40
39
|
this.spawnOptions = spawnOptions;
|
|
41
40
|
|
|
41
|
+
let settled = false;
|
|
42
|
+
this.exited = new Promise((resolve, reject) => {
|
|
43
|
+
this.resolve = value => {
|
|
44
|
+
if (!settled) {
|
|
45
|
+
settled = true;
|
|
46
|
+
resolve(value);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
this.reject = value => {
|
|
50
|
+
if (!settled) {
|
|
51
|
+
settled = true;
|
|
52
|
+
reject(value);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
|
|
42
57
|
this.childProcess = spawn(command[0], command.slice(1), spawnOptions);
|
|
43
58
|
this.childProcess.on('exit', (code, signal) => {
|
|
44
59
|
this.finished = true;
|
|
@@ -51,11 +66,6 @@ class Subprocess {
|
|
|
51
66
|
this.reject(error);
|
|
52
67
|
});
|
|
53
68
|
|
|
54
|
-
this.exited = new Promise((resolve, reject) => {
|
|
55
|
-
this.resolve = resolve;
|
|
56
|
-
this.reject = reject;
|
|
57
|
-
});
|
|
58
|
-
|
|
59
69
|
this.stdin = this.childProcess.stdin && Writable.toWeb(this.childProcess.stdin);
|
|
60
70
|
this.stdout = this.childProcess.stdout && Readable.toWeb(this.childProcess.stdout);
|
|
61
71
|
this.stderr = this.childProcess.stderr && Readable.toWeb(this.childProcess.stderr);
|
package/src/utils.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
let getEnv, isWindows, isAndroid;
|
|
4
2
|
if (typeof Deno !== 'undefined') {
|
|
5
3
|
getEnv = name => Deno.env.get(name);
|
|
@@ -16,8 +14,7 @@ if (typeof Deno !== 'undefined') {
|
|
|
16
14
|
}
|
|
17
15
|
export {getEnv, isWindows, isAndroid};
|
|
18
16
|
|
|
19
|
-
export const verifyStrings = strings =>
|
|
20
|
-
Array.isArray(strings) && strings.every(s => typeof s === 'string');
|
|
17
|
+
export const verifyStrings = strings => Array.isArray(strings) && Array.isArray(strings.raw);
|
|
21
18
|
|
|
22
19
|
export const toBase64 = s => {
|
|
23
20
|
// const buf = new TextEncoder().encode(s),
|