mirror of https://github.com/garrytan/gstack.git
fix(browse): forward windowsHide through the Bun polyfill spawn shims
The Node fallback shim accepts a Bun.spawn options object and forwards only stdio, env and cwd to child_process.spawn. windowsHide is dropped, and because Node defaults it to false while Bun.spawn hides the console window, the omission inverts the behavior on the one platform the shim exists to support. Symptom: the terminal-agent respawn in server.ts (60s watchdog ticker) pops a visible bun.exe console window on Windows every time it fires, so the window keeps coming back with no scheduled task or startup entry behind it. stdio:'ignore' silences the child's output but does not suppress its window. Both shims now forward the option and default it to true, matching the Bun API being emulated; an explicit windowsHide:false still passes through. spawnTerminalAgent also sets it explicitly at the call site. Tests: three cases in browse/test/bun-polyfill.test.ts assert the default for spawn and spawnSync and that an explicit false is honored. Each was confirmed to fail against the unpatched shim. Drive-by, required to run the suite at all on Windows: the tests interpolated an absolute path into a JS string literal, so backslashes were consumed as escapes and every require() failed with MODULE_NOT_FOUND. The path is now normalized to forward slashes. On Windows this file went from 0/4 passing to 7/7.
This commit is contained in:
parent
e1f734bfa5
commit
01adcf8e8e
|
|
@ -75,9 +75,11 @@ globalThis.Bun = {
|
|||
timeout: options.timeout,
|
||||
env: options.env,
|
||||
cwd: options.cwd,
|
||||
// Bun never shows a console window; node's spawn defaults windowsHide to
|
||||
// false, so without this every console child pops a window on Windows.
|
||||
windowsHide: true,
|
||||
// Node defaults windowsHide to false; Bun.spawn hides the console
|
||||
// window. Without this the shim silently inverts the behavior on the
|
||||
// one platform it exists to serve — every console child pops a window.
|
||||
// Forwarded (not hardcoded) so an explicit windowsHide:false survives.
|
||||
windowsHide: options.windowsHide !== false,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
@ -94,10 +96,12 @@ globalThis.Bun = {
|
|||
stdio,
|
||||
env: options.env,
|
||||
cwd: options.cwd,
|
||||
// See spawnSync. This is the one users notice: spawnTerminalAgent() launches
|
||||
// `bun run terminal-agent.ts` through here and the daemon respawns it on a
|
||||
// watchdog, so an empty console window reappears every few minutes.
|
||||
windowsHide: true,
|
||||
// stdio:'ignore' silences a child's output but does not suppress its
|
||||
// console window on Windows. The terminal-agent respawn (server.ts
|
||||
// watchdog, 60s ticker) popped a visible bun.exe window on every
|
||||
// respawn until this was forwarded. Forwarded, not hardcoded, so an
|
||||
// explicit windowsHide:false survives.
|
||||
windowsHide: options.windowsHide !== false,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -77,6 +77,10 @@ export function spawnTerminalAgent(opts: {
|
|||
...(opts.extraEnv || {}),
|
||||
},
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
// Explicit for the Node fallback path (dist/bun-polyfill.cjs), where the
|
||||
// host default is the opposite of Bun's. A visible console window on every
|
||||
// watchdog respawn is the symptom when this is missing.
|
||||
windowsHide: true,
|
||||
});
|
||||
proc.unref?.();
|
||||
return proc.pid ?? null;
|
||||
|
|
|
|||
|
|
@ -70,24 +70,47 @@ describe('bun-polyfill', () => {
|
|||
expect(lines[1]).toBe('HAS_PORT');
|
||||
});
|
||||
|
||||
// Regression: node defaults windowsHide to false, so a console child spawned
|
||||
// through this shim pops a visible window on Windows — bun never does. Asserted
|
||||
// by stubbing child_process before the polyfill destructures it, so the check is
|
||||
// deterministic and runs on every platform.
|
||||
test('spawn and spawnSync pass windowsHide so Windows shows no console window', () => {
|
||||
// windowsHide is the one option where Node's default is the opposite of
|
||||
// Bun's: Node shows the child's console window, Bun hides it. Dropping it
|
||||
// in translation makes every spawned child pop a window on Windows, which
|
||||
// is the platform this whole file exists for. Both shims are covered, and
|
||||
// an explicit windowsHide:false must survive forwarding (#2523 + #2539).
|
||||
test('Bun.spawn defaults windowsHide to true', () => {
|
||||
const result = Bun.spawnSync(['node', '-e', `
|
||||
const cp = require('child_process');
|
||||
const seen = [];
|
||||
cp.spawn = (c, a, o) => { seen.push(o); return { pid: 1, stdout: null, stderr: null, stdin: null, unref() {}, kill() {} }; };
|
||||
cp.spawnSync = (c, a, o) => { seen.push(o); return { status: 0, stdout: Buffer.from(''), stderr: Buffer.from('') }; };
|
||||
const orig = cp.spawn;
|
||||
let seen;
|
||||
cp.spawn = (c, a, o) => { seen = o; return orig(c, a, o); };
|
||||
require(${JSON.stringify(polyfillPath)});
|
||||
Bun.spawn(['echo', 'x'], { stdio: ['pipe', 'pipe', 'pipe'] });
|
||||
Bun.spawnSync(['echo', 'x'], { stdout: 'pipe' });
|
||||
console.log(seen.length === 2 ? 'BOTH' : 'GOT_' + seen.length);
|
||||
console.log(seen.every((o) => o && o.windowsHide === true) ? 'HIDDEN' : 'VISIBLE');
|
||||
Bun.spawn(['node', '-e', ''], { stdio: ['ignore', 'ignore', 'ignore'] });
|
||||
console.log('windowsHide:' + seen.windowsHide);
|
||||
`], { stdout: 'pipe', stderr: 'pipe' });
|
||||
const lines = result.stdout.toString().trim().split('\n');
|
||||
expect(lines[0]).toBe('BOTH');
|
||||
expect(lines[1]).toBe('HIDDEN');
|
||||
expect(result.stdout.toString().trim()).toBe('windowsHide:true');
|
||||
});
|
||||
|
||||
test('Bun.spawnSync defaults windowsHide to true', () => {
|
||||
const result = Bun.spawnSync(['node', '-e', `
|
||||
const cp = require('child_process');
|
||||
const orig = cp.spawnSync;
|
||||
let seen;
|
||||
cp.spawnSync = (c, a, o) => { seen = o; return orig(c, a, o); };
|
||||
require(${JSON.stringify(polyfillPath)});
|
||||
Bun.spawnSync(['node', '-e', '']);
|
||||
console.log('windowsHide:' + seen.windowsHide);
|
||||
`], { stdout: 'pipe', stderr: 'pipe' });
|
||||
expect(result.stdout.toString().trim()).toBe('windowsHide:true');
|
||||
});
|
||||
|
||||
test('an explicit windowsHide:false is honored', () => {
|
||||
const result = Bun.spawnSync(['node', '-e', `
|
||||
const cp = require('child_process');
|
||||
const orig = cp.spawn;
|
||||
let seen;
|
||||
cp.spawn = (c, a, o) => { seen = o; return orig(c, a, o); };
|
||||
require(${JSON.stringify(polyfillPath)});
|
||||
Bun.spawn(['node', '-e', ''], { stdio: ['ignore', 'ignore', 'ignore'], windowsHide: false });
|
||||
console.log('windowsHide:' + seen.windowsHide);
|
||||
`], { stdout: 'pipe', stderr: 'pipe' });
|
||||
expect(result.stdout.toString().trim()).toBe('windowsHide:false');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue