Minimal repro for a behavior difference between Rollup and Rolldown PR #9305 (401218c).
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.js has a top-level side effect:
console.log('d1', marker);Calling only main.load2() should not execute d1.js.
dist/rollup-v4.60.3/— generated with Rollup v4.60.3.dist/rolldown-pr9305/— generated from Rolldown PR #9305 at401218c.
npm run check:rollup
# ["main","shared","d2"]
npm run check:rolldown
# ["main","shared","d1 main","d2"]Rolldown PR #9305 emits this for d2.js:
import "./d1.js";
console.log("d2");So import("./d2.js") executes sibling dynamic entry d1.js first, even though source semantics do not require d1.js to be loaded.
Rollup does not do this. Its d2.js imports shared.js directly:
import './shared.js';
console.log('d2');The risky assumption appears to be treating “the same user entry can reach both d1 and d2” as “loading d2 implies d1 is already loaded”. Those are different: main.load2() can run before main.load1().
A safe dominator check needs to prove every dynamic-import path to d2 goes through d1, or avoid merging sibling dynamic entries this way.
npm run build:rollup