Reproduction link or steps
Minimal reproduction: https://github.com/KurtGokhan/rolldown-checkglobals-repro
git clone https://github.com/KurtGokhan/rolldown-checkglobals-repro
cd rolldown-checkglobals-repro
npm install
npm run build
grep -c 'checkGlobals()' dist/assets/*.js # => 1 (call kept)
grep -c 'function checkGlobals' dist/assets/*.js # => 0 (definition gone)
npm run preview # blank page; console: "ReferenceError: checkGlobals is not defined"
The bug surfaces in the wild via msw: its core/index.mjs calls checkGlobals() at module top level, while its package.json declares "sideEffects": false.
// node_modules/msw/lib/core/index.mjs
import { checkGlobals } from './utils/internal/checkGlobals.mjs';
// ...
checkGlobals(); // top-level side effect
export { http, HttpResponse, /* ... */ };
Relevant config (vite.config.js):
export default defineConfig({
build: {
minify: false,
rolldownOptions: { output: { strictExecutionOrder: true } },
},
});
Trigger conditions (all required):
output.strictExecutionOrder: true — without it, both the call and the definition are removed together (no dangling reference)
- the dependency declares
"sideEffects": false
- that dependency calls an imported cross-module function at its module top level
- other exports of the dependency are used, so its module is retained
What is expected?
A referenced binding must be retained whenever the reference (the checkGlobals() call) is retained, regardless of "sideEffects": false. Either keep both the call and the definition, or remove both — never keep the call while dropping the definition.
This is what rolldown 1.0.3 (vite 8.0.16) does: the definition is present and the app works.
What is actually happening?
rolldown keeps the top-level checkGlobals() call but tree-shakes the checkGlobals function definition out of every chunk, leaving a free identifier → ReferenceError: checkGlobals is not defined at module-eval time → blank app.
This is a regression between rolldown 1.0.3 and 1.1.3:
| vite |
rolldown |
result |
| 8.1.0 |
1.1.3 |
❌ definition dropped, ReferenceError |
| 8.0.16 |
1.0.3 |
✅ definition retained, works |
System Info
System:
OS: macOS 26.5.1
CPU: (12) arm64 Apple M3 Pro
Memory: 845.58 MB / 36.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 24.18.0
npm: 11.16.0
pnpm: 10.28.1
bun: 1.3.13
Deno: 2.8.3+
Browsers:
Chrome: 149.0.7827.156
Firefox Developer Edition: 151.0
Safari: 26.5
npmPackages:
rolldown: 1.1.3 (transitive, via vite 8.1.0)
vite: 8.1.0
msw: 2.14.6
Any additional comments?
msw declaring "sideEffects": false while running checkGlobals() at import time is arguably inaccurate on msw's side, but a bundler must still not drop a referenced binding — so this looks like a rolldown correctness bug in scope-hoisting / side-effect handling. The concerning part is that the same mechanism could silently drop other referenced cross-module bindings that don't happen to throw loudly at load.
Originally hit while bumping vite 8.0.x → 8.1.0 in a real app, where it produced a fully blank page (ReferenceError: checkGlobals is not defined).
Reproduction link or steps
Minimal reproduction: https://github.com/KurtGokhan/rolldown-checkglobals-repro
The bug surfaces in the wild via
msw: itscore/index.mjscallscheckGlobals()at module top level, while itspackage.jsondeclares"sideEffects": false.Relevant config (
vite.config.js):Trigger conditions (all required):
output.strictExecutionOrder: true— without it, both the call and the definition are removed together (no dangling reference)"sideEffects": falseWhat is expected?
A referenced binding must be retained whenever the reference (the
checkGlobals()call) is retained, regardless of"sideEffects": false. Either keep both the call and the definition, or remove both — never keep the call while dropping the definition.This is what rolldown
1.0.3(vite8.0.16) does: the definition is present and the app works.What is actually happening?
rolldown keeps the top-level
checkGlobals()call but tree-shakes thecheckGlobalsfunction definition out of every chunk, leaving a free identifier →ReferenceError: checkGlobals is not definedat module-eval time → blank app.This is a regression between rolldown
1.0.3and1.1.3:ReferenceErrorSystem Info
System: OS: macOS 26.5.1 CPU: (12) arm64 Apple M3 Pro Memory: 845.58 MB / 36.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 24.18.0 npm: 11.16.0 pnpm: 10.28.1 bun: 1.3.13 Deno: 2.8.3+ Browsers: Chrome: 149.0.7827.156 Firefox Developer Edition: 151.0 Safari: 26.5 npmPackages: rolldown: 1.1.3 (transitive, via vite 8.1.0) vite: 8.1.0 msw: 2.14.6Any additional comments?
mswdeclaring"sideEffects": falsewhile runningcheckGlobals()at import time is arguably inaccurate on msw's side, but a bundler must still not drop a referenced binding — so this looks like a rolldown correctness bug in scope-hoisting / side-effect handling. The concerning part is that the same mechanism could silently drop other referenced cross-module bindings that don't happen to throw loudly at load.Originally hit while bumping vite
8.0.x→8.1.0in a real app, where it produced a fully blank page (ReferenceError: checkGlobals is not defined).