From 798b48c898fcf596e27c8fffb5abfb3b838ca3d1 Mon Sep 17 00:00:00 2001 From: robobun Date: Wed, 14 Jan 2026 17:41:21 -0800 Subject: [PATCH] fix(runtime): exclude globalThis from auto-serve detection (#26107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - When a module exports `globalThis` (e.g., `module.exports = globalThis`), Bun's auto-serve detection incorrectly triggered because `globalThis.fetch` is the Fetch API function - Scripts that export globalThis (like `core-js/es/global-this.js`) would start a development server on port 3000 instead of exiting normally - Added explicit check to skip auto-serve when the default export is `globalThis` itself Fixes #440 ## Test plan - [x] Added test case `test/regression/issue/440.test.ts` that verifies: - `module.exports = globalThis` does not start a server - `export default globalThis` does not start a server - [x] Verified test fails with system Bun (without fix) - [x] Verified test passes with debug build (with fix) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Bot Co-authored-by: Claude Opus 4.5 --- src/bundler/entry_points.zig | 8 +++--- test/regression/issue/440.test.ts | 46 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 test/regression/issue/440.test.ts diff --git a/src/bundler/entry_points.zig b/src/bundler/entry_points.zig index ca3196bf15..fdf2df2d1e 100644 --- a/src/bundler/entry_points.zig +++ b/src/bundler/entry_points.zig @@ -169,7 +169,7 @@ pub const ServerEntryPoint = struct { \\if (typeof entryNamespace?.then === 'function') {{ \\ entryNamespace = entryNamespace.then((entryNamespace) => {{ \\ var def = entryNamespace?.default; - \\ if (def && (typeof def.fetch === 'function' || def.app != undefined)) {{ + \\ if (def && def !== globalThis && (typeof def.fetch === 'function' || def.app != undefined)) {{ \\ var server = globalThis[hmrSymbol]; \\ if (server) {{ \\ server.reload(def); @@ -180,7 +180,7 @@ pub const ServerEntryPoint = struct { \\ }} \\ }} \\ }}, reportError); - \\}} else if (typeof entryNamespace?.default?.fetch === 'function' || entryNamespace?.default?.app != undefined) {{ + \\}} else if (entryNamespace?.default !== globalThis && (typeof entryNamespace?.default?.fetch === 'function' || entryNamespace?.default?.app != undefined)) {{ \\ var server = globalThis[hmrSymbol]; \\ if (server) {{ \\ server.reload(entryNamespace.default); @@ -204,12 +204,12 @@ pub const ServerEntryPoint = struct { \\var entryNamespace = start; \\if (typeof entryNamespace?.then === 'function') {{ \\ entryNamespace = entryNamespace.then((entryNamespace) => {{ - \\ if (typeof entryNamespace?.default?.fetch === 'function') {{ + \\ if (entryNamespace?.default !== globalThis && typeof entryNamespace?.default?.fetch === 'function') {{ \\ const server = Bun.serve(entryNamespace.default); \\ console.debug(`Started ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`); \\ }} \\ }}, reportError); - \\}} else if (typeof entryNamespace?.default?.fetch === 'function' || entryNamespace?.default?.app != null) {{ + \\}} else if (entryNamespace?.default !== globalThis && (typeof entryNamespace?.default?.fetch === 'function' || entryNamespace?.default?.app != null)) {{ \\ const server = Bun.serve(entryNamespace.default); \\ console.debug(`Started ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`); \\}} diff --git a/test/regression/issue/440.test.ts b/test/regression/issue/440.test.ts new file mode 100644 index 0000000000..4a915e903c --- /dev/null +++ b/test/regression/issue/440.test.ts @@ -0,0 +1,46 @@ +import { expect, test } from "bun:test"; +import { bunEnv, bunExe, tempDir } from "harness"; + +test("exporting globalThis should not start a server", async () => { + using dir = tempDir("issue-440", { + "test.js": `module.exports = globalThis;`, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "test.js"], + env: bunEnv, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + // Should NOT contain server started message + expect(stderr).not.toContain("Started"); + expect(stderr).not.toContain("server:"); + expect(stdout).toBe(""); + expect(exitCode).toBe(0); +}); + +test("default export of globalThis should not start a server", async () => { + using dir = tempDir("issue-440-esm", { + "test.js": `export default globalThis;`, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "test.js"], + env: bunEnv, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + // Should NOT contain server started message + expect(stderr).not.toContain("Started"); + expect(stderr).not.toContain("server:"); + expect(stdout).toBe(""); + expect(exitCode).toBe(0); +});