(cmd: string, opts?: { cwd?: string; input?: string })
| 77 | * to stdout; this surfaces both streams instead of swallowing them. |
| 78 | */ |
| 79 | export function runStreaming(cmd: string, opts?: { cwd?: string; input?: string }): Promise<void> { |
| 80 | const result = checkIntercept(cmd.split(/\s+/), opts); |
| 81 | if (result?.intercepted) { |
| 82 | if ('error' in result) return Promise.reject(new Error(result.error)); |
| 83 | return Promise.resolve(); |
| 84 | } |
| 85 | return new Promise((resolve, reject) => { |
| 86 | const child = spawn(cmd, { |
| 87 | cwd: opts?.cwd, |
| 88 | shell: true, |
| 89 | stdio: [opts?.input ? 'pipe' : 'inherit', 'pipe', 'pipe'], |
| 90 | }); |
| 91 | let stdout = ''; |
| 92 | let stderr = ''; |
| 93 | child.stdout?.on('data', (chunk) => { |
| 94 | stdout += chunk; |
| 95 | process.stdout.write(chunk); |
| 96 | }); |
| 97 | child.stderr?.on('data', (chunk) => { |
| 98 | stderr += chunk; |
| 99 | process.stderr.write(chunk); |
| 100 | }); |
| 101 | if (opts?.input) { |
| 102 | child.stdin?.write(opts.input); |
| 103 | child.stdin?.end(); |
| 104 | } |
| 105 | child.on('error', (err) => reject(err)); |
| 106 | child.on('close', (code) => { |
| 107 | if (code === 0) { |
| 108 | resolve(); |
| 109 | } else { |
| 110 | const detail = `${stdout}\n${stderr}`.trim(); |
| 111 | reject(new Error(`Command failed (exit code ${code}): ${cmd}${detail ? `\n${detail}` : ''}`)); |
| 112 | } |
| 113 | }); |
| 114 | }); |
| 115 | } |
| 116 | |
| 117 | export function tryRun(cmd: string, opts?: { cwd?: string }): string | null { |
| 118 | try { |
no test coverage detected
searching dependent graphs…