You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Note on this issue number: this was previously "Consider caching vat bundles". That content has been folded into #991, which covers bundle identity and caching together as one content-addressed store. This number has been reused for a correctness concern that surfaced while investigating it.
removeDynamicImportsPlugin (packages/kernel-utils/src/vite-plugins/bundle-vat.ts:33-60) rewrites every import('literal') in the bundle graph to Promise.resolve({}).
The reason is legitimate, and the plugin's own comment explains it: rolldown refuses IIFE output when any module in the graph contains a dynamic import, even with output.codeSplitting disabled. Some dependencies use lazy await import(x) in utility functions — viem is named — that vats never actually call.
The concern is the failure mode when that assumption is wrong.
The transform applies across the whole dependency graph, including third-party code we do not control, and it does not fail — it substitutes an empty object. So if a vat ever does reach one of those code paths:
the dynamic import resolves successfully to {}
every property access on the result is undefined
the failure surfaces somewhere unrelated, as a TypeError or as silently wrong behaviour
That is a silent wrong answer rather than an error, in code we rewrote without the dependency author's knowledge. It also means a dependency version bump can quietly add a newly-reachable dynamic import, and nothing tells us.
Secondary effect: two parties building the same vat from different transitive dependency versions can get different bundles with no signal — relevant to #991, since bundle identity is only meaningful if the build is reproducible.
Options, roughly in increasing cost:
Fail loudly instead of silently. Replace Promise.resolve({}) with a rejected promise, or a thunk that throws a clear "dynamic import was stripped at bundle time" error. Same build outcome, but reachable paths become visible immediately instead of turning into undefined. Cheapest real improvement and probably worth doing regardless of what else we choose.
Report what was stripped. Have the plugin emit the list of modules and specifiers it rewrote, so the assumption is auditable and reviewable rather than invisible.
Scope the transform. Only rewrite in node_modules, or only for an explicit allowlist of specifiers we have checked, rather than the entire graph by default.
removeDynamicImportsPlugin(packages/kernel-utils/src/vite-plugins/bundle-vat.ts:33-60) rewrites everyimport('literal')in the bundle graph toPromise.resolve({}).The reason is legitimate, and the plugin's own comment explains it: rolldown refuses IIFE output when any module in the graph contains a dynamic import, even with
output.codeSplittingdisabled. Some dependencies use lazyawait import(x)in utility functions — viem is named — that vats never actually call.The concern is the failure mode when that assumption is wrong.
The transform applies across the whole dependency graph, including third-party code we do not control, and it does not fail — it substitutes an empty object. So if a vat ever does reach one of those code paths:
{}undefinedTypeErroror as silently wrong behaviourThat is a silent wrong answer rather than an error, in code we rewrote without the dependency author's knowledge. It also means a dependency version bump can quietly add a newly-reachable dynamic import, and nothing tells us.
Secondary effect: two parties building the same vat from different transitive dependency versions can get different bundles with no signal — relevant to #991, since bundle identity is only meaningful if the build is reproducible.
Options, roughly in increasing cost:
Promise.resolve({})with a rejected promise, or a thunk that throws a clear "dynamic import was stripped at bundle time" error. Same build outcome, but reachable paths become visible immediately instead of turning intoundefined. Cheapest real improvement and probably worth doing regardless of what else we choose.node_modules, or only for an explicit allowlist of specifiers we have checked, rather than the entire graph by default.endoZipBase64is not a small change — but it is the one thing that would remove the need for this plugin entirely.Option 1 alone would convert this from a latent silent failure into a loud one, which is most of the value.