Skip to content

[Bug]: chunk-optimization dedupe incorrectly drops sibling dynamic entry side effects #9350

@IWANABETHATGUY

Description

@IWANABETHATGUY

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.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions