diff --git a/package.json b/package.json index 6cd4988c3b..d0713ac79d 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "pathe": "2.0.1", "pkg-pr-new": "0.0.39", "playwright-core": "1.49.1", + "rollup": "4.30.1", "semver": "7.6.3", "sherif": "1.1.1", "std-env": "3.8.0", diff --git a/packages/nuxt/src/core/plugins/virtual.ts b/packages/nuxt/src/core/plugins/virtual.ts index 80cc119842..2a3f00a3bd 100644 --- a/packages/nuxt/src/core/plugins/virtual.ts +++ b/packages/nuxt/src/core/plugins/virtual.ts @@ -42,7 +42,7 @@ export const VirtualFSPlugin = (nuxt: Nuxt, options: VirtualFSPluginOptions) => } if (importer && RELATIVE_ID_RE.test(id)) { - const path = resolve(dirname(withoutPrefix(importer)), id) + const path = resolve(dirname(withoutPrefix(decodeURIComponent(importer))), id) const resolved = resolveWithExt(path) if (resolved) { return PREFIX + encodeURIComponent(resolved) diff --git a/packages/nuxt/test/virtual.test.ts b/packages/nuxt/test/virtual.test.ts new file mode 100644 index 0000000000..edd386ff9d --- /dev/null +++ b/packages/nuxt/test/virtual.test.ts @@ -0,0 +1,84 @@ +import { describe, expect, it } from 'vitest' +import type { Nuxt } from '@nuxt/schema' +import { rollup } from 'rollup' + +import { VirtualFSPlugin } from '../src/core/plugins/virtual' + +describe('virtual fs plugin', () => { + it('should support loading files virtually', async () => { + const code = await generateCode('export { foo } from "#build/foo"', { + vfs: { + '/.nuxt/foo': 'export const foo = "hello world"', + }, + }) + expect(code).toMatchInlineSnapshot(` + "const foo = "hello world"; + + export { foo };" + `) + }) + + it('should support loading virtual files by suffix', async () => { + const code = await generateCode('export { foo } from "#build/foo"', { + mode: 'client', + vfs: { + '/.nuxt/foo.server.ts': 'export const foo = "foo server file"', + '/.nuxt/foo.client.ts': 'export const foo = "foo client file"', + }, + }) + expect(code).toMatchInlineSnapshot(` + "const foo = "foo client file"; + + export { foo };" + `) + }) + + it('should support loading files referenced relatively', async () => { + const code = await generateCode('export { foo } from "#build/foo"', { + vfs: { + '/.nuxt/foo': 'export { foo } from "./bar"', + '/.nuxt/bar': 'export const foo = "relative import"', + }, + }) + expect(code).toMatchInlineSnapshot(` + "const foo = "relative import"; + + export { foo };" + `) + }) +}) + +async function generateCode (input: string, options: { mode?: 'client' | 'server', vfs: Record }) { + const stubNuxt = { + options: { + extensions: ['.ts', '.js'], + alias: { + '~': '/', + '#build': '/.nuxt', + }, + }, + vfs: options.vfs, + } as unknown as Nuxt + + const bundle = await rollup({ + input: 'entry.ts', + plugins: [ + { + name: 'entry', + resolveId (id) { + if (id === 'entry.ts') { + return id + } + }, + load (id) { + if (id === 'entry.ts') { + return input + } + }, + }, + VirtualFSPlugin(stubNuxt, { mode: options.mode || 'client', alias: stubNuxt.options.alias }).rollup(), + ], + }) + const { output: [chunk] } = await bundle.generate({}) + return chunk.code.trim() +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f0a760775..fde9c83d67 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -155,6 +155,9 @@ importers: playwright-core: specifier: 1.49.1 version: 1.49.1 + rollup: + specifier: 4.30.1 + version: 4.30.1 semver: specifier: 7.6.3 version: 7.6.3