Skip to content

Make the bundler tests use the API by default in most cases - #22646

Merged
Jarred-Sumner merged 1 commit into
mainfrom
jarred/faster-bundler-tests
Sep 14, 2025
Merged

Jarred-Sumner merged 1 commit into
mainfrom
jarred/faster-bundler-tests

Conversation

@Jarred-Sumner

@Jarred-Sumner Jarred-Sumner commented Sep 14, 2025 •

Copy link
Copy Markdown
Collaborator

What does this PR do?

This branch:

Ran 1600 tests across 46 files. [63.24s]

Main:

Ran 1600 tests across 46 files. [137.05s]

This makes the bundler tests run about 60 seconds faster

How did you verify your code works?

@robobun

robobun commented Sep 14, 2025 •

Copy link
Copy Markdown
Collaborator
Updated 2:06 AM PT - Sep 14th, 2025

❌ @Jarred-Sumner, your commit f7b22b8 has 1 failures in Build #26000 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 22646

That installs a local version of the PR into your bun-22646 executable, so you can run:

bun-22646 --bun

@coderabbitai

coderabbitai Bot commented Sep 14, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Added errdefer cleanup for ArrayList instances in the Zig JSON parser. Updated test harness to change default backend resolution and run Bun.build with a temporary CWD. Multiple bundler tests now explicitly set backend: "cli".

Changes

Cohort / File(s) Summary of Changes
JSON parser cleanup
src/interchange/json.zig
Added errdefer deinitialization for locally-initialized ArrayLists in JSONLikeParser_.
Test harness backend logic & CWD handling
test/bundler/expectBundled.ts
Reworked default backend selection conditions; now picks "cli" under several option flags, else "api". Wrapped Bun.build with temporary chdir to bundle root and restored CWD in finally.
Tests switched to CLI backend
test/bundler/bundler_compile.test.ts, test/bundler/bundler_edgecase.test.ts, test/bundler/bundler_footer.test.ts, test/bundler/bundler_regressions.test.ts, test/bundler/compile-argv.test.ts, test/bundler/esbuild/dce.test.ts
Added backend: "cli" to selected itBundled test cases; no other test logic changes.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description contains timing numbers and a claim of a ~60s speedup but does not describe the actual code changes (which files and logic were modified) and leaves the "How did you verify your code works?" section empty, so it does not satisfy the repository's required template content. Because the template asks for both a summary of what the PR does and verification steps, the current description is incomplete. Please expand the PR description to explicitly summarize the implementation (e.g., changes to test/bundler/expectBundled.ts to default to the API, tests updated to set backend: "cli" where needed, and the small json.zig cleanup), and fill in "How did you verify your code works?" with concrete verification steps and evidence such as the exact commands run, test/CI logs or benchmark output, environment details, and any observed behavioral differences so reviewers can reproduce and confirm the claimed speedup.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title accurately and concisely summarizes the primary change: the tests and test harness were adjusted so the bundler tests use the API as the default in most cases while explicitly selecting the CLI backend for specific tests, which matches the changes to expectBundled.ts and the individual tests shown in the diff. The wording is clear and specific enough for a teammate scanning history to understand the main intent.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jarred/faster-bundler-tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
test/bundler/compile-argv.test.ts (2)

10-10: Pinning these tests to the CLI backend is the right call.

This locks the intended compile/execArgv semantics regardless of harness default-backend heuristics. Consider adding a short inline comment so future readers don’t “simplify” it away.

Apply this small annotation for clarity:

-    backend: "cli",
+    // Force CLI backend: validate compile execArgv vs argv separation using the CLI bundler.
+    backend: "cli",

Also applies to: 56-56, 120-120


86-88: Make argv[0] assertion OS‑agnostic to avoid flakiness.

These assertions hardcode argv[0] === "bun", which may differ (e.g., bun.exe on Windows). Prefer a suffix or regex match.

-        if (process.argv[0] !== "bun") {
+        if (!/bun(\.exe)?$/i.test(process.argv[0])) {
           console.error("FAIL: Expected argv[0] to be 'bun', got", process.argv[0]);
           process.exit(1);
         }

Please run this file on Windows in CI to confirm no regressions in argv semantics for the CLI backend.

Also applies to: 144-146

test/bundler/bundler_compile.test.ts (1)

109-109: Explicitly using the CLI backend for these compile cases is appropriate.

These scenarios (worker relative paths, bytecode, and import.meta.main inlining) are sensitive to backend differences; pinning avoids accidental drift if harness defaults change.

Add a brief rationale to each to prevent future “cleanup”:

-  itBundled("compile/WorkerRelativePathNoExtension", {
-    backend: "cli",
+  itBundled("compile/WorkerRelativePathNoExtension", {
+    // Force CLI backend: validate worker path resolution under compile.
+    backend: "cli",
-  itBundled("compile/WorkerRelativePathTSExtension", {
-    backend: "cli",
+  itBundled("compile/WorkerRelativePathTSExtension", {
+    // Force CLI backend: validate worker .ts extension handling under compile.
+    backend: "cli",
-  itBundled("compile/WorkerRelativePathTSExtensionBytecode", {
-    backend: "cli",
+  itBundled("compile/WorkerRelativePathTSExtensionBytecode", {
+    // Force CLI backend: bytecode + worker path resolution via CLI bundler.
+    backend: "cli",
-  itBundled("compile/ImportMetaMain", {
-    compile: true,
-    backend: "cli",
+  itBundled("compile/ImportMetaMain", {
+    compile: true,
+    // Force CLI backend: ensures import.meta.main and require.main semantics from CLI compile.
+    backend: "cli",

Also applies to: 129-129, 148-148, 564-564

test/bundler/bundler_footer.test.ts (1)

7-7: Good: footers validated under the CLI backend.

Footer emission can vary by backend; pinning to CLI removes ambiguity. Consider adding a short comment to codify the intent.

-    backend: "cli",
+    // Force CLI backend: verify footer is appended by CLI bundler.
+    backend: "cli",

Also applies to: 20-20

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ecd23df and f7b22b8.

📒 Files selected for processing (8)
  • src/interchange/json.zig (2 hunks)
  • test/bundler/bundler_compile.test.ts (4 hunks)
  • test/bundler/bundler_edgecase.test.ts (2 hunks)
  • test/bundler/bundler_footer.test.ts (2 hunks)
  • test/bundler/bundler_regressions.test.ts (1 hunks)
  • test/bundler/compile-argv.test.ts (3 hunks)
  • test/bundler/esbuild/dce.test.ts (1 hunks)
  • test/bundler/expectBundled.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
test/**

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

Place all tests under the test/ directory

Files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/compile-argv.test.ts
  • test/bundler/bundler_edgecase.test.ts
  • test/bundler/expectBundled.ts
  • test/bundler/bundler_regressions.test.ts
  • test/bundler/esbuild/dce.test.ts
  • test/bundler/bundler_footer.test.ts
test/bundler/**/*

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

Place bundler/transpiler/CSS/bun build tests under test/bundler/

Files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/compile-argv.test.ts
  • test/bundler/bundler_edgecase.test.ts
  • test/bundler/expectBundled.ts
  • test/bundler/bundler_regressions.test.ts
  • test/bundler/esbuild/dce.test.ts
  • test/bundler/bundler_footer.test.ts
test/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

test/**/*.{js,ts}: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable

Files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/compile-argv.test.ts
  • test/bundler/bundler_edgecase.test.ts
  • test/bundler/expectBundled.ts
  • test/bundler/bundler_regressions.test.ts
  • test/bundler/esbuild/dce.test.ts
  • test/bundler/bundler_footer.test.ts
test/**/*.test.ts

📄 CodeRabbit inference engine (test/CLAUDE.md)

test/**/*.test.ts: Name test files *.test.ts and use bun:test
Do not write flaky tests: never wait for arbitrary time; wait for conditions instead
Never hardcode port numbers in tests; use port: 0 to get a random port
When spawning Bun in tests, use bunExe() and bunEnv from harness
Prefer async/await in tests; for a single callback, use Promise.withResolvers()
Do not set explicit test timeouts; rely on Bun’s built-in timeouts
Use tempDir/tempDirWithFiles from harness for temporary files and directories in tests
For large/repetitive strings in tests, prefer Buffer.alloc(count, fill).toString() over "A".repeat(count)
Import common test utilities from harness (e.g., bunExe, bunEnv, tempDirWithFiles, tmpdirSync, platform checks, GC helpers)
In error tests, assert non-zero exit codes for failing processes and use toThrow for synchronous errors
Use describe blocks for grouping, describe.each for parameterized tests, snapshots with toMatchSnapshot, and lifecycle hooks (beforeAll, beforeEach, afterEach); track resources for cleanup in afterEach
Use using/await using with Bun resources (e.g., Bun.listen/connect/spawn/serve) to ensure cleanup in tests

Files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/compile-argv.test.ts
  • test/bundler/bundler_edgecase.test.ts
  • test/bundler/bundler_regressions.test.ts
  • test/bundler/esbuild/dce.test.ts
  • test/bundler/bundler_footer.test.ts
test/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

test/**/*.test.{ts,tsx}: Test files must be placed under test/ and end with .test.ts or .test.tsx
In tests, always use port: 0; do not hardcode ports or use custom random port functions
In tests, use normalizeBunSnapshot when asserting snapshots
Never write tests that merely assert absence of "panic" or "uncaught exception" in output
Avoid shell commands (e.g., find, grep) in tests; use Bun.Glob and built-ins instead
Prefer snapshot tests over exact stdout equality assertions

Files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/compile-argv.test.ts
  • test/bundler/bundler_edgecase.test.ts
  • test/bundler/bundler_regressions.test.ts
  • test/bundler/esbuild/dce.test.ts
  • test/bundler/bundler_footer.test.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Format JavaScript/TypeScript files with Prettier (bun run prettier)

Files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/compile-argv.test.ts
  • test/bundler/bundler_edgecase.test.ts
  • test/bundler/expectBundled.ts
  • test/bundler/bundler_regressions.test.ts
  • test/bundler/esbuild/dce.test.ts
  • test/bundler/bundler_footer.test.ts
src/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

Implement debug logs in Zig using const log = bun.Output.scoped(.${SCOPE}, false); and invoking log("...", .{})

Files:

  • src/interchange/json.zig
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

**/*.zig: Format Zig files with zig-format (bun run zig-format)
In Zig, manage memory carefully with allocators and use defer for cleanup

Files:

  • src/interchange/json.zig
🧠 Learnings (15)
📓 Common learnings
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: For platform-specific changes, run bun run zig:check-all to compile on all platforms
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/{**/js_*.zig,bun.js/api/**/*.zig} : Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to **/*.zig : In Zig, manage memory carefully with allocators and use defer for cleanup
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
📚 Learning: 2025-08-30T00:09:39.100Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.100Z
Learning: Applies to test/bake/dev/bundle.test.ts : bundle.test.ts should contain DevServer-specific bundling tests

Applied to files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/compile-argv.test.ts
  • test/bundler/bundler_edgecase.test.ts
  • test/bundler/expectBundled.ts
  • test/bundler/bundler_regressions.test.ts
  • test/bundler/esbuild/dce.test.ts
  • test/bundler/bundler_footer.test.ts
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/cli/**/*.{js,ts} : When testing Bun as a CLI, use spawn with bunExe() and bunEnv from harness, and capture stdout/stderr via pipes

Applied to files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/compile-argv.test.ts
  • test/bundler/bundler_edgecase.test.ts
  • test/bundler/expectBundled.ts
  • test/bundler/bundler_regressions.test.ts
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : When spawning Bun in tests, use `bunExe()` and `bunEnv` from `harness`

Applied to files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/expectBundled.ts
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/bundler/**/* : Place bundler/transpiler/CSS/bun build tests under test/bundler/

Applied to files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/expectBundled.ts
📚 Learning: 2025-08-30T00:09:39.100Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.100Z
Learning: Applies to test/bake/dev/css.test.ts : css.test.ts should contain CSS bundling tests in dev mode

Applied to files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/bundler_edgecase.test.ts
  • test/bundler/expectBundled.ts
  • test/bundler/bundler_regressions.test.ts
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/**/*.{js,ts} : Use shared utilities from test/harness.ts where applicable

Applied to files:

  • test/bundler/bundler_compile.test.ts
📚 Learning: 2025-09-07T05:41:52.563Z
Learnt from: CR
PR: oven-sh/bun#0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-09-07T05:41:52.563Z
Learning: Follow the build pipeline: Source TS/JS → Preprocessor → Bundler → C++ Headers; IDs assigned A–Z; `$` replaced with `__intrinsic__`; `require("x")` replaced with `$requireId(n)`; `export default` converted to `return`; `__intrinsic__` replaced with `@`; inlined into C++; modules loaded by numeric ID

Applied to files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/expectBundled.ts
📚 Learning: 2025-08-30T00:09:39.100Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.100Z
Learning: Applies to test/bake/dev/esm.test.ts : esm.test.ts should cover ESM feature behavior in development mode

Applied to files:

  • test/bundler/bundler_compile.test.ts
  • test/bundler/expectBundled.ts
  • test/bundler/esbuild/dce.test.ts
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/cli/**/*.{js,ts} : Place CLI command tests (e.g., bun install, bun init) under test/cli/

Applied to files:

  • test/bundler/compile-argv.test.ts
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to **/*.zig : In Zig, manage memory carefully with allocators and use defer for cleanup

Applied to files:

  • src/interchange/json.zig
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/**/js_*.zig : Always implement proper cleanup in deinit() and finalize() for JS-exposed types

Applied to files:

  • src/interchange/json.zig
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/{**/js_*.zig,bun.js/api/**/*.zig} : Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Applied to files:

  • src/interchange/json.zig
📚 Learning: 2025-09-07T05:41:52.563Z
Learnt from: CR
PR: oven-sh/bun#0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-09-07T05:41:52.563Z
Learning: Applies to src/js/{builtins,node,bun,thirdparty,internal}/**/*.{js,ts} : Author modules as CommonJS-style with `require(...)` and export via `export default {}` (no ESM `import`/named exports)

Applied to files:

  • test/bundler/expectBundled.ts
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/js/bun/**/*.{js,ts} : Place Bun API tests under test/js/bun/, separated by category (e.g., test/js/bun/glob/)

Applied to files:

  • test/bundler/expectBundled.ts
🔇 Additional comments (7)
test/bundler/bundler_regressions.test.ts (1)

219-219: Explicitly pinning backend to CLI is appropriate here.

This test uses entryPointsRaw and expects CLI-specific behavior; forcing backend: "cli" avoids accidental API execution as the harness defaults evolve.

Please confirm no other tests relying on entryPointsRaw remain without backend: "cli".

src/interchange/json.zig (2)

207-208: Good: errdefer for properties mirrors array handling.

Matches the array case and prevents leaks when object parsing errors early.


173-174: Approve — errdefer correctly frees exprs on error; run Zig checks locally
errdefer ensures the exprs ArrayList storage is freed on error paths; moveFromList transfers ownership so the happy path is unaffected.
Location: src/interchange/json.zig (lines 173–174)
Run locally: bun run zig:check-all (sandbox couldn't execute this: bun not found / nl unavailable).

test/bundler/bundler_edgecase.test.ts (2)

277-277: Good: force CLI for invalid loader case.

The asserted error string is CLI-specific; pinning backend avoids false negatives when API is chosen by default.


2075-2075: Good: force CLI for multi-output + outfile error.

The harness would otherwise preempt with its own validation; this ensures the test checks Bun CLI’s error message.

test/bundler/esbuild/dce.test.ts (1)

1245-1245: Stabilize DCE annotation tests by pinning CLI.

Ensures consistent semantics across variants (minify/emitDCEAnnotations) regardless of default backend heuristics.

test/bundler/expectBundled.ts (1)

575-591: ```shell
#!/bin/bash
set -euo pipefail

FILE="test/bundler/expectBundled.ts"

echo "=== Checking repository for ${FILE} ==="
if [ ! -f "$FILE" ]; then
echo "FILE_NOT_FOUND"
exit 0
fi

echo
echo "=== File excerpt (lines 540-610) ==="
awk 'NR>=540 && NR<=610{printf("%6d %s\n", NR, $0)}' "$FILE" || true

echo
echo "=== Search: 'backend =' occurrences ==="
if command -v rg >/dev/null 2>&1; then
rg -n --hidden --no-ignore "backend\s*=" || true
else
grep -RIn --exclude-dir=.git --exclude-dir=node_modules -E "backend\s*=" || true
fi

echo
echo "=== Search: backend declarations (let|var|const backend) ==="
if command -v rg >/dev/null 2>&1; then
rg -n --hidden --no-ignore "\b(let|var|const)\s+backend\b" || true
else
grep -RIn --exclude-dir=.git --exclude-dir=node_modules -E "\b(let|var|const)\s+backend\b" || true
fi

echo
echo "=== Search: chooseBundlerBackend (collision check) ==="
if command -v rg >/dev/null 2>&1; then
rg -n --hidden --no-ignore "chooseBundlerBackend" || true
else
grep -RIn --exclude-dir=.git --exclude-dir=node_modules -e "chooseBundlerBackend" || true
fi

echo
echo "=== Occurrences of variables used in the expression ==="
for v in dotenv jsx production bundling run target emitDCEAnnotations bundleWarnings env define; do
echo
echo "---- $v ----"
if command -v rg >/dev/null 2>&1; then
rg -n --hidden --no-ignore "\b$v\b" || true
else
grep -RIn --exclude-dir=.git --exclude-dir=node_modules -E "\b$v\b" || true
fi
done

echo
echo "=== Done ==="


</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +1125 to +1131
const cwd = process.cwd();
process.chdir(root);
try {
build = await Bun.build(buildConfig);
} finally {
process.chdir(cwd);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Potential flakiness: global process.chdir around Bun.build is not concurrency‑safe.

If tests run concurrently in the same process, chdir() can race and misdirect builds. Serialize these sections or avoid chdir entirely.

Apply this diff to use a simple async mutex around cwd switches:

-          const cwd = process.cwd();
-          process.chdir(root);
-          try {
-            build = await Bun.build(buildConfig);
-          } finally {
-            process.chdir(cwd);
-          }
+          build = await withCwd(root, () => Bun.build(buildConfig));

Add this helper once (outside the hunk, top-level of this file):

let __cwdQueue: Promise<void> = Promise.resolve();
async function withCwd<T>(dir: string, fn: () => Promise<T> | T): Promise<T> {
  let release!: () => void;
  const next = new Promise<void>(r => (release = r));
  const prev = __cwdQueue;
  __cwdQueue = next;
  await prev;
  const old = process.cwd();
  process.chdir(dir);
  try {
    return await fn();
  } finally {
    process.chdir(old);
    release();
  }
}

Alternatively, remove the need to chdir by ensuring all API entrypoints/paths are absolute for the API path.

Please run the bundler suite with parallelism enabled to smoke-test races:


🏁 Script executed:

#!/bin/bash
# from repo root
bun test test/bundler -j $(nproc)

Length of output: 85


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "PWD: $(pwd)"
echo "Checking for ripgrep (rg)..."
if command -v rg >/dev/null 2>&1; then
  echo "rg: found"
  RG=rg
else
  echo "rg: not found, will use grep"
  RG=grep
fi

echo
echo "Searching for occurrences of process.chdir across repo..."
if [ "$RG" = "rg" ]; then
  rg -n -C3 "process.chdir\(" || true
else
  grep -RIn --line-number --binary-files=without-match -H "process.chdir(" . || true
fi

echo
echo "Showing lines 1108-1140 of test/bundler/expectBundled.ts (if present)"
if [ -f test/bundler/expectBundled.ts ]; then
  sed -n '1108,1140p' test/bundler/expectBundled.ts || true
else
  echo "File not found: test/bundler/expectBundled.ts"
fi

echo
echo "Searching for Bun.build occurrences..."
if [ "$RG" = "rg" ]; then
  rg -n -C2 "Bun.build\(" || true
else
  grep -RIn --line-number --binary-files=without-match -H "Bun.build(" . || true
fi

echo
echo "Searching for existing withCwd or __cwdQueue helpers..."
if [ "$RG" = "rg" ]; then
  rg -n "withCwd|__cwdQueue" || true
else
  grep -RIn --line-number -E "withCwd|__cwdQueue" . || true
fi

echo
echo "Listing process.chdir occurrences under test/ (if any)..."
if [ "$RG" = "rg" ]; then
  rg -n --hidden --no-ignore --glob 'test/**' "process.chdir\(" || true
else
  grep -RIn --line-number --include='test/*' "process.chdir(" . || true
fi

Length of output: 108380


Serialize/avoid global process.chdir around Bun.build (prevents race)

  • test/bundler/expectBundled.ts (lines 1125–1131): replace the manual process.chdir block with either the existing test harness cwdScope (test/harness.ts:1568–1576) for scoped cleanup, or — if tests run in parallel — wrap the operation in a global async mutex (e.g. withCwd(root, () => Bun.build(buildConfig))) to serialize chdir.
  • Prefer removing chdir entirely by using absolute paths for Bun.build when possible.

@Jarred-Sumner
Jarred-Sumner merged commit 9411c62 into main Sep 14, 2025
62 of 64 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the jarred/faster-bundler-tests branch September 14, 2025 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants