Description
Currently, if a virtual: module is imported (e.g. import 'virtual:my-plugin:global-state') but cannot be resolved by any plugin, or is inadvertently marked as external during an SSR build, the bundler might succeed silently or just emit a non-blocking warning. This causes the application to crash at runtime with a Cannot find module 'virtual:...' error.
Additionally, if a virtual module is resolved but lacks explicit side-effects, it might be silently tree-shaken from the output, leading to successful builds that are intrinsically broken at runtime.
Suggested solution
Since virtual: (and \0 prefixed) modules are strictly build-time constructs provided by plugins, they inherently cannot exist at runtime. I would like Vite / Rolldown to enforce a strict behavior:
- Throw a hard build error if an import path starting with
virtual: remains unresolved after all plugins have been executed
- Prevent
virtual: modules from being externalized: they should automatically circumvent external SSR configurations. If a virtual module ends up as a raw import in the final bundle output, the build must fail
- (Optional but helpful) Emit a warning/error if a bare virtual import used for side-effects (e.g.
import 'virtual:plugin-init') is completely tree-shaken and dropped from the final bundle
Alternative
- Relying on plugin authors to correctly flag
moduleSideEffects, though this doesn't protect the user from missing plugins or misconfigured SSR externalization
- Manually inspecting the generated
.mjs server bundles to verify that virtual modules haven't leaked into the runtime code, which is a slow and error-prone DX
Why is this needed?
Failing the build early and explicitly with a dedicated error (e.g. [UNRESOLVED_VIRTUAL_MODULE]) would save developers hours of debugging runtime crashes in production or SSR environments. Virtual modules leaking into runtime or being silently ignored is silently dropped is a guaranteed fatal error, so the build should fail immediately.
Validations
Description
Currently, if a
virtual:module is imported (e.g.import 'virtual:my-plugin:global-state') but cannot be resolved by any plugin, or is inadvertently marked as external during an SSR build, the bundler might succeed silently or just emit a non-blocking warning. This causes the application to crash at runtime with aCannot find module 'virtual:...'error.Additionally, if a virtual module is resolved but lacks explicit side-effects, it might be silently tree-shaken from the output, leading to successful builds that are intrinsically broken at runtime.
Suggested solution
Since
virtual:(and\0prefixed) modules are strictly build-time constructs provided by plugins, they inherently cannot exist at runtime. I would like Vite / Rolldown to enforce a strict behavior:virtual:remains unresolved after all plugins have been executedvirtual:modules from being externalized: they should automatically circumventexternalSSR configurations. If a virtual module ends up as a raw import in the final bundle output, the build must failimport 'virtual:plugin-init') is completely tree-shaken and dropped from the final bundleAlternative
moduleSideEffects, though this doesn't protect the user from missing plugins or misconfigured SSR externalization.mjsserver bundles to verify that virtual modules haven't leaked into the runtime code, which is a slow and error-prone DXWhy is this needed?
Failing the build early and explicitly with a dedicated error (e.g.
[UNRESOLVED_VIRTUAL_MODULE]) would save developers hours of debugging runtime crashes in production or SSR environments. Virtual modules leaking into runtime or being silently ignored is silently dropped is a guaranteed fatal error, so the build should fail immediately.Validations