@sandro-sikic/maker 1.0.10 → 1.0.11
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/docs/API.md +6 -2
- package/index.d.ts +1 -1
- package/index.js +14 -4
- package/package.json +1 -1
- package/test/index.test.js +57 -0
package/docs/API.md
CHANGED
|
@@ -21,13 +21,17 @@ Exits with code 1 if not running in an interactive terminal (TTY).
|
|
|
21
21
|
Execute shell commands with streaming output.
|
|
22
22
|
|
|
23
23
|
```javascript
|
|
24
|
-
const result = await run('npm install', {
|
|
24
|
+
const result = await run('npm install', {
|
|
25
|
+
maxLines: 10000,
|
|
26
|
+
cwd: process.cwd(),
|
|
27
|
+
});
|
|
25
28
|
```
|
|
26
29
|
|
|
27
30
|
**Parameters:**
|
|
28
31
|
|
|
29
32
|
- `command` - Shell command string (required)
|
|
30
|
-
- `opts.maxLines` - Max output lines to capture (default: 10000)
|
|
33
|
+
- `opts.maxLines` - Max output lines to capture (default: 10000). This option is used only for capturing output and is not forwarded to the spawned process.
|
|
34
|
+
- `opts` (other properties) - Any additional top-level properties are forwarded directly to `child_process.spawn` (e.g. `cwd`, `env`, `stdio`).
|
|
31
35
|
|
|
32
36
|
**Returns:**
|
|
33
37
|
|
package/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type RunResult = {
|
|
|
11
11
|
|
|
12
12
|
export function run(
|
|
13
13
|
command: string,
|
|
14
|
-
opts?: { maxLines?: number },
|
|
14
|
+
opts?: Partial<import('child_process').SpawnOptions> & { maxLines?: number },
|
|
15
15
|
): Promise<RunResult>;
|
|
16
16
|
|
|
17
17
|
export function onExit(cb: () => void | Promise<void>): () => void;
|
package/index.js
CHANGED
|
@@ -18,9 +18,10 @@ function init() {
|
|
|
18
18
|
* task; if you `await run(...)` (use inside an async function) the caller will wait for completion
|
|
19
19
|
* and the call behaves like a foreground task.
|
|
20
20
|
*
|
|
21
|
-
* @param {string} command - Shell command to execute
|
|
21
|
+
* @param {string} command - Shell command to execute.
|
|
22
22
|
* @param {Object} [opts]
|
|
23
|
-
* @param {number} [opts.maxLines=10000] - Maximum number of lines to retain in captured output.
|
|
23
|
+
* @param {number} [opts.maxLines=10000] - Maximum number of lines to retain in captured output. (capture-only; not forwarded to the spawned process)
|
|
24
|
+
* @param {Object} [opts] - Options forwarded directly to `child_process.spawn` (e.g. `cwd`, `env`, `stdio`).
|
|
24
25
|
* @returns {Promise<{output:string, stdout:string, stderr:string, code:number|null, isError:boolean, error:Error|null}>}
|
|
25
26
|
*/
|
|
26
27
|
async function run(command, opts = {}) {
|
|
@@ -28,10 +29,19 @@ async function run(command, opts = {}) {
|
|
|
28
29
|
throw new TypeError('run() requires a non-empty string command');
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
// extract maxLines (used for capturing) and forward the rest of opts directly to spawn
|
|
33
|
+
const { maxLines = 10000, ...forwardedOpts } = opts;
|
|
34
|
+
|
|
32
35
|
// import spawn at runtime so test mocks (vi.mock) take effect per-test
|
|
33
36
|
const { spawn } = await import('child_process');
|
|
34
37
|
|
|
38
|
+
// Merge defaults with the caller-provided options. Caller options are forwarded
|
|
39
|
+
// directly to spawn; `maxLines` is used only for output capture (not forwarded).
|
|
40
|
+
const spawnOpts = Object.assign(
|
|
41
|
+
{ shell: true, stdio: 'pipe' },
|
|
42
|
+
forwardedOpts,
|
|
43
|
+
);
|
|
44
|
+
|
|
35
45
|
function trimToLastNLines(s, n) {
|
|
36
46
|
if (!s) return s;
|
|
37
47
|
// remove trailing newlines for accurate line counting
|
|
@@ -44,7 +54,7 @@ async function run(command, opts = {}) {
|
|
|
44
54
|
}
|
|
45
55
|
|
|
46
56
|
return new Promise((resolve) => {
|
|
47
|
-
const child = spawn(command,
|
|
57
|
+
const child = spawn(command, spawnOpts);
|
|
48
58
|
|
|
49
59
|
let stdout = '';
|
|
50
60
|
let stderr = '';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sandro-sikic/maker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A lightweight library for building interactive command-line tools with prompts, command execution, spinners, and graceful shutdown handling",
|
|
6
6
|
"main": "index.js",
|
package/test/index.test.js
CHANGED
|
@@ -405,6 +405,63 @@ describe('run() - additional edge cases', () => {
|
|
|
405
405
|
vi.resetModules();
|
|
406
406
|
});
|
|
407
407
|
|
|
408
|
+
it('forwards opts directly to spawn (no alias)', async () => {
|
|
409
|
+
vi.resetModules();
|
|
410
|
+
let captured;
|
|
411
|
+
vi.doMock('child_process', () => ({
|
|
412
|
+
spawn: (cmd, spawnOpts) => {
|
|
413
|
+
captured = spawnOpts;
|
|
414
|
+
const child = {
|
|
415
|
+
stdout: { on: () => {} },
|
|
416
|
+
stderr: { on: () => {} },
|
|
417
|
+
on: (ev, cb) => {
|
|
418
|
+
if (ev === 'close') cb(0);
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
return child;
|
|
422
|
+
},
|
|
423
|
+
}));
|
|
424
|
+
const { run: mockedRun } = await import('../index.js');
|
|
425
|
+
const res = await mockedRun('anything', {
|
|
426
|
+
cwd: '/tmp',
|
|
427
|
+
env: { FOO: 'bar' },
|
|
428
|
+
});
|
|
429
|
+
expect(captured.cwd).toBe('/tmp');
|
|
430
|
+
expect(captured.env).toEqual(expect.objectContaining({ FOO: 'bar' }));
|
|
431
|
+
expect(captured.shell).toBe(true);
|
|
432
|
+
expect(captured.stdio).toBe('pipe');
|
|
433
|
+
expect(res.code).toBe(0);
|
|
434
|
+
vi.resetModules();
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('forwards top-level spawn options (maxLines is capture-only)', async () => {
|
|
438
|
+
vi.resetModules();
|
|
439
|
+
let captured;
|
|
440
|
+
vi.doMock('child_process', () => ({
|
|
441
|
+
spawn: (cmd, spawnOpts) => {
|
|
442
|
+
captured = spawnOpts;
|
|
443
|
+
const child = {
|
|
444
|
+
stdout: { on: () => {} },
|
|
445
|
+
stderr: { on: () => {} },
|
|
446
|
+
on: (ev, cb) => {
|
|
447
|
+
if (ev === 'close') cb(0);
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
return child;
|
|
451
|
+
},
|
|
452
|
+
}));
|
|
453
|
+
const { run: mockedRun } = await import('../index.js');
|
|
454
|
+
const res = await mockedRun('anything', {
|
|
455
|
+
cwd: '/cwd',
|
|
456
|
+
maxLines: 5,
|
|
457
|
+
shell: false,
|
|
458
|
+
});
|
|
459
|
+
expect(captured.cwd).toBe('/cwd');
|
|
460
|
+
expect(captured.maxLines).toBeUndefined();
|
|
461
|
+
expect(captured.shell).toBe(false);
|
|
462
|
+
expect(res.code).toBe(0);
|
|
463
|
+
vi.resetModules();
|
|
464
|
+
});
|
|
408
465
|
it('handles multiple data chunks correctly', async () => {
|
|
409
466
|
vi.resetModules();
|
|
410
467
|
vi.doMock('child_process', () => ({
|