Repository navigation
bundler: document and pin the skip of an unused barrel re-export into a package with side effects - #46062
bundler: document and pin the skip of an unused barrel re-export into a package with side effects#46062robobun wants to merge 2 commits into
Conversation
… 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.
|
Updated 10:05 PM PT - Oct 20th, 2026
❌ @robobun, your commit 8c63a74 has some failures in 🧪 To try this PR locally: bunx bun-pr 46062That installs a local version of the PR into your bun-46062 --bun |
|
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
|
…remedy to automatic mode
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
WalkthroughThe 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. ChangesOptimize imports re-export behavior
Suggested reviewers: Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to
|
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
export { poly } from "effect-pkg"in a pure barrel of a"sideEffects": falsepackage does not loadeffect-pkgwhen nothing importspoly, althougheffect-pkghas side effects. Bun 1.3.9 and esbuild load it. webpack, Rollup and Rolldown skip it.effect-pkg.Fix
docs/bundler/index.mdxand one in theoptimizeImportsJSDoc state the rule, and how to load the package (import "pkg").test/bundler/bundler_barrel.test.tspin the skip (automatic mode andoptimizeImports) and three ways to load the package. No source file changes.Background
"sideEffects": falsepackage, the bundler does not resolve or parse a re-export that no importer uses.importof a file that its ownpackage.jsondoes not call side-effect free. The barrel pass decides earlier, from thepackage.jsonof the barrel alone.diff@9from 4 to 20 modules and costs 4.4% more instructions on the@mui/icons-materialindex, so it needs a maintainer's yes first.Notes
The fixture
entry.jshasimport { a } from "barrel-pkg".barrel-pkghas"sideEffects": falseandexport { a } from "./a.js"; export { poly } from "effect-pkg";.effect-pkghas nosideEffectsfield and sets a global at its top level."sideEffects": false)export const local = 0import "effect-pkg"in the barrelexport * from "effect-pkg""sideEffects": ["./index.js"]sideEffectsfieldimport "effect-pkg"@rollup/plugin-node-resolve, and Rolldown 1.2.14 skipeffect-pkgin the first row.sideEffectsarray.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 inoptimizeImportsdo not change. Release builds of main 5900354 and of the branch, same machine:effect-pkg. 37 new test cases, 23 fail on main.@mui/icons-material,lucide-react,@fluentui/react-components,@mui/material,radix-ui,react-ariaandlodash-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.jsonsays"sideEffects": falseand has noname, 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.react-bootstrap/<Component>(2.10.5): 10 to 121 modules. The proxy directory of each component has a package.json withoutsideEffects.--format=cjs.@mui/icons-materialindex +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).Related open work
bun test --changedscan #45938 makes--ignore-dce-annotationsturn automatic mode off and keeps the barrel pass out of graph scans. It does not change the first row.