Reproduction link or steps
Minimal repro: https://github.com/hyf0-agent/rolldown-pr9305-sibling-dynamic-side-effect
Source graph:
main.js
├─ import("./d1.js")
└─ import("./d2.js")
d1.js
├─ static import main.js
├─ static import shared.js
└─ import("./d2.js")
d2.js
└─ static import shared.js
d1.js has a top-level side effect: console.log("d1", marker);
main.js exposes load1()/load2() returning import("./d1.js") / import("./d2.js").
Run:
npm run check:rollup # ["main","shared","d2"]
npm run check:rolldown # ["main","shared","d1 main","d2"]
What is expected?
Calling only main.load2() should not execute d1.js. Rollup v4.60.3 emits a d2.js that imports shared.js directly. Sequence: ["main","shared","d2"].
What is actually happening?
After #9305, Rolldown emits d2.js as import "./d1.js"; console.log("d2");, producing ["main","shared","d1 main","d2"] — the d1.js side effect runs even though main.load2() never imports d1.
The chunk_optimization dedupe pass appears to treat "the same user entry can reach both d1 and d2" as "loading d2 implies d1 is already loaded". That is unsound for sibling dynamic entries: main.load2() can run before (or without) main.load1(). A safe reduction needs to prove every dynamic-import path to d2 goes through d1 (a true dominator), or avoid merging across sibling dynamic entries.
System Info
Reproduced against rolldown PR #9305 at commit 401218c (merged as 7e5855c).
Any additional comments?
Reported by @hyf0 in #9305 (review) after the PR was merged. Filing as a follow-up tracking issue.
Reproduction link or steps
Minimal repro: https://github.com/hyf0-agent/rolldown-pr9305-sibling-dynamic-side-effect
Source graph:
main.js ├─ import("./d1.js") └─ import("./d2.js") d1.js ├─ static import main.js ├─ static import shared.js └─ import("./d2.js") d2.js └─ static import shared.jsd1.jshas a top-level side effect:console.log("d1", marker);main.jsexposesload1()/load2()returningimport("./d1.js")/import("./d2.js").Run:
What is expected?
Calling only
main.load2()should not executed1.js. Rollup v4.60.3 emits ad2.jsthat importsshared.jsdirectly. Sequence:["main","shared","d2"].What is actually happening?
After #9305, Rolldown emits
d2.jsasimport "./d1.js"; console.log("d2");, producing["main","shared","d1 main","d2"]— thed1.jsside effect runs even thoughmain.load2()never importsd1.The
chunk_optimizationdedupe pass appears to treat "the same user entry can reach bothd1andd2" as "loadingd2impliesd1is already loaded". That is unsound for sibling dynamic entries:main.load2()can run before (or without)main.load1(). A safe reduction needs to prove every dynamic-import path tod2goes throughd1(a true dominator), or avoid merging across sibling dynamic entries.System Info
Any additional comments?
Reported by @hyf0 in #9305 (review) after the PR was merged. Filing as a follow-up tracking issue.