From db3256baf619262e83617c04b3dacb39b977b0a3 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 12 May 2026 12:46:31 +0900 Subject: [PATCH] test: harden dist-bundle regression guard + Node smoke test Existing test only checked `globalThis.Bun` top-level destructures and `__require` calls. Add two new test cases: 1. Raw Bun runtime API scanner: scans dist/index.js for any `Bun.(` or `Bun..` call outside shim-safe patterns (runtime.Bun, globalThis.Bun, typeof Bun, and the "Bun is not defined" error-message string). Uses a negative lookbehind so shim indirection (`runtime.Bun.foo`) passes. 2. Node smoke test: imports dist/index.js under `node --input-type=module` and asserts stderr contains no `ReferenceError` and no `Bun is not defined`. The existing case 3 only checked exit code, which masked lazy-evaluation crashes that fire after import. Reading exports forces lazy module-level evaluation paths to run. --- src/shared/dist-bundle-bun-globals.test.ts | 111 +++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/src/shared/dist-bundle-bun-globals.test.ts b/src/shared/dist-bundle-bun-globals.test.ts index 4d8b7a1d8..08cfc97a9 100644 --- a/src/shared/dist-bundle-bun-globals.test.ts +++ b/src/shared/dist-bundle-bun-globals.test.ts @@ -1,9 +1,58 @@ +/// + import { existsSync } from "node:fs" import { describe, expect, test } from "bun:test" const DIST_INDEX = "dist/index.js" const GLOBAL_BUN_DESTRUCTURE = /^\s*(?:var|let|const)\s*\{[^}]*\}\s*=\s*globalThis\.Bun/gm const TOP_LEVEL_REQUIRE_CALL = "__require(" +const RAW_BUN_API_CALL = /(? 120 ? `${content.slice(0, 117)}...` : content + + return `${lineNumber}: ${truncated}` +} describe("dist bundle Bun globals", () => { test.skipIf(!existsSync(DIST_INDEX))("#given dist bundle #when scanned #then no globalThis.Bun destructures remain", async () => { @@ -62,4 +111,66 @@ describe("dist bundle Bun globals", () => { stderr: "", }) }) + + test.skipIf(!existsSync(DIST_INDEX))("#given dist bundle #when scanned for raw Bun runtime APIs #then no unshimmed Bun API calls remain", async () => { + expect(hasRawBunApiCall("Bun.file('dist/index.js')")).toBe(true) + expect(hasRawBunApiCall("runtime.Bun.file('dist/index.js')")).toBe(false) + expect(hasRawBunApiCall(".Bun.file('dist/index.js')")).toBe(false) + expect(hasRawBunApiCall("$Bun.file('dist/index.js')")).toBe(false) + expect(hasRawBunApiCall("Bun.spawnSync.options")).toBe(true) + expect(hasRawBunApiCall("Bun.readableStreamToText(stream)")).toBe(true) + + const dist = await Bun.file(DIST_INDEX).text() + const offending: string[] = [] + let insideJSDoc = false + + for (const [index, line] of dist.split("\n").entries()) { + const trimmed = line.trimStart() + + if (insideJSDoc || trimmed.startsWith("/**")) { + insideJSDoc = !trimmed.includes("*/") + continue + } + + if (line.includes("runtime.Bun") || line.includes("globalThis.Bun") || line.includes("typeof Bun")) { + continue + } + + RAW_BUN_API_CALL.lastIndex = 0 + const rawMatch = [...line.matchAll(RAW_BUN_API_CALL)].find( + (match) => match.index !== undefined && !isInsideStringLiteral(line, match.index), + ) + + if (rawMatch) { + offending.push(formatOffendingLine(index + 1, line)) + } + } + + expect( + offending, + `Expected zero raw Bun API calls in dist/index.js but found ${offending.length}:\n${offending.join("\n")}`, + ).toEqual([]) + }) + + test.skipIf(!existsSync(DIST_INDEX))("#given dist bundle #when imported and inspected under node --input-type=module #then stderr has no Bun reference errors", async () => { + const node = Bun.which("node") + if (!node) return + + const proc = Bun.spawn({ + cmd: [node, "--input-type=module", "-e", NODE_EXPORT_SMOKE_SCRIPT], + cwd: process.cwd(), + stdout: "pipe", + stderr: "pipe", + }) + + const stdout = await new Response(proc.stdout).text() + const stderr = await new Response(proc.stderr).text() + const exitCode = await proc.exited + const stderrLower = stderr.toLowerCase() + + expect(exitCode, stderr.trim()).toBe(0) + expect(stdout).toContain("SMOKE_OK:") + expect(stderrLower).not.toContain("referenceerror") + expect(stderr).not.toContain("Bun is not defined") + }) })