Skip to content

aminpaks/vite8-cycle-import-bug

Repository files navigation

vite 8 cycle-import bug — reproducible test case

Identical app code that runs fine on vite 6 (rollup) crashes on vite 8 (rolldown).

When two ES modules form a static-import cycle AND both are dynamic-import targets, vite 8 (rolldown) merges them into one chunk and lets rolldown name the chunk after one of the modules' basenames. As a result, import('./action') resolves to a namespace that contains both action's exports AND form's exports (renamed to n, t).

App code that validates the namespace contract — like Object.entries(mod).forEach(([k, v]) => { if (!isExpected(v)) throw … }), which is the standard pattern for a module-as-registry — then throws, crashing the app.

Background

This is the underlying rolldown behavior that caused Shopify Admin's iPhone WebContent OOM crashes during the f_vite_8_client_bundle rollout (incident #22422). In our codebase the same cycle-prevention pass (rolldown #9093) snowballed through 1,067 unrelated modules — 65 admin sections + refractor's 279 prismjs grammars + vendor packages — into a single 38 MB loaders-common chunk that tripped iPhone WKWebView's 1,536 MB WebContent process limit.

This repository extracts the underlying namespace-pollution mechanism into a 4-file standalone repro you can build and inspect locally without any of the admin-web tooling.

What this repo demonstrates

src/main.ts      ─▶  await import('./form.ts')      // dynamic-import target
                 ─▶  await import('./action.ts')    // dynamic-import target

src/form.ts      ◀─── statically import each other ───▶  src/action.ts
                                  (CYCLE)

main.ts represents app code: it dynamic-imports each module and validates that the returned namespace contains exactly the keys it declared. If extra keys appear, the app throws.

script bundler result
pnpm run build:v6 vite 6 / rollup ✅ app runs — namespaces are clean
pnpm run build:v8 vite 8 / rolldown 💥 app crashes — action's namespace contains form's exports renamed to n, t

Run the repro

pnpm install
pnpm run all

Expected output

========================================================================
vite 6 (rollup)
========================================================================
form.ts   body in: form-<hash>.js
action.ts body in: form-<hash>.js

  form namespace exports : [ 'callActionFromForm', 'formImpl' ]
  action namespace exports: [ 'actionImpl', 'callFormFromAction' ]

  ✅ both modules pass their contract — app continues

✅ app ran successfully — no contract violation

========================================================================
vite 8 (rolldown)
========================================================================
form.ts   body in: action-<hash>.js
action.ts body in: action-<hash>.js

  form namespace exports : [ 'callActionFromForm', 'formImpl' ]
  action namespace exports: [ 'actionImpl', 'callFormFromAction', 'n', 't' ]

  💥 App crashed: action module contract violated:
     expected exports [actionImpl, callFormFromAction],
     got [actionImpl, callFormFromAction, n, t]

💥 APP CRASHED on vite 8: namespace pollution broke the contract

What's actually happening

In the vite 8 build, open dist-v8/assets/action-<hash>.js and look at the bottom:

export { actionImpl, callFormFromAction, formImpl as n, callActionFromForm as t };
//                                       ^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^
//                                       form's exports leaking out as n, t

That single physical chunk contains both form.ts and action.ts (rolldown's cycle-prevention pass merged them). Because the chunk is named action-<hash>.js (matching action.ts's basename), rolldown makes the chunk's top-level exports the union of all members' symbols.

When main.ts does import('./action'), it resolves to that chunk and gets the polluted namespace. Validation throws. App crashes.

About

Reproducible test case for the vite 8 (rolldown) namespace-pollution bug caused by static-import cycles between dynamic-import targets. App code that works on vite 6 crashes on vite 8.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors