Skip to content

bundler: document and pin the skip of an unused barrel re-export into a package with side effects - #46062

Open
robobun wants to merge 2 commits into
mainfrom
robobun/1ac42521/barrel-document-skip
Open

robobun wants to merge 2 commits into
mainfrom
robobun/1ac42521/barrel-document-skip

Conversation

@robobun

@robobun robobun commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Behaviour change: none

Question for a maintainer. Is the skip below the contract of automatic barrel mode, or a bug? This PR documents and pins what main does. The other rule is ready on robobun/1ac42521/barrel-defer-by-target-verdict (cost in Notes).

Problem

  • Since 1.3.10, export { poly } from "effect-pkg" in a pure barrel of a "sideEffects": false package does not load effect-pkg when nothing imports poly, although effect-pkg has side effects. Bun 1.3.9 and esbuild load it. webpack, Rollup and Rolldown skip it.
  • No doc line and no test says which result is intended. Main is not uniform: the same barrel with one local export loads effect-pkg.

Fix

  • One paragraph in docs/bundler/index.mdx and one in the optimizeImports JSDoc state the rule, and how to load the package (import "pkg").
  • Six cases in test/bundler/bundler_barrel.test.ts pin the skip (automatic mode and optimizeImports) and three ways to load the package. No source file changes.
  • Verified: the six cases pass on main.

Background

  • A barrel is an index file that only re-exports. For a "sideEffects": false package, the bundler does not resolve or parse a re-export that no importer uses.
  • The linker keeps an unused import of a file that its own package.json does not call side-effect free. The barrel pass decides earlier, from the package.json of the barrel alone.
  • Considered the other rule (load a target that has side effects): it grows diff@9 from 4 to 20 modules and costs 4.4% more instructions on the @mui/icons-material index, so it needs a maintainer's yes first.
Notes

The fixture

entry.js has import { a } from "barrel-pkg". barrel-pkg has "sideEffects": false and export { a } from "./a.js"; export { poly } from "effect-pkg";. effect-pkg has no sideEffects field and sets a global at its top level.

Barrel main Bun 1.3.9 esbuild 0.21.5
as above (pure, "sideEffects": false) skipped runs runs
plus export const local = 0 runs runs runs
plus a bare import "effect-pkg" in the barrel runs runs runs
export * from "effect-pkg" runs runs runs
"sideEffects": ["./index.js"] runs runs runs
no sideEffects field runs runs runs
the application adds import "effect-pkg" runs runs runs
  • webpack 5.112.0 (production), Rollup 4.64.9 with @rollup/plugin-node-resolve, and Rolldown 1.2.14 skip effect-pkg in the first row.
  • Bun 1.3.10 also skips it in the first row. esbuild 0.28.2 also runs it.
  • No GitHub issue reports the first row. bundler: stop a sideEffects array from enabling the barrel optimization #40651 (merged) fixed the same class for a barrel whose package has a sideEffects array.

The other rule, measured

The branch robobun/1ac42521/barrel-defer-by-target-verdict (434c07c) makes such a barrel only nominate its unused re-exports. The resolver is asked about each one, and the record stays deferred only when the linker would drop an unused import of the target. Barrels in optimizeImports do not change. Release builds of main 5900354 and of the branch, same machine:

  • The first row runs effect-pkg. 37 new test cases, 23 fail on main.
  • Output is byte-identical on @mui/icons-material, lucide-react, @fluentui/react-components, @mui/material, radix-ui, react-aria and lodash-es (one name imported from each).
  • import { diffChars } from "diff" (9.0.0): 4 to 20 modules, 7,568 to 20,914 bytes. Bun 1.3.9: 20 modules. esbuild: 7,711 bytes. The cause is in the resolver: libesm/package.json says "sideEffects": false and has no name, and Bun ignores a package.json without a name for this field. resolver: record enclosing package.json regardless of "name" field #33883 is open in that code.
  • An application barrel over react-bootstrap/<Component> (2.10.5): 10 to 121 modules. The proxy directory of each component has a package.json without sideEffects.
  • A module that loads again can fail a build that passes on main: a server-only package behind a barrel in a browser build, or top-level await under --format=cjs.
  • Instructions, median of 5 runs: @mui/icons-material index +4.4%, lucide-react +3.8%, 3,000 re-exports of files in one directory +7.3%, a build with no barrel -0.01% (20,000 modules). Same-directory files need no resolver call (4 to 4 calls).
  • A barrel over 3,000 directories: 4 to 3,003 resolver calls, 576 to 18,722 syscalls, +128% instructions, 27 to 37 MB peak RSS. 300 packages: 4 to 303 resolver calls.
  • Binary: +4,096 bytes stripped.

Related open work

… a package with side effects

A pure barrel of a "sideEffects": false package does not load a
re-export that nothing imports, whatever package it points at. The docs
now say so and say how to load the package. Four bundler tests pin the
skip and three ways to load the package. No source file changes.
@robobun

robobun commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 10:05 PM PT - Oct 20th, 2026

❌ @robobun, your commit 8c63a74 has some failures in Build #129762 (All Failures)


🧪   To try this PR locally:

bunx bun-pr 46062

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

bun-46062 --bun

@robobun

robobun commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Status: ready for review. It waits for the maintainer answer to the question in the description. PR: #46062

How to reproduce on main

mkdir -p node_modules/barrel-pkg node_modules/effect-pkg
echo '{ "name": "barrel-pkg", "main": "./index.js", "sideEffects": false }' > node_modules/barrel-pkg/package.json
echo 'export { a } from "./a.js"; export { poly } from "effect-pkg";' > node_modules/barrel-pkg/index.js
echo 'export const a = "a";' > node_modules/barrel-pkg/a.js
echo '{ "name": "effect-pkg", "main": "./index.js" }' > node_modules/effect-pkg/package.json
echo 'globalThis.effect = "effect-pkg ran"; export const poly = 1;' > node_modules/effect-pkg/index.js
echo 'import { a } from "barrel-pkg"; console.log(a, globalThis.effect);' > entry.js
bun build entry.js --outfile=out.js && bun out.js
  • main and 1.3.10 print a undefined.
  • 1.3.9 and esbuild print a effect-pkg ran.
  • With export const local = 0; added to the barrel, main prints a effect-pkg ran.

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

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Comment thread docs/bundler/index.mdx Resolved
Comment thread test/bundler/bundler_barrel.test.ts Resolved
Comment thread test/bundler/bundler_barrel.test.ts Resolved
@coderabbitai

coderabbitai Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: oven-sh/bun/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: b995e820-377e-4565-9459-e00a94a236ce
📥 Commits

Reviewing files that changed from the base of the PR and between 91e7e3b and 8c63a74.

📒 Files selected for processing: 3
  • docs/bundler/index.mdx
  • packages/bun-types/bun.d.ts
  • test/bundler/bundler_barrel.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.

Walkthrough

The change adds regression tests for unused re-exports from side-effectful packages and documents when optimization skips those packages and how direct imports or barrel configuration can load them.

Changes

Optimize imports re-export behavior

Layer / File(s) Summary
Re-export behavior and documentation
test/bundler/bundler_barrel.test.ts, docs/bundler/index.mdx, packages/bun-types/bun.d.ts
Tests cover cases where unused re-exports are skipped and cases where the side-effectful package loads. The documentation describes these rules and direct imports.

Suggested reviewers: jarred-sumner

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 8c63a

No actionable merge-blocking issue is established; the change is ready for normal checks and the maintainer’s decision on the intended behavior.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly summarizes the main change: documenting and testing the behavior that skips unused barrel re-exports into packages with side effects.
Description check Passed The description explains the problem, proposed documentation and regression tests, verification status, behavior details, and maintainer question. It does not use the exact template headings, but it p…
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.
Full details: Description check

Explanation

The description explains the problem, proposed documentation and regression tests, verification status, behavior details, and maintainer question. It does not use the exact template headings, but it provides the required information in equivalent sections.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

Code review found no issues

No high-confidence issues detected in this change.

This branch has not been deployed

No deployments
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