virtual file system is broken in v6
Describe the bug
import { createServer } from 'vite'
const server = await createServer({
plugins: [
{
name: 'virtual-fs',
resolveId (id) {
if (id === 'my-virtual-file') {
return id + '.js'
}
},
load (id) {
if (id === 'my-virtual-file.js') {
return 'export default "Hello, world!"'
}
}
}
]
})
console.log(await server.ssrLoadModule('my-virtual-file.js'))
await server.close()
This is working in v5, but broken in v6.
Reproduction
https://stackblitz.com/edit/stackblitz-starters-prwdrf?file=index.js
Steps to reproduce
No response
System Info
N/A
Used Package Manager
npm
Logs
No response
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] Make sure this is a Vite issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to vuejs/core instead.
- [X] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- [X] The provided reproduction is a minimal reproducible example of the bug.
I suppose minimal repro is just load hook:
import { createServer } from 'vite';
const server = await createServer({
plugins: [
{
name: 'virtual-fs',
load(id) {
if (id === 'my-virtual-file.js') {
return 'export default "Hello, world!"';
}
},
},
],
});
console.log(await server.ssrLoadModule('my-virtual-file.js'));
On v5 https://stackblitz.com/edit/stackblitz-starters-cr4to5?file=index.js
{ default: 'Hello, world!', [Symbol(Symbol.toStringTag)]: 'Module' }
On v6 https://stackblitz.com/edit/stackblitz-starters-asgtkf?file=index.js
Error: [vite] cannot find entry point module 'my-virtual-file.js'.
at async ModuleLoader.import (https://stackblitzstartersasgtkf-wdbx.w-credentialless-staticblitz.com/builtins.ddb8d84d.js:154:2688)
I don't see why this even worked before, to be honest. The my-virtual-file.js in the reproduction is the resolved ID, not the user ID that would be used in an import. For example, this fails in Vite 5 and Vite 6:
import 'my-virtual-file.js'
Reproduction: https://stackblitz.com/edit/stackblitz-starters-a9fxsa?file=actual-fs.js,index.js
This however works as intended (if you provide resolveId hook like in the reproduction):
import 'my-virtual-file'
I'd say making the entry point work the same way as the import is a bug fix.
Interesting, Vite 6's behavior looks legitimate to me.
@himself65 Do you have any reason to do server.ssrLoadModule('my-virtual-file.js') instead of server.ssrLoadModule('my-virtual-file'). It looks like a simply typo to me and I don't think this change would be a blocker anyways, so I'll close this issue for now.
yes, in waku we build on top of vite, and we have a file import , when user not provided we will make a fake one. So in this case we wanna ts/js/tsx
yes, in waku we build on top of vite, and we have a file import , when user not provided we will make a fake one. So in this case we wanna ts/js/tsx
@himself65 Are you saying Vite 5 behavior is required for your use case? I cannot tell from your initial reproduction, so it would be great if you can elaborate it further. Maybe this plugin? https://github.com/dai-shi/waku/blob/711be51c02eac6a767fe5651fae6d6417d0679f8/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts#L40
yes
Probably your reproduction was misleading and the issue might be more like this scenario:
ssrLoadModule("/abs-path-to/entries")
plugin.resolveId: "/abs-path-to/entries" --> "/abs-path-to/entries.jsx"
plugin.load: "/abs-path-to/entries.jsx" --> ...virtual content...
On Vite 6, resolveId is called differently and maybe that's causing an issue.
- Vite 5 https://stackblitz.com/edit/stackblitz-starters-amncz3?file=index.js
❯ node index.js
[resolveId] { id: '/home/projects/stackblitz-starters-prwdrf/entries' }
{ default: 'Hello, world!', [Symbol(Symbol.toStringTag)]: 'Module' }
- Vite 6 https://stackblitz.com/edit/stackblitz-starters-hb6a1z?file=index.js
❯ node index.js
[resolveId] { id: '/home/projects/stackblitz-starters-prwdrf/entries' }
[resolveId] { id: '/entries' }
5:52:39 PM [vite] (ssr) Error when evaluating SSR module /home/projects/stackblitz-starters-prwdrf/entries:
|- Error: [vite] cannot find entry point module '/entries'.
at fetchModule (file:///home/projects/stackblitz-starters-prwdrf/node_modules/vite/dist/node/chunks/dep-BHXIdTzn.js:66013:13)
This seems like an odd case, but not sure. Let me re-open the issue for now.
@himself65 I haven't look into the issue, but I would probably try this to provide "optionally virtual entry" from plugin: https://stackblitz.com/edit/stackblitz-starters-t9hvyx?file=index.js
im not sure the best practice here, entries file is possible to be virtual or real file. I feel the logic is broken after we add virtual: as prefix
@himself65 Other than switching to virtual:, you might be able to workaround by removing root from entries https://github.com/dai-shi/waku/blob/711be51c02eac6a767fe5651fae6d6417d0679f8/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts#L55-L56 (for example, use /src/entries instead of /abs-path-to/src/entries).
The issue (on Vite, fair to say) is that it stripes root as a part of normalization here, so your plugin's resolveId doesn't consistently see root.
https://github.com/vitejs/vite/blob/93b03b2692cc2617570f83e365606dae0a8ed859/packages/vite/src/module-runner/runner.ts#L223
on v5, adding ?t=${Date.now()} works properly
await viteDevServer.ssrLoadModule(`${options.serverFile}?t=${Date.now()}`)
but on v6, i get error [vite] cannot find entry point module 'virtual:server-build?t=1734890638891'.
^ this can be fix by removing ?t=${Date.now()}
await viteDevServer.ssrLoadModule(options.serverFile)
@2dragonly Your issue seems a bit different though it looks like technically a regression. Can you create a separate issue?
Probably there was some special handling for the exact query ?t=${Date.now()} but the query in general form ?abcd=1234 isn't supported in Vite 5. https://stackblitz.com/edit/vitejs-vite-98ukz5g2?file=repro.js
@2dragonly Your issue seems a bit different though it looks like technically a regression. Can you create a separate issue?
Probably there was some special handling for the exact query
?t=${Date.now()}but the query in general form?abcd=1234isn't supported in Vite 5. https://stackblitz.com/edit/vitejs-vite-98ukz5g2?file=repro.js
I didn't mean ?abcd=1234 tho,. I just want to know why ?t special query doesn't work on vite 6
I just want to know why
?tspecial query doesn't work on vite 6
I haven't confirmed but I'm guessing ?t is handled transformRequest layer (in Vite 5 ssrLoadModule)
https://github.com/vitejs/vite/blob/ac329685bba229e1ff43e3d96324f817d48abe48/packages/vite/src/node/server/transformRequest.ts#L147-L153
but Vite 6 has a different architecture and checks resolveId first, which doesn't have a special treatment of ?t (or any query) for virtual modules.
https://github.com/vitejs/vite/blob/ac329685bba229e1ff43e3d96324f817d48abe48/packages/vite/src/node/ssr/fetchModule.ts#L75-L78
dose the virtual module can have import statement? like
if (id === resolvedId) {
return { code: import { isUndefined } from 'lodash'; console.log(123) };
}