Skip to content

bundler: fix re-exports of the "bun" builtin with --target=bun - #37829

Open
robobun wants to merge 7 commits into
mainfrom
farm/9387196a/bundle-reexport-bun-builtin
Open

bundler: fix re-exports of the "bun" builtin with --target=bun#37829
robobun wants to merge 7 commits into
mainfrom
farm/9387196a/bundle-reexport-bun-builtin

Conversation

@robobun

@robobun robobun commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • With --target=bun, a non-entry file containing export * from "bun" re-exports nothing: Glob imported through it is undefined. In --format=cjs output the same input also copies the whole Bun object onto the entry's module.exports, so running the bundle prints Started development server: http://localhost:3000 and never exits. No build diagnostic in either case; reproduces on 1.4.0 and main.
  • export { default } from "bun", a default import of "bun" in cjs output (what --bytecode users hit), and import { default as x } from "bun" in both the bundler and the runtime transpiler bind undefined instead of Bun: each was lowered to a read of Bun.default, which does not exist.
  • Cause of the empty re-export: an import of "bun" prints as a plain var, not a hoisted import, and the __reExport() call landed before it.
  • Cause of the leak: the copy onto module.exports was keyed on the chunk being an entry point, not the file, so any non-entry export * from <external> in a cjs bundle leaked onto the entry's exports, plain externals included.

Fixes #20670

Fix

  • A non-entry export * from "bun" now passes globalThis.Bun to __reExport() directly and emits no import, so there is nothing left to hoist. The entry-point form is printed verbatim as before.
  • Only a file that is itself an entry point mirrors onto module.exports, the same condition that emits that file's module.exports = ... line. One existing importstar expectation encoded the leak and is updated.
  • Import items aliased default bind to the module object itself, and a "bun" import the linker marked for __toESM wrapping prints as __toESM(globalThis.Bun), which does have .default; require("bun") and named imports still print bare globalThis.Bun. The runtime transpiler cache version is bumped so files cached with the old lowering are not reused. (await import("bun")).default in bundles is still undefined; Stop rewriting a literal import("bun") to Promise.resolve(globalThis.Bun) #37730 removes that rewrite, and bundler: emit namespace binding for runtime export * from <external> #36714 (the generic ESM export * external bug) is untouched.
  • Verification: new bundler tests for each case above fail on 1.4.0 with the outputs from the repro and pass here; one runtime transpiler test covers default as x; the repro was also run by hand under --compile --bytecode in both formats.

Background

  • With --target=bun, "bun" is a builtin: neither the bundler nor the runtime transpiler loads a module for it, they print globalThis.Bun in its place, so import { Glob } from "bun" becomes var { Glob } = globalThis.Bun;. A var runs where it sits; a real import is hoisted.
  • The "bun" module's default export is the Bun object itself; there is no Bun.default property.
  • __reExport(exports, mod, module.exports?) is the runtime helper behind export * from an external: it copies mod's properties onto exports, and onto module.exports too when the third argument is passed. The third argument is only meant for the entry point of a cjs bundle.
  • __toESM(mod) wraps a CommonJS-shaped value as an ESM namespace, adding a default that points at the value. The linker flags an import record when references to it must go through this wrapper, which is what a default import in cjs output does.
  • The runtime transpiler cache stores transpiled files on disk under a version number; when a construct lowers differently, the number must change or older output is served for cached files.
Original description

Repro

printf 'export * from "bun";\n' > re.mjs
printf 'import { Glob, version } from "./re.mjs";\nconsole.log(typeof Glob, version === Bun.version);\n' > entry.mjs
printf 'export { default } from "bun";\n' > red.mjs
printf 'import B from "./red.mjs"; console.log(B === Bun);\n' > entryd.mjs

bun entry.mjs                                                        # function true
bun build --target=bun entry.mjs --outfile=o.js && bun o.js          # undefined false
bun entryd.mjs                                                       # true
bun build --target=bun entryd.mjs --outfile=od.js && bun od.js       # false
bun build --target=bun --format=cjs entryd.mjs --outfile=od.cjs && bun od.cjs   # false
bun build --target=bun --format=cjs entry.mjs --outfile=o.cjs && bun o.cjs
# function true
# Started development server: http://localhost:3000   (never exits)

Reproduces on 1.4.0 and on main. No build diagnostic in any of these cases.

Cause

Four separate problems, two of them not specific to "bun":

  1. export * from "bun" in a non-entry file, ESM output (convertStmtsForChunk): the export star is turned into import * as bun from "bun" plus a __reExport(exports_re, bun) prefix. For a real external that works because imports are hoisted, but the printer lowers an import of "bun" to a plain var bun = globalThis.Bun, which ends up after the call:
    __reExport(exports_re, bun);
    var bun =globalThis.Bun;
  2. export { default } from "bun" (bundler, ESM output) and import { default as x } from "bun" (bundler and runtime transpiler) reach print_internal_bun_import as a clause item aliased default, which it printed as var {default: default2 } = globalThis.Bun;. The default export of the "bun" module is the Bun object itself, and Bun.default does not exist.
  3. import x from "bun" / export { default } from "bun" with --format=cjs (which --bytecode defaults to; this is Bun import fails (undefined) when using the bytecode flag #20670): the linker converts the import to var import_bun = require("bun"), references become import_bun.default, and it sets WRAP_WITH_TO_ESM on the record (the __toESM helper is already present in such bundles). The globalThis.Bun shortcut in print_require_or_import_expr ignored the flag, so the output was var import_bun = globalThis.Bun; and .default was undefined.
  4. The module.exports third argument of __reExport(exports, mod, module.exports) was added when the chunk is an entry point rather than when the file being converted is the entry point (convertStmtsForChunk line 72), so every non-entry export * from <external> in a CJS bundle was copied onto the entry's module.exports. With "bun" that copies fetch/serve there, and running the bundle then starts a server:
    __reExport(exports_re, globalThis.Bun, module.exports);
    Plain externals hit the same thing: re.mjs: export * from "ext" made require("./out.cjs") return ext's exports even though the entry exports nothing.

(The generic ESM variant of 1, where a non-entry export * from <external> loses its * as ns binding entirely, is #36714 and is not touched here; the two changes are independent.)

Fix

  • convertStmtsForChunk: for a runtime export star whose record is the "bun" builtin, pass the module expression (an E::RequireString, which the printer emits as globalThis.Bun) to __reExport directly and drop the statement, which is what the CJS output path already produced. Output is now __reExport(exports_re, globalThis.Bun);. The entry point case, where export * from "bun" is printed verbatim, is unchanged.
  • convertStmtsForChunk: key module_exports_for_export off the file's own entry point kind (the same condition that emits the file's module.exports = __toCommonJS(...)), and drop the now unused chunk parameter.
  • print_internal_bun_import: clause items aliased default are emitted as var x = <module>; and left out of the destructuring pattern; the three copies of the module expression printing are folded into a helper.
  • print_require_or_import_expr: honor WRAP_WITH_TO_ESM for the "bun" record, giving var import_bun = __toESM(globalThis.Bun);. This also applies to import * as ns from "bun" in CJS output, where the linker sets the same flag: ns becomes the __toESM view (every Bun.* property plus default), which is the shape the module loader itself gives the "bun" namespace. require("bun") and named imports are unaffected and still print bare globalThis.Bun.
  • RuntimeTranspilerCache: bump the cache version. The runtime transpiler lowering of import { default as x } from "bun" changed, and a release build otherwise restores the old output for files cached by a previous version (a .pile written by the current canary still contains var {default: x, ...} = globalThis.Bun).

Intentionally not touched: the ImportKind::Dynamic arm next to the one changed above still prints a literal import("bun") as Promise.resolve(globalThis.Bun), so (await import("bun")).default stays undefined in bundles. #37730 removes that rewrite altogether (the loader then provides the namespace), which is the right fix for it, and import-meta.test.js currently asserts the old behavior.

importstar/ReExportStarEntryPointAndInnerFileExternal expected the leaked key from problem 4 ({"inner":{"b":456},"a":123,"b":456}); its non-external twin already expected b to be absent. Updated to {"inner":{"b":456},"a":123}.

Verification

New tests, all of which fail on 1.4.0 with the outputs shown in the repro and pass with this change:

  • test/bundler/bundler_bun.test.ts: export * from "bun" in a non-entry file for ESM and CJS output (the CJS one also checks the entry's module.exports stays empty; before the fix it had 115 keys), export { default } from "bun" plus import { default as x } for ESM output, and export { default } / default import / import * for CJS output.
  • test/bundler/bundler_cjs.test.ts: non-entry export * from "ext" with a plain external must not touch the entry's module.exports.
  • test/bundler/bundler_minify.test.ts: import { default as bun, embeddedFiles } from "bun" under full minification (the default item adds a statement, so this covers semicolon insertion like the neighbouring tests).
  • test/js/bun/resolve/import-meta.test.js: runtime transpiler import { default as x, spawnSync } from "bun".

Also run locally on this build: bundler_bun, bundler_cjs, bundler_minify, esbuild/importstar, esbuild/default, esbuild/splitting, bundler_edgecase, the bundler_compile tests that import from "bun", resolve/builtin-esm-lazy-exports, and resolve/import-meta; --compile --bytecode in both formats of the repro above prints the right values.

Fixes #20670

… from non-entry export stars

With --target=bun, a module doing `export * from "bun"` or
`export { default } from "bun"` produced undefined bindings, and with
--format=cjs the re-export also copied the whole Bun object onto the
entry point's module.exports.

- convertStmtsForChunk: a runtime `export * from "bun"` in ESM output
  was turned into `import * as ns from "bun"` plus `__reExport(exports,
  ns)`. The printer lowers that import to `var ns = globalThis.Bun`,
  which is not hoisted, so __reExport ran before ns was assigned. The
  "bun" record now passes the module expression (printed as
  globalThis.Bun) to __reExport directly, as the CJS path already did.

- convertStmtsForChunk: the module.exports argument of __reExport was
  added for every file in an entry chunk instead of only the entry
  point file, so any non-entry `export * from <external>` leaked onto
  the entry's module.exports. For "bun" that copied fetch/serve there,
  which made `bun out.cjs` start a server. importstar/
  ReExportStarEntryPointAndInnerFileExternal had the leaked key baked
  into its expected output; the non-external variant of the same test
  already expected the key to be absent.

- js_printer: `import { default as x } from "bun"` (which is also what
  the bundler turns `export { default } from "bun"` into) was printed as
  `var { default: x } = globalThis.Bun`. The default export of "bun" is
  the Bun object itself, so such items now bind to the module value.

- js_printer: in CJS output the linker marks default/namespace imports of
  "bun" with WRAP_WITH_TO_ESM and emits the __toESM helper, but the
  globalThis.Bun shortcut ignored the flag, so `import x from "bun"`
  became `import_bun.default` on the bare Bun object. The flag is now
  honored, giving the import the same namespace shape the module loader
  produces for "bun".
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 28 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7cfb54db-f535-4b82-a3a4-74dd0c0ccc3f

📥 Commits

Reviewing files that changed from the base of the PR and between 9a543cc and 336d975.

📒 Files selected for processing (9)
  • src/bundler/linker_context/convertStmtsForChunk.rs
  • src/bundler/linker_context/generateCodeForFileInChunkJS.rs
  • src/js_printer/lib.rs
  • src/jsc/RuntimeTranspilerCache.rs
  • test/bundler/bundler_bun.test.ts
  • test/bundler/bundler_cjs.test.ts
  • test/bundler/bundler_minify.test.ts
  • test/bundler/esbuild/importstar.test.ts
  • test/js/bun/resolve/import-meta.test.js

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

@robobun

robobun commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator Author

Status: reproduced on 1.4.0 and main with the commands in the description (ESM: undefined false / false; CJS: module.exports picks up 115 Bun properties and bun o.cjs starts a server). All four fixes plus the new tests are in this PR; the new tests fail on 1.4.0 and pass with this branch. Also fixes #20670 (import Bun from "bun" with --bytecode), verified with the issue's script.

CI (build 93270, finished): 178 jobs passed. The one failed job (darwin 14 aarch64) is two bun install tests failing on GET https://github.com/cujojs/when/tarball/1.0.2 - 503, unrelated to this change and reported separately. The two darwin 26 aarch64 test jobs expired in the queue without running (the single macOS 26 runner was busy with other builds), so the build shows as failed; nothing in it points at this diff. Ready for review.

@github-actions

Copy link
Copy Markdown
Contributor

Found 1 issue this PR may fix:

  1. Bun import fails (undefined) when using the bytecode flag #20670 - --bytecode defaults the output format to CJS, so import Bun from 'bun' printed var import_bun = globalThis.Bun and import_bun.default was undefined; this PR makes print_require_or_import_expr honor WRAP_WITH_TO_ESM on the "bun" record and emit __toESM(globalThis.Bun).

If this is helpful, copy the block below into the PR description to auto-close these issues on merge.

Fixes #20670

🤖 Generated with Claude Code

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

I reviewed this PR and didn't find any bugs. Because it changes bundler output-shape logic in the linker (the per-file vs per-chunk module_exports_for_export gating affects all CJS bundles with a non-entry export * from <external>, and updates an existing esbuild-ported test's expected output), a maintainer look would still be worthwhile.

What was reviewed:

  • is_entry_point() is exactly != Kind::None, so should_strip_exports is behavior-preserving; only module_exports_for_export changes.
  • The updated ReExportStarEntryPointAndInnerFileExternal expectation matches its non-external twin and aligns with where force_include_exports_for_entry_point is set (per-file, LinkerContext.rs:526).
  • print_internal_bun_import_value reorders but preserves the original three-branch precedence; the manual i counter correctly skips leading commas when default items are filtered.
  • WRAP_WITH_TO_ESM handling in print_require_or_import_expr doesn't affect require("bun") or named-only imports (flag isn't set on those records).
Extended reasoning...

Overview

The PR fixes four bundler bugs around re-exporting the "bun" builtin, two of which are not "bun"-specific. Changes touch convertStmtsForChunk.rs (linker chunk conversion), generateCodeForFileInChunkJS.rs (mechanical parameter drop), and js_printer/lib.rs (print_internal_bun_import refactor + WRAP_WITH_TO_ESM handling). Five test files add coverage; one existing esbuild-ported test's expected stdout is updated.

Security risks

None. This is bundler output-shaping logic; no untrusted-input parsing, no auth/crypto, no filesystem or network paths.

Level of scrutiny

High. The bundler linker is a critical hot path, and one of the four fixes (keying module_exports_for_export off the file's entry-point kind rather than the chunk's) changes CJS output for every non-entry export * from <external>, not just "bun". That change updates an existing test expectation (importstar/ReExportStarEntryPointAndInnerFileExternal). The reasoning is sound — force_include_exports_for_entry_point is set per-file at LinkerContext.rs:526, and the non-external sibling test already expected the corrected shape — but a maintainer should confirm the behavior change is intended.

Other factors

  • I verified EntryPoint::Kind::is_entry_point() is defined as self != Self::None (LinkerGraph.rs:53-55), so the should_strip_exports refactor is behavior-preserving.
  • The print_internal_bun_import refactor extracts three identical match blocks into print_internal_bun_module and reorders (but does not change) the default-name/star-name/module-expression precedence into print_internal_bun_import_value.
  • Test coverage is thorough: ESM and CJS output, minification (semicolon-insertion sibling), runtime transpiler, and the generic-external case for the module.exports leak.
  • No prior reviews on this PR.

Comment thread src/bundler/linker_context/convertStmtsForChunk.rs Outdated
Comment thread src/bundler/linker_context/convertStmtsForChunk.rs Outdated
Comment thread src/js_printer/lib.rs Outdated
Comment thread src/js_printer/lib.rs Outdated
Comment thread src/js_printer/lib.rs Outdated
Comment thread src/bundler/linker_context/convertStmtsForChunk.rs Outdated
Comment thread src/js_printer/lib.rs Outdated
Comment thread src/js_printer/lib.rs Outdated
@robobun

robobun commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 1:05 PM PT - Aug 12th, 2026

@robobun, your commit 336d975 has 2 failures in Build #93270 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 37829

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

bun-37829 --bun

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

I reviewed this PR and didn't find any bugs. Because it changes bundler linker/printer codegen in a way that affects all non-entry export * from <external> in CJS output (not just "bun") and updates an existing test's expected output, a human look would still be worthwhile.

What was reviewed:

  • should_strip_exports semantics unchanged — is_entry_point() is != Kind::None, same as before.
  • print_internal_bun_import_value ordering (default → star → module) matches the old destructuring-RHS logic for all combinations.
  • WRAP_WITH_TO_ESM path uses self.options.to_esm_ref, which is populated from the runtime scope in postProcessJSChunk.
  • The updated ReExportStarEntryPointAndInnerFileExternal expectation now matches its non-external twin.
Extended reasoning...

Overview

Four fixes across the bundler linker and JS printer for how the "bun" builtin is emitted when re-exported or default-imported, plus one general fix: convertStmtsForChunk now keys the module.exports third argument of __reExport(...) on the file's entry-point kind rather than the chunk's, so a non-entry export * from <external> in a CJS bundle no longer leaks onto the entry's module.exports. The now-unused chunk parameter is dropped from both call sites in generateCodeForFileInChunkJS. print_internal_bun_import is refactored to emit default-aliased clause items as var x = <module> and skip them in the destructuring pattern; print_require_or_import_expr honors WRAP_WITH_TO_ESM on the "bun" record. Five test files add coverage; one existing esbuild-ported test's expected stdout is updated.

Security risks

None identified. No user-input parsing, no path/security checks touched — this is bundler AST-to-code emission.

Level of scrutiny

Medium-high. The bundler linker (convertStmtsForChunk) and printer are on the hot path for every bun build, and the module_exports_for_export change is a general semantic change (all externals in CJS output), not scoped to the "bun" builtin. The updated importstar/ReExportStarEntryPointAndInnerFileExternal expectation is well-justified (its non-external twin already expected the corrected shape, and the PR description explains the old expectation encoded the bug), but changing an existing test's expected output warrants human confirmation that the new behavior is what maintainers want.

Other factors

  • The comment-cop bot threads on lib.rs/convertStmtsForChunk.rs appear to have been addressed in commits 41206bc and 5c8d427; the remaining comments in the diff are all single-line.
  • Test coverage is thorough: ESM and CJS output, entry and non-entry files, default alias with and without other named items, minified output for semicolon insertion, and the runtime transpiler path.
  • I verified is_entry_point() on EntryPoint::Kind is self != Self::None (LinkerGraph.rs:53-55), so should_strip_exports is semantically unchanged despite the refactor.

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

Beyond the inline nit, I checked that Kind::is_entry_point() is exactly != Kind::None (LinkerGraph.rs:53-54), so should_strip_exports is unchanged — only module_exports_for_export moved from chunk-level to file-level. The updated ReExportStarEntryPointAndInnerFileExternal expectation now matches its non-external twin at importstar.test.ts:1409-1433, which already omitted b. That behavior change (problem 4) affects all CJS bundles with non-entry export * from <external>, so a maintainer sign-off is still worth having.

Extended reasoning...

The four fixes and their tests read correctly; the one non-"bun"-specific semantic change (keying module_exports_for_export off the file rather than the chunk) is well-argued and consistent with the sibling non-external test, but it changes general bundler output and edits an esbuild-ported expectation, so I'm deferring rather than approving. The dynamic-import arm noted inline is pre-existing and correctly scoped out.

Comment thread src/js_printer/lib.rs
The printer now lowers `import { default as x } from "bun"` differently,
and cached output written by an older binary would otherwise be reused
as-is after an upgrade (a release build restoring such an entry still
binds x to undefined).
Comment thread src/jsc/RuntimeTranspilerCache.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.

I re-reviewed after the description update and comment trims — no bugs found, and the earlier note about the ImportKind::Dynamic arm is now covered in the description. Given this changes general CJS linker semantics (the module_exports_for_export gating moves from chunk-level to file-level, which updates an esbuild-ported test's expected output) alongside three "bun"-specific printer fixes, a human sign-off is still worthwhile.

What was reviewed:

  • should_strip_exports is unchanged — Kind::is_entry_point() is exactly != Kind::None (LinkerGraph.rs:53), so only module_exports_for_export gating moved.
  • print_internal_bun_import_value preserves the old RHS-selection order for the destructuring pattern; the new per-default-item var emission calls print_semicolon_if_needed() first and reuses the default/namespace symbol when one exists.
  • The is_bun_builtin branch drops the statement via continue 'stmt_loop so no dead S::ExportStar reaches the wrapper-prefix append; the non-bun path is byte-identical to before.
  • Cache-version bump matches the runtime-transpiler output change for import { default as x } from "bun".
Extended reasoning...

Overview

Four related fixes to how the bundler and runtime transpiler handle the "bun" builtin (and one general CJS linker fix that surfaced through it):

  1. convertStmtsForChunk.rs: for a runtime export * from "bun" in a non-entry file with ESM output, emit __reExport(exports_re, globalThis.Bun) directly via an E::RequireString argument instead of an import * as ns statement (which the printer lowers to an unhoisted var, ordering it after the __reExport call).
  2. convertStmtsForChunk.rs: gate the module.exports third argument to __reExport on the file's entry-point kind rather than the chunk's, so a non-entry file's export * from <external> in a CJS bundle no longer copies the external's keys onto the entry's module.exports. This is the general (non-"bun") fix and is what changes importstar/ReExportStarEntryPointAndInnerFileExternal's expected output. The now-unused chunk parameter is dropped from both call sites in generateCodeForFileInChunkJS.rs.
  3. js_printer/lib.rs print_internal_bun_import: clause items aliased default are emitted as var x = <module> and skipped in the destructuring pattern; the three copies of the module-expression printing are folded into print_internal_bun_module / print_internal_bun_import_value.
  4. js_printer/lib.rs print_require_or_import_expr: honor WRAP_WITH_TO_ESM on the "bun" record so CJS-format output emits __toESM(globalThis.Bun) and .default resolves (fixes #20670 for --bytecode).
  5. RuntimeTranspilerCache.rs: version bump 25→26 because the runtime transpiler's output for import { default as x } from "bun" changed.

New tests in bundler_bun.test.ts, bundler_cjs.test.ts, bundler_minify.test.ts, and import-meta.test.js cover each fix; importstar.test.ts updates one expected output.

Security risks

None identified. This is bundler/printer output-shape work; no untrusted input parsing, no auth/crypto/permissions, no filesystem or network paths touched beyond the pre-existing cache-version constant.

Level of scrutiny

Medium-high. The "bun"-specific printer changes are narrow and well-tested, but fix (2) is a general CJS linker semantics change that affects any bundle with a non-entry export * from <external>, and it revises an esbuild-ported test's expected output. I verified Kind::is_entry_point() is exactly self != Kind::None, so should_strip_exports is unchanged and only the module_exports_for_export gate moved. The non-external twin test already expected the new shape, and the new cjs/__reExport_external_in_non_entry_file test pins it directly, so the change is well-argued — but a maintainer should confirm the intent for the esbuild-ported case.

Other factors

  • My earlier inline note about the untouched ImportKind::Dynamic arm was addressed: the description now explicitly scopes it out to #37730, and import-meta.test.js:228 currently pins the old behavior.
  • All comment-cop flags are resolved (comments trimmed to one line each; the cache-version note follows the existing changelog block format).
  • Test coverage is thorough across ESM/CJS × entry/non-entry × default/named/star, plus the minify variant for semicolon insertion and the runtime-transpiler path.
  • No memory-safety concerns: the new code allocates via the existing Expr::init/Stmt::alloc arena helpers and the is_bun_builtin branch cleanly continues the statement loop.

Deferring rather than approving because this is four fixes across the linker and printer, one of which changes general (non-"bun") CJS output and an existing test expectation.

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 import fails (undefined) when using the bytecode flag

2 participants