Skip to content

bundler(options): collapse bool pairs into OfflineMode/CompileMode enums - #36768

Merged
Jarred-Sumner merged 6 commits into
mainfrom
claude/farm/b0dc8bb6/bundler-options-enums
Aug 2, 2026
Merged

bundler(options): collapse bool pairs into OfflineMode/CompileMode enums#36768
Jarred-Sumner merged 6 commits into
mainfrom
claude/farm/b0dc8bb6/bundler-options-enums

Conversation

@robobun

@robobun robobun commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Two related type-hardening refactors in the bundler option structs. No behavior change.

install_preference: OfflineMode

BundleOptions (both the bundler and resolver copies) carried prefer_offline_install: bool plus, on the bundler side, prefer_latest_install: bool. Every write site (run_command.rs, repl_command.rs, bake/production.rs) derived both from a single OfflineMode value:

b.options.prefer_offline_install = offline == OfflineMode::Offline;
b.options.prefer_latest_install  = offline == OfflineMode::Latest;

prefer_latest_install was write-only: nothing in the tree reads it. (true, true) would have been contradictory but the type allowed it.

Now both structs store install_preference: OfflineMode directly. The one reader (resolver auto-install disk-cache lookup) checks == OfflineMode::Offline. The three write sites each drop ~10 lines of enum-to-bool decoding plus the comments explaining why the resolver lacked prefer_latest_install.

compile_mode: CompileMode

BundleOptions and LinkerOptions each carried compile: bool and compile_to_standalone_html: bool. The two are mutually exclusive by construction: both build_command.rs and js_bundle_completion_task.rs explicitly clear compile when they set compile_to_standalone_html:

this_transpiler.options.compile_to_standalone_html = true;
this_transpiler.options.compile = false;

Now both structs store:

pub enum CompileMode { None, Executable, StandaloneHtml }

with is_executable() / is_standalone_html() helpers. The (true, true) state is no longer representable. The resolver's BundleOptions.compile: bool projection is kept as-is (resolver never cares about standalone HTML) and is fed compile_mode.is_executable().

The CLI-level ctx.bundler_options.compile: bool is unchanged: that is the raw --compile flag before HTML/browser detection decides which mode it becomes.

Why

Making illegal states unrepresentable removes a class of drift bugs where one bool gets updated and the other doesn't, and it deletes the cross-file comments that existed only to explain which struct had which subset of the bools. prefer_latest_install was already dead; this removes it rather than leaving it to bit-rot.

Verification

No behavior change; verified against existing coverage:

  • test/bundler/bundler_compile.test.ts (60 pass, 1 pre-existing fail on main: HelloWorldWithProcessVersionsBun)
  • test/bundler/standalone.test.ts (23 pass; covers CompileMode::StandaloneHtml via both CLI and Bun.build)
  • test/bundler/bundler_html.test.ts (22 pass)
  • test/bundler/bun-build-api.test.ts (49 pass)
  • test/bundler/cli.test.ts (16 pass)
  • test/bundler/bun-build-compile.test.ts (11 pass, 1 pre-existing timeout on main: compile with relative outfile paths)

Net -1 line across 18 files.

Two related type-hardening refactors with no behavior change:

install_preference: OfflineMode
  Replaces the prefer_offline_install + prefer_latest_install bool pair on
  the bundler and resolver BundleOptions. Every write site already decoded
  these from an OfflineMode and prefer_latest_install was write-only dead
  state. The one read site (resolver disk-cache lookup) now checks the
  enum directly.

compile_mode: CompileMode
  Replaces the compile + compile_to_standalone_html bool pair on the
  bundler BundleOptions and LinkerOptions. The two were mutually exclusive
  by construction (both build_command and Bun.build clear compile when
  setting compile_to_standalone_html) so a three-state enum makes the
  invariant unrepresentable-if-wrong. The resolver projection keeps its
  compile: bool and is fed compile_mode.is_executable().

Net -1 line across 18 files; three write sites each drop ~10 lines of
duplicated enum-to-bool decoding plus the comments explaining why the
resolver lacks prefer_latest_install.
@github-actions github-actions Bot added the claude label Aug 2, 2026
@robobun

robobun commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator Author

Type-hardening refactor with no behavior change. Verified against existing bundler test suites (compile, standalone HTML, Bun.build API, CLI). The two pre-existing test failures noted in the PR body reproduce identically on main at 2569ae3.

Self-review pass clean; both review comments (doc-comment length, CompileMode placement) addressed in 7151926 / 9d0ff35 / eb6548e.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The change replaces compile and install-preference booleans with CompileMode and OfflineMode. Runtime configuration, resolver behavior, linker options, asset paths, chunk generation, HTML handling, and bytecode output now use the typed modes.

Changes

Typed mode migration

Layer / File(s) Summary
Compile and installation mode contracts
src/bundler/options.rs, src/resolver/options.rs, src/options_types/offline_mode.rs, src/bundler/LinkerContext.rs
Adds CompileMode, consolidates installation preferences into OfflineMode, and updates defaults and worker propagation.
Runtime and resolver mode wiring
src/runtime/api/..., src/runtime/cli/..., src/runtime/bake/production.rs, src/bundler/transpiler.rs, src/resolver/resolver.rs
Runtime entry points assign typed modes. Transpiler and resolver paths consume the new fields.
Bundler compile-mode behavior
src/bundler/HTMLImportManifest.rs, src/bundler/ParseTask.rs, src/bundler/bundle_v2.rs, src/bundler/linker_context/...
Bundler paths use compile-mode predicates for executable output, standalone HTML handling, module information, paths, and generated assets.

Possibly related PRs

  • oven-sh/bun#36765: Refactors related boolean configuration into enums across shared bundler and runtime paths.

Suggested reviewers: jarred-sumner

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main refactor from boolean pairs to OfflineMode and CompileMode enums.
Description check ✅ Passed The description explains the refactor, rationale, behavior impact, and verification results, covering both template requirements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

Comment thread src/bundler/options.rs Outdated

@claude claude 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.

LGTM — mechanical type-hardening refactor; the one inline finding is a cosmetic placement nit.

Extended reasoning...

Overview

Two bool-pair → enum collapses in bundler option structs across 18 files: (prefer_offline_install, prefer_latest_install)install_preference: OfflineMode, and (compile, compile_to_standalone_html)compile_mode: CompileMode. Every read site is a 1:1 substitution (.compile.compile_mode.is_executable(), .compile_to_standalone_html.compile_mode.is_standalone_html(), prefer_offline_installinstall_preference == Offline); prefer_latest_install was write-only and is removed.

Semantic equivalence checks

I traced every changed read/write site:

  • Both write paths (build_command.rs, js_bundle_completion_task.rs) previously set compile = false whenever compile_to_standalone_html = true, so the two bools were already mutually exclusive; the new enum encodes exactly that.
  • Defaults line up: (false, false)CompileMode::None; prefer_offline_install = falseOfflineMode::Online (now #[default]).
  • The resolver-side BundleOptions.compile: bool is intentionally kept and fed compile_mode.is_executable() in transpiler.rs; the two remaining c.resolver().opts.compile reads in generateChunksInParallel.rs therefore behave identically.
  • Grepped the tree for stragglers: remaining compile_to_standalone_html identifiers are all local variables / the HTMLLoader struct field, populated from compile_mode.is_standalone_html().

Security risks

None. Pure internal type refactor of build-configuration state; no user input parsing, no I/O, no auth/crypto surface.

Level of scrutiny

Wide (18 files) but shallow — each hunk is a single-expression substitution against a Copy enum with two matches! helpers. The Rust type system guarantees no site was missed (removed fields would fail to compile). Existing bundler test suites (bundler_compile, standalone, bundler_html, bun-build-api, cli, bun-build-compile) exercise both Executable and StandaloneHtml modes per the PR body.

Other factors

The one bug-hunter finding is a code-organization nit (CompileMode inserted between SourceMapOption and its impl); zero behavioral impact. The comment-cop bot flag on the 3-line CompileMode doc comment looks like a false positive — it's an ordinary enum description, not a workaround justification. Neither warrants blocking.

Comment thread src/bundler/options.rs Outdated
@robobun

robobun commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator Author

Closing: this is a no-behavior-change type-hardening refactor, so there is no regression test that can distinguish before from after.

Both bool pairs were already mutually exclusive at every write site (the (true, true) state was never constructed) and prefer_latest_install was write-only dead state. Collapsing each pair into an enum makes the invariant unrepresentable but does not change what any input produces. The refactored paths are exercised end-to-end by test/bundler/bundler_compile.test.ts, test/bundler/standalone.test.ts, test/bundler/bundler_html.test.ts, test/bundler/bun-build-api.test.ts, and test/bundler/cli.test.ts, all of which pass against this branch.

Happy to reopen if a maintainer wants to land it as a cleanup; the branch is claude/farm/b0dc8bb6/bundler-options-enums.

@robobun robobun closed this Aug 2, 2026

@claude claude 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.

LGTM — mechanical type-hardening refactor; the earlier nit about CompileMode placement was addressed in 9d0ff35.

What was reviewed:

  • Traced every compile/compile_to_standalone_html read site → is_executable()/is_standalone_html() are 1:1 equivalent given both writers cleared compile when setting standalone HTML.
  • Resolver's sole prefer_offline_install reader now checks == OfflineMode::Offline, matching how every writer derived the bool; prefer_latest_install confirmed write-only.
  • New Default derives (OfflineMode::Online, CompileMode::None) match the prior false/false defaults; for_worker/LinkerOptions::default/resolver projection all updated consistently.
Extended reasoning...

Overview

This PR collapses two pairs of mutually-exclusive booleans into single enums across the bundler option structs: prefer_offline_install + prefer_latest_installinstall_preference: OfflineMode, and compile + compile_to_standalone_htmlcompile_mode: CompileMode. It touches 18 files but every change is a mechanical field rename or a boolenum.is_variant() substitution. Net -1 line.

Security risks

None. This is a pure type refactor of internal option-struct fields with no user-facing surface, no parsing, no I/O, and no security-relevant logic.

Level of scrutiny

Low-to-medium. While 18 files are touched, the Rust type system guarantees every read/write site was updated (a missed site would fail to compile). I traced each semantic change:

  • Every former options.compile read becomes compile_mode.is_executable(); every compile_to_standalone_html read becomes compile_mode.is_standalone_html(). The two writers (build_command.rs, js_bundle_completion_task.rs) previously enforced mutual exclusion by clearing compile when setting standalone HTML, so the tri-state enum is exactly equivalent.
  • The resolver's one reader of prefer_offline_install now checks == OfflineMode::Offline — identical to how all three writers derived the bool. prefer_latest_install was confirmed write-only (dead field removal).
  • Defaults preserved: #[default] Online / #[default] None match the prior false initializers in from_api, for_worker, LinkerOptions::default, and the resolver's BundleOptions::default.
  • The resolver-side compile: bool projection is fed compile_mode.is_executable(), which is false for StandaloneHtml — matching prior behavior where writers cleared compile in that branch.

Other factors

The author verified against six existing bundler test suites (compile, standalone HTML, Bun.build API, CLI, HTML) with only pre-existing failures that reproduce on main. My earlier nit about CompileMode splitting SourceMapOption from its impl was addressed in 9d0ff35, and the comment-cop feedback about the doc comment was addressed in 7151926. Both inline threads are resolved. This is a textbook "make illegal states unrepresentable" refactor with no behavior change.

@Jarred-Sumner Jarred-Sumner reopened this Aug 2, 2026

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/bundler/linker_context/postProcessJSChunk.rs`:
- Around line 99-101: The ESM bytecode module_info eligibility condition is
duplicated across four sites. Add a shared LinkerOptions::wants_module_info()
helper containing the generate_bytecode_cache, ESM output_format, and executable
compile_mode checks, use it in postProcessJSChunk.rs#L99-L101, and replace the
inline conditions in OutputFileListBuilder.rs#L130-L137 and
generateChunksInParallel.rs#L452-L455 and `#L1102-L1105` with the helper.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: cb47d18c-4d78-4f16-a56c-8e8abf031790

📥 Commits

Reviewing files that changed from the base of the PR and between 7354c5e and a034cad.

📒 Files selected for processing (18)
  • src/bundler/HTMLImportManifest.rs
  • src/bundler/LinkerContext.rs
  • src/bundler/ParseTask.rs
  • src/bundler/bundle_v2.rs
  • src/bundler/linker_context/OutputFileListBuilder.rs
  • src/bundler/linker_context/generateChunksInParallel.rs
  • src/bundler/linker_context/generateCompileResultForHtmlChunk.rs
  • src/bundler/linker_context/postProcessJSChunk.rs
  • src/bundler/options.rs
  • src/bundler/transpiler.rs
  • src/options_types/offline_mode.rs
  • src/resolver/options.rs
  • src/resolver/resolver.rs
  • src/runtime/api/js_bundle_completion_task.rs
  • src/runtime/bake/production.rs
  • src/runtime/cli/build_command.rs
  • src/runtime/cli/repl_command.rs
  • src/runtime/cli/run_command.rs

Comment thread src/bundler/linker_context/postProcessJSChunk.rs

@claude claude 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.

LGTM — mechanical bool-pair → enum refactor, prior nit addressed.

Checked that every former options.compile / compile_to_standalone_html read maps to compile_mode.is_executable() / .is_standalone_html() with identical truth tables; the resolver's opts.compile: bool projection and CLI-level ctx.bundler_options.compile are intentionally unchanged.
Verified prefer_latest_install was write-only (no readers in tree) and OfflineMode::default() == Online matches the old prefer_offline_install: false default.
Confirmed LinkerOptions now copies compile_mode once where it previously copied both bools separately, and sync_resolver_opts() runs after all compile_mode mutations in both build_command.rs and js_bundle_completion_task.rs.

Extended reasoning...

Overview

Two type-hardening refactors across 18 files in the bundler/resolver/CLI: (1) prefer_offline_install: bool + dead prefer_latest_install: boolinstall_preference: OfflineMode, and (2) compile: bool + compile_to_standalone_html: boolcompile_mode: CompileMode enum on BundleOptions and LinkerOptions. The resolver-side BundleOptions.compile: bool and CLI-level ctx.bundler_options.compile: bool are kept as-is by design.

Security risks

None. No parsing of untrusted input, no auth/crypto/permissions, no FFI or memory-safety surface. Pure internal option-struct reshaping.

Level of scrutiny

Medium — 18 files is broad, but every change is a mechanical field rename/accessor swap that the Rust compiler enforces (a missed read of a removed field would fail to build). I traced each read site: is_executable() ↔ old compile, is_standalone_html() ↔ old compile_to_standalone_html, == OfflineMode::Offline ↔ old prefer_offline_install. Defaults (CompileMode::None, OfflineMode::Online) match the old false defaults. The bundle_v2.rs linker-options copy correctly folds the two removed bool copies into one compile_mode copy. Grepped for stragglers: remaining compile_to_standalone_html / .compile hits are local variable names, the resolver bool, or the CLI flag — all intentional.

Other factors

This is the fifth PR in a batch applying the same "collapse dependent bool pairs into enums" pattern (#36762/64/65/66 already merged). My earlier placement nit (CompileMode wedged between SourceMapOption and its impl) was addressed in 9d0ff35 — the diff now shows CompileMode after SOURCE_MAP_OPTION_MAP. The comment-cop finding was addressed in 7151926. Existing bundler test suites (bundler_compile, standalone, bundler_html, bun-build-api, cli, bun-build-compile) exercise all three CompileMode variants via both CLI and Bun.build, and a no-behavior-change refactor cannot have a distinguishing regression test.

@Jarred-Sumner
Jarred-Sumner merged commit 4abba47 into main Aug 2, 2026
50 of 54 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the claude/farm/b0dc8bb6/bundler-options-enums branch August 2, 2026 11:01
@robobun

robobun commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up: while adding coverage for the install_preference path, I found that --prefer-offline runtime auto-install never actually resolved from the disk cache (the version-index entry names fail the semver parse, dist-tag latest bailed before the scan, and the cache folder resolution read the project's package.json). Fix and test in #36776.

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.

2 participants