vite icon indicating copy to clipboard operation
vite copied to clipboard

virtual file system is broken in v6

Open himself65 opened this issue 1 year ago • 10 comments

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

himself65 avatar Sep 28 '24 10:09 himself65

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)

hi-ogawa avatar Sep 29 '24 02:09 hi-ogawa

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.

sheremet-va avatar Oct 03 '24 06:10 sheremet-va

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.

hi-ogawa avatar Oct 05 '24 07:10 hi-ogawa

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 avatar Oct 05 '24 19:10 himself65

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

hi-ogawa avatar Oct 06 '24 02:10 hi-ogawa

yes

himself65 avatar Oct 06 '24 03:10 himself65

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.

hi-ogawa avatar Oct 06 '24 09:10 hi-ogawa

@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

hi-ogawa avatar Oct 07 '24 08:10 hi-ogawa

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 avatar Oct 12 '24 03:10 himself65

@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

hi-ogawa avatar Oct 16 '24 01:10 hi-ogawa

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 avatar Dec 22 '24 18:12 2dragonly

@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

hi-ogawa avatar Dec 23 '24 00:12 hi-ogawa

@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

I didn't mean ?abcd=1234 tho,. I just want to know why ?t special query doesn't work on vite 6

Screenshot 2024-12-23 at 08.14.02.webm

2dragonly avatar Dec 23 '24 00:12 2dragonly

I just want to know why ?t special 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

hi-ogawa avatar Dec 23 '24 00:12 hi-ogawa

dose the virtual module can have import statement? like
if (id === resolvedId) { return { code: import { isUndefined } from 'lodash'; console.log(123) }; }

Shangyunliang avatar Dec 26 '24 14:12 Shangyunliang