diff --git a/src/main.ts b/src/main.ts index 5038d56..0f51a4f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -292,6 +292,27 @@ export class ExecProcess implements Result { nodeOptions.env = computeEnv(cwd, nodeOptions.env); + // Always use 'pipe' for stdout/stderr so we can capture output + // If user requested 'inherit', we will forward output manually + const wantInheritStdout = + nodeOptions.stdio === 'inherit' || + (Array.isArray(nodeOptions.stdio) && nodeOptions.stdio[1] === 'inherit'); + const wantInheritStderr = + nodeOptions.stdio === 'inherit' || + (Array.isArray(nodeOptions.stdio) && nodeOptions.stdio[2] === 'inherit'); + + // Force stdio to 'pipe' for stdout/stderr + if (nodeOptions.stdio === 'inherit') { + nodeOptions.stdio = ['inherit', 'pipe', 'pipe']; + } else if (Array.isArray(nodeOptions.stdio)) { + nodeOptions.stdio = [ + nodeOptions.stdio[0] ?? 'inherit', + 'pipe', + 'pipe', + ...nodeOptions.stdio.slice(3) + ]; + } + const {command: normalisedCommand, args: normalisedArgs} = normaliseCommandAndArgs(this._command, this._args); @@ -305,9 +326,19 @@ export class ExecProcess implements Result { if (handle.stderr) { this._streamErr = handle.stderr; + if (wantInheritStderr) { + handle.stderr.on('data', (chunk) => { + process.stderr.write(chunk); + }); + } } if (handle.stdout) { this._streamOut = handle.stdout; + if (wantInheritStdout) { + handle.stdout.on('data', (chunk) => { + process.stdout.write(chunk); + }); + } } this._process = handle; diff --git a/src/test/main_test.ts b/src/test/main_test.ts index 94367f6..9888df8 100644 --- a/src/test/main_test.ts +++ b/src/test/main_test.ts @@ -1,6 +1,6 @@ -import {x, NonZeroExitError} from '../main.js'; -import {describe, test, expect} from 'vitest'; import os from 'node:os'; +import {describe, expect, test, vi} from 'vitest'; +import {NonZeroExitError, x} from '../main.js'; const isWindows = os.platform() === 'win32'; @@ -38,15 +38,39 @@ describe('exec', async () => { }); test('resolves to stdout', async () => { + const stdoutWrite = vi.spyOn(process.stdout, 'write'); const result = await x('node', ['-e', "console.log('foo')"]); expect(result.stdout).toBe('foo\n'); expect(result.stderr).toBe(''); + expect(stdoutWrite).not.toHaveBeenCalled(); + }); + + test('resolves to stdout - with inherit', async () => { + const stdoutWrite = vi.spyOn(process.stdout, 'write'); + const result = await x('node', ['-e', "console.log('foo')"], { + nodeOptions: {stdio: 'inherit'} + }); + expect(result.stdout).toBe('foo\n'); + expect(result.stderr).toBe(''); + expect(stdoutWrite).toHaveBeenCalledWith(Buffer.from('foo\n')); }); test('captures stderr', async () => { + const stderrWrite = vi.spyOn(process.stderr, 'write'); const result = await x('node', ['-e', "console.error('some error')"]); expect(result.stderr).toBe('some error\n'); expect(result.stdout).toBe(''); + expect(stderrWrite).not.toHaveBeenCalled(); + }); + + test('captures stderr - with inherit', async () => { + const stderrWrite = vi.spyOn(process.stderr, 'write'); + const result = await x('node', ['-e', "console.error('some error')"], { + nodeOptions: {stdio: 'inherit'} + }); + expect(result.stderr).toBe('some error\n'); + expect(result.stdout).toBe(''); + expect(stderrWrite).toHaveBeenCalledWith(Buffer.from('some error\n')); }); });