diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 6056c8395164..a7807dd86c00 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -304,6 +304,24 @@ export class ViteNodeRunner { filename: __filename, dirname: __dirname, } + if (typeof import.meta.resolve !== 'undefined') { + // check if 2nd argument feature is available + let ok = false + try { + // old Node returns Promise + const testParent = await import.meta.resolve('.', new URL('./__test_parent__/', import.meta.url)) + ok = typeof testParent === 'string' && testParent.endsWith('/__test_parent__/') + } + catch { + // old Node throws when invalid path + } + (meta as any).resolve = (specifier: string, parent?: string | URL) => { + if (!ok) + throw new Error('[vite-node] "--experimental-import-meta-resolve" is required to enable "import.meta.resolve"') + return import.meta.resolve(specifier, parent ?? href) + } + } + const exports = Object.create(null) Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dab06b605830..8208d57fc1fd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1824,6 +1824,12 @@ importers: specifier: workspace:* version: link:../../packages/vitest + test/import-meta-resolve: + devDependencies: + vitest: + specifier: workspace:* + version: link:../../packages/vitest + test/mixed-pools: devDependencies: vitest: diff --git a/test/import-meta-resolve/package.json b/test/import-meta-resolve/package.json new file mode 100644 index 000000000000..037385d1b1cf --- /dev/null +++ b/test/import-meta-resolve/package.json @@ -0,0 +1,11 @@ +{ + "name": "@vitest/test-import-meta-resolve", + "type": "module", + "private": true, + "scripts": { + "test": "vitest" + }, + "devDependencies": { + "vitest": "workspace:*" + } +} diff --git a/test/import-meta-resolve/test/basic.test.ts b/test/import-meta-resolve/test/basic.test.ts new file mode 100644 index 000000000000..7b587fd1bd66 --- /dev/null +++ b/test/import-meta-resolve/test/basic.test.ts @@ -0,0 +1,41 @@ +import { expect, test } from 'vitest' + +test('basic', () => { + // relative path + expect( + cleanDir(import.meta.resolve('../package.json')), + ).toMatchInlineSnapshot(`"__DIR__/test/import-meta-resolve/package.json"`) + + // not throw in latest NodeJS + expect(cleanDir(import.meta.resolve('../no-such-file'))).toMatchInlineSnapshot( + `"__DIR__/test/import-meta-resolve/no-such-file"`, + ) + + // with 2nd argument `parent` + expect( + cleanDir( + import.meta.resolve('./package.json', new URL('..', import.meta.url)), + ), + ).toMatchInlineSnapshot(`"__DIR__/test/import-meta-resolve/package.json"`) + + // node_module + expect(cleanDir(import.meta.resolve('vitest'))).toMatchInlineSnapshot( + `"__DIR__/packages/vitest/dist/index.js"`, + ) + + expect(() => + cleanDir(import.meta.resolve('@vitest/not-such-module')), + ).toThrow( + expect.objectContaining({ + message: expect.stringContaining( + 'Cannot find package \'@vitest/not-such-module\' imported from', + ), + }), + ) +}) + +// make output deterministic +function cleanDir(out: string) { + const dir = new URL('../../..', import.meta.url).toString() + return out.replace(dir, '__DIR__/') +} diff --git a/test/import-meta-resolve/vitest.config.ts b/test/import-meta-resolve/vitest.config.ts new file mode 100644 index 000000000000..345cf5d17b54 --- /dev/null +++ b/test/import-meta-resolve/vitest.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + poolOptions: { + threads: { + execArgv: ['--experimental-import-meta-resolve'], + }, + forks: { + execArgv: ['--experimental-import-meta-resolve'], + }, + vmThreads: { + // vmThreads already enables this flag + // execArgv: ['--experimental-import-meta-resolve'], + }, + }, + }, +}) diff --git a/test/vite-node/src/require-vite-node.cjs b/test/vite-node/src/require-vite-node.cjs new file mode 100644 index 000000000000..9462b5c4cf94 --- /dev/null +++ b/test/vite-node/src/require-vite-node.cjs @@ -0,0 +1,2 @@ +require('vite-node/client') +require('vite-node/server') diff --git a/test/vite-node/test/cjs.test.ts b/test/vite-node/test/cjs.test.ts new file mode 100644 index 000000000000..9212f5ae9a10 --- /dev/null +++ b/test/vite-node/test/cjs.test.ts @@ -0,0 +1,15 @@ +import { execFile } from 'node:child_process' +import { fileURLToPath } from 'node:url' +import { promisify } from 'node:util' +import { it } from 'vitest' + +const execFileAsync = promisify(execFile) + +it('require vite-node', async () => { + // verify `import.meta.resolve` usage doesn't cause syntax error on vite-node cjs build + // since rollup replaces it with `undefined` + await execFileAsync( + 'node', + [fileURLToPath(new URL('../src/require-vite-node.cjs', import.meta.url))], + ) +})