diff --git a/package.json b/package.json index 5a8e67ca1d..b3405a287a 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,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 6d5bd62920..73ba69b5d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -158,6 +158,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 @@ -483,7 +486,7 @@ importers: devDependencies: '@nuxt/scripts': specifier: 0.9.5 - version: 0.9.5(@nuxt/devtools@1.7.0(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.78.0)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.3)))(@unocss/webpack@0.62.4(rollup@4.30.1)(webpack@5.97.1(esbuild@0.24.2)))(@vue/compiler-core@3.5.13)(change-case@5.4.4)(fuse.js@7.0.0)(nuxt@packages+nuxt)(postcss@8.5.1)(rollup@4.30.1)(typescript@5.7.3)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.78.0)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.3))(webpack@5.97.1(esbuild@0.24.2)) + version: 0.9.5(@nuxt/devtools@1.7.0(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.78.0)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.3)))(@unocss/webpack@0.62.4(rollup@4.30.1)(webpack@5.97.1(esbuild@0.24.2)))(@vue/compiler-core@3.5.13)(change-case@5.4.4)(db0@0.1.4)(fuse.js@7.0.0)(ioredis@5.4.1)(nuxt@packages+nuxt)(postcss@8.5.1)(rollup@4.30.1)(typescript@5.7.3)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.78.0)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.3))(webpack@5.97.1(esbuild@0.24.2)) '@parcel/watcher': specifier: 2.5.0 version: 2.5.0 @@ -8996,7 +8999,7 @@ snapshots: string-width: 4.2.3 webpack: 5.97.1 - '@nuxt/scripts@0.9.5(@nuxt/devtools@1.7.0(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.78.0)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.3)))(@unocss/webpack@0.62.4(rollup@4.30.1)(webpack@5.97.1(esbuild@0.24.2)))(@vue/compiler-core@3.5.13)(change-case@5.4.4)(fuse.js@7.0.0)(nuxt@packages+nuxt)(postcss@8.5.1)(rollup@4.30.1)(typescript@5.7.3)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.78.0)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.3))(webpack@5.97.1(esbuild@0.24.2))': + '@nuxt/scripts@0.9.5(@nuxt/devtools@1.7.0(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.78.0)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.3)))(@unocss/webpack@0.62.4(rollup@4.30.1)(webpack@5.97.1(esbuild@0.24.2)))(@vue/compiler-core@3.5.13)(change-case@5.4.4)(db0@0.1.4)(fuse.js@7.0.0)(ioredis@5.4.1)(nuxt@packages+nuxt)(postcss@8.5.1)(rollup@4.30.1)(typescript@5.7.3)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.78.0)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.3))(webpack@5.97.1(esbuild@0.24.2))': dependencies: '@nuxt/devtools-kit': 1.7.0(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.78.0)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1)) '@nuxt/devtools-ui-kit': 1.5.1(@nuxt/devtools@1.7.0(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.78.0)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.3)))(@unocss/webpack@0.62.4(rollup@4.30.1)(webpack@5.97.1(esbuild@0.24.2)))(@vue/compiler-core@3.5.13)(change-case@5.4.4)(fuse.js@7.0.0)(nuxt@packages+nuxt)(postcss@8.5.1)(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.78.0)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.3))(webpack@5.97.1(esbuild@0.24.2))