Skip to content

Conversation

robobun
Copy link
Collaborator

@robobun robobun commented Oct 13, 2025

Fixes #23569

Summary

HTML imports require bundling to work correctly, as they need to process and transform linked assets (JS/CSS). When --no-bundle is used, no bundling or transformation happens, which causes a crash.

This change adds validation to detect HTML entrypoints when --no-bundle is used and provides a clear error message explaining that "HTML imports are only supported when bundling".

Changes

  • Added validation in src/cli/build_command.zig to check for HTML entrypoints when --no-bundle flag is used
  • Shows clear error message: "HTML imports are only supported when bundling"
  • Added regression tests in test/regression/issue/23569.test.ts

Test Plan

Before

$ bun build ./index.html --no-bundle
# Crashes without helpful error

After

$ bun build ./index.html --no-bundle
error: HTML imports are only supported when bundling

Tests

  • ✅ Test with --no-bundle flag errors correctly
  • ✅ Test with --no-bundle --outdir errors correctly
  • ✅ Test without --no-bundle works normally
  • ✅ All 3 regression tests pass

🤖 Generated with Claude Code

Co-Authored-By: Claude [email protected]

)

HTML imports require bundling to work correctly, as they need to process
and transform linked assets (JS/CSS). When --no-bundle is used, no bundling
or transformation happens, which causes a crash.

This change adds validation to detect HTML entrypoints when --no-bundle is
used and provides a clear error message explaining that "HTML imports are
only supported when bundling".

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@robobun
Copy link
Collaborator Author

robobun commented Oct 13, 2025

Updated 7:48 AM PT - Oct 13th, 2025

❌ Your commit c6b44756 has 4 failures in Build #29075 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 23572

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

bun-23572 --bun

Copy link
Contributor

coderabbitai bot commented Oct 13, 2025

Walkthrough

Adds a runtime guard to reject HTML entry points when transform_only (--no-bundle) is enabled, ensures output is flushed on exit, and adds regression tests covering HTML entrypoint error cases and a successful bundling case.

Changes

Cohort / File(s) Summary of changes
Build command validation
src/cli/build_command.zig
When ctx.bundler_options.transform_only is true, iterate this_transpiler.options.entry_points and error+exit(1) if any entry ends with the literal suffix .html (case-sensitive), preventing transform-only workflows from accepting HTML entrypoints.
Process exit handling
src/Global.zig
Call Output.flush() in exit(code) before performing the platform-specific exit to ensure buffered output is written prior to termination.
Regression tests: HTML entrypoints
test/regression/issue/23569.test.ts
Add tests verifying bun build --no-bundle errors for HTML entrypoints (including .htm and .HTML), with/without --outdir, and a successful build when bundling is enabled.

Possibly related PRs

Suggested reviewers

  • nektro
  • dylan-conway

Pre-merge checks

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The modification in Global.zig that adds a flush in the global exit function is unrelated to preventing HTML entrypoint crashes and falls outside the scope of issue #23569. Remove or isolate the Global.zig exit flush change into a separate PR or restrict this PR to only the build_command.zig validation to keep it focused on HTML entrypoint handling.
Description Check ⚠️ Warning The PR description uses custom headings (Summary, Changes, Test Plan) rather than the required template sections “What does this PR do?” and “How did you verify your code works?”, so it does not conform to the repository’s specified description template. Update the PR description to include the required template headings “What does this PR do?” and “How did you verify your code works?”, moving the summary and test details under those sections accordingly.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly describes the primary change—adding an error when using bun build --no-bundle with an HTML entrypoint—and accurately reflects the content of the pull request.
Linked Issues Check ✅ Passed The changes implement the required validation to detect HTML entrypoints when --no-bundle is used and emit the specified error message, satisfying the primary coding objective of linked issue #23569.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

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 6edae88 and c6b4475.

📒 Files selected for processing (1)
  • src/cli/build_command.zig (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.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

Files:

  • src/cli/build_command.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

src/**/*.zig: Use the # prefix to declare private fields in Zig structs (e.g., struct { #foo: u32 })
Prefer decl literals when initializing values in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 })
Place @import directives at the bottom of Zig files
Use @import("bun") instead of @import("root").bun

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

Files:

  • src/cli/build_command.zig
🔇 Additional comments (1)
src/cli/build_command.zig (1)

140-149: LGTM! HTML entrypoint validation is correct.

The validation logic correctly prevents the crash reported in #23569 by detecting HTML entrypoints when --no-bundle is used. The implementation is sound:

  • Case-sensitive .html check aligns with Bun's module resolution design
  • Error message is clear and actionable
  • Pattern matches other error paths (exit + return)
  • Placement is logical within the validation sequence

The past review concern about adding an explicit return; statement has been addressed.

Based on learnings


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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

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 db7bcd7 and 1507d85.

📒 Files selected for processing (2)
  • src/cli/build_command.zig (1 hunks)
  • test/regression/issue/23569.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.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

Files:

  • src/cli/build_command.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

src/**/*.zig: Use the # prefix to declare private fields in Zig structs (e.g., struct { #foo: u32 })
Prefer decl literals when initializing values in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 })
Place @import directives at the bottom of Zig files
Use @import("bun") instead of @import("root").bun

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

Files:

  • src/cli/build_command.zig
test/**

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

Place all tests under the test/ directory

Files:

  • test/regression/issue/23569.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/regression/issue/23569.test.ts
test/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

test/**/*.test.{ts,tsx}: Test files must live under test/ and end with .test.ts or .test.tsx
In tests, always use port: 0; do not hardcode ports or roll your own random port
Prefer normalizeBunSnapshot for snapshotting test output instead of asserting raw strings
Do not write tests that assert absence of crashes (e.g., 'no panic' or 'no uncaught exception')
Use Bun’s Jest-compatible runner (import { test, expect } from "bun:test") for tests
Avoid shell commands like find or grep in tests; use Bun’s Glob and built-in tools instead
Prefer running tests via bun bd test and use provided harness utilities (bunEnv, bunExe, tempDir)
Use Bun.spawn with proper stdio handling and await proc.exited in process-spawning tests

Files:

  • test/regression/issue/23569.test.ts
test/regression/issue/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place regression tests under test/regression/issue/ (one per bug fix)

Files:

  • test/regression/issue/23569.test.ts
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}

📄 CodeRabbit inference engine (test/CLAUDE.md)

test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}: Use bun:test for files ending with *.test.{ts,js,jsx,tsx,mjs,cjs}
Prefer concurrent tests (test.concurrent/describe.concurrent) over sequential when feasible
Organize tests with describe blocks to group related tests
Use utilities like describe.each, toMatchSnapshot, and lifecycle hooks (beforeAll, beforeEach, afterEach) and track resources for cleanup

Files:

  • test/regression/issue/23569.test.ts
test/**/*.{ts,tsx,js,jsx,mjs,cjs}

📄 CodeRabbit inference engine (test/CLAUDE.md)

For large/repetitive strings, use Buffer.alloc(count, fill).toString() instead of "A".repeat(count)

Files:

  • test/regression/issue/23569.test.ts
test/regression/issue/*.test.ts

📄 CodeRabbit inference engine (test/CLAUDE.md)

Place regression tests for specific issues in /test/regression/issue/${issueNumber}.test.ts

Files:

  • test/regression/issue/23569.test.ts
test/regression/**

📄 CodeRabbit inference engine (test/CLAUDE.md)

Do not place tests without an issue number in the regression directory

Files:

  • test/regression/issue/23569.test.ts
🧠 Learnings (11)
📚 Learning: 2025-10-12T02:22:34.349Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-10-12T02:22:34.349Z
Learning: Applies to test/{test/**/*.test.{ts,js,jsx,tsx,mjs,cjs},test/js/node/test/{parallel,sequential}/*.js} : When spawning Bun in tests, use `bunExe()` and `bunEnv` from `harness`

Applied to files:

  • test/regression/issue/23569.test.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/html.test.ts : html.test.ts should contain tests relating to HTML files themselves

Applied to files:

  • test/regression/issue/23569.test.ts
📚 Learning: 2025-10-04T09:51:30.294Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-04T09:51:30.294Z
Learning: Applies to test/**/*.test.{ts,tsx} : Prefer running tests via bun bd test <file> and use provided harness utilities (bunEnv, bunExe, tempDir)

Applied to files:

  • test/regression/issue/23569.test.ts
📚 Learning: 2025-10-12T02:22:34.349Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-10-12T02:22:34.349Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Use `bun:test` for files ending with `*.test.{ts,js,jsx,tsx,mjs,cjs}`

Applied to files:

  • test/regression/issue/23569.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} : Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test

Applied to files:

  • test/regression/issue/23569.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/bun/**/*.{js,ts} : Place Bun API tests under test/js/bun/, separated by category (e.g., test/js/bun/glob/)

Applied to files:

  • test/regression/issue/23569.test.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/bundle.test.ts : bundle.test.ts should contain DevServer-specific bundling tests

Applied to files:

  • test/regression/issue/23569.test.ts
📚 Learning: 2025-10-04T09:51:30.294Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-04T09:51:30.294Z
Learning: Applies to test/**/*.test.{ts,tsx} : Use Bun’s Jest-compatible runner (import { test, expect } from "bun:test") for tests

Applied to files:

  • test/regression/issue/23569.test.ts
📚 Learning: 2025-10-04T09:51:30.294Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-04T09:51:30.294Z
Learning: Applies to test/**/*.test.{ts,tsx} : Use Bun.spawn with proper stdio handling and await proc.exited in process-spawning tests

Applied to files:

  • test/regression/issue/23569.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/regression/issue/23569.test.ts
📚 Learning: 2025-10-12T02:22:34.349Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-10-12T02:22:34.349Z
Learning: Applies to test/{test/**/*.test.{ts,js,jsx,tsx,mjs,cjs},test/js/node/test/{parallel,sequential}/*.js,test/**/*-fixture.ts} : Use `using`/`await using` for resource cleanup with Bun APIs (e.g., `Bun.spawn`, `Bun.listen`, `Bun.serve`)

Applied to files:

  • test/regression/issue/23569.test.ts
🧬 Code graph analysis (1)
test/regression/issue/23569.test.ts (1)
test/harness.ts (2)
  • tempDir (277-284)
  • bunExe (102-105)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format
🔇 Additional comments (2)
src/cli/build_command.zig (1)

140-150: LGTM! Clear error prevents crash.

The validation logic correctly intercepts HTML entrypoints in transform-only mode before they can cause a crash downstream. The error message is clear and actionable, and the placement in the execution flow is appropriate—after compile-mode checks but before bundling begins.

test/regression/issue/23569.test.ts (1)

1-86: LGTM! Comprehensive regression tests.

The three tests provide excellent coverage of the fix:

  1. Core error case with --no-bundle
  2. Error case with --no-bundle and --outdir to ensure the validation happens regardless of output configuration
  3. Success case without --no-bundle to confirm normal HTML builds still work

Resource management is handled correctly with using and await using, and test isolation is ensured through separate temp directories.

Based on learnings.

@robobun robobun force-pushed the claude/fix-23569-html-no-bundle branch from 28f75a6 to 1507d85 Compare October 13, 2025 06:54
@robobun
Copy link
Collaborator Author

robobun commented Oct 13, 2025

@coderabbitai The .htm extension is not relevant here. Bun's HTML bundler only supports .html files, and adding .htm support would be a separate feature request. The case-insensitive check is also unnecessary - file extensions in module resolution are case-sensitive by design, and there are no existing precedents in the codebase for case-insensitive HTML file detection.

Copy link
Contributor

coderabbitai bot commented Oct 13, 2025

Tip

For best results, initiate chat on the files or code changes.

@robobun: Thank you for the clarification! I understand now that Bun's HTML bundler is specifically designed to support only .html files, and that adding .htm support would be a separate feature. I've noted the design decision regarding case-sensitive file extensions in module resolution.


✏️ Learnings added
Learnt from: robobun
URL: https://github.com/oven-sh/bun/pull/23572

Timestamp: 2025-10-13T06:54:58.013Z
Learning: Bun's HTML bundler only supports `.html` file extensions, not `.htm`. Adding `.htm` support would require a separate feature request.

Learnt from: robobun
URL: https://github.com/oven-sh/bun/pull/23572

Timestamp: 2025-10-13T06:54:58.013Z
Learning: File extensions in Bun's module resolution are case-sensitive by design, and there are no existing precedents in the codebase for case-insensitive HTML file detection.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

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 1507d85 and 4a19f4a.

📒 Files selected for processing (1)
  • src/cli/build_command.zig (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.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

Files:

  • src/cli/build_command.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

src/**/*.zig: Use the # prefix to declare private fields in Zig structs (e.g., struct { #foo: u32 })
Prefer decl literals when initializing values in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 })
Place @import directives at the bottom of Zig files
Use @import("bun") instead of @import("root").bun

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

Files:

  • src/cli/build_command.zig
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format

Claude Bot added 2 commits October 13, 2025 13:15
Global.exit() was supposed to flush via callbacks but on Linux it wasn't
working reliably. Now explicitly flushes Output before calling exit/quick_exit
to ensure all error messages are visible.

This fixes the need for manual Output.flush() calls before Global.exit()
throughout the codebase.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bun build crashes when using --no-bundle

1 participant