From e37f4deb4f1753ea3d5740793455174176fe971d Mon Sep 17 00:00:00 2001 From: Vladimir Date: Mon, 21 Feb 2022 08:19:23 +0300 Subject: [PATCH] fix: resolve import path for dynamic imports (#817) --- packages/vite-node/src/cli.ts | 3 ++ packages/vite-node/src/client.ts | 16 ++++++++++- packages/vite-node/src/types.ts | 3 ++ packages/vitest/src/node/execute.ts | 5 +--- .../vitest/src/node/plugins/globalSetup.ts | 3 ++ test/core/test/imports.test.ts | 28 +++++++++++++++++++ test/core/vitest.config.ts | 6 ++++ 7 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 test/core/test/imports.test.ts diff --git a/packages/vite-node/src/cli.ts b/packages/vite-node/src/cli.ts index aec50fc02884..88668100baf3 100644 --- a/packages/vite-node/src/cli.ts +++ b/packages/vite-node/src/cli.ts @@ -82,6 +82,9 @@ async function run(options: CliOptions = {}) { fetchModule(id) { return node.fetchModule(id) }, + resolveId(id, importer) { + return node.resolveId(id, importer) + }, }) // provide the vite define variable in this context diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 43065be542ac..3ed8e6f54ab8 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -1,7 +1,8 @@ import { createRequire } from 'module' import { fileURLToPath, pathToFileURL } from 'url' import vm from 'vm' -import { dirname, resolve } from 'pathe' +import { dirname, isAbsolute, resolve } from 'pathe' +import { isNodeBuiltin } from 'mlly' import { isPrimitive, normalizeId, slash, toFilePath } from './utils' import type { ModuleCache, ViteNodeRunnerOptions } from './types' @@ -52,6 +53,12 @@ export class ViteNodeRunner { async directRequest(id: string, fsPath: string, callstack: string[]) { callstack = [...callstack, id] const request = async(dep: string) => { + // probably means it was passed as variable + // and wasn't transformed by Vite + if (this.shouldResolveId(dep)) { + const resolvedDep = await this.options.resolveId(dep, id) + dep = resolvedDep?.id || dep + } if (callstack.includes(dep)) { if (!this.moduleCache.get(dep)?.exports) throw new Error(`[vite-node] Circular dependency detected\nStack:\n${[...callstack, dep].reverse().map(p => `- ${p}`).join('\n')}`) @@ -132,6 +139,13 @@ export class ViteNodeRunner { Object.assign(this.moduleCache.get(id), mod) } + shouldResolveId(dep: string) { + if (isNodeBuiltin(dep)) + return false + + return !isAbsolute(dep) + } + /** * Define if a module should be interop-ed * This function mostly for the ability to override by subclass diff --git a/packages/vite-node/src/types.ts b/packages/vite-node/src/types.ts index cdfa237bf948..24fc2b21901c 100644 --- a/packages/vite-node/src/types.ts +++ b/packages/vite-node/src/types.ts @@ -29,6 +29,8 @@ export interface FetchResult { export type FetchFunction = (id: string) => Promise +export type ResolveIdFunction = (id: string, importer?: string) => Promise + export interface ModuleCache { promise?: Promise exports?: any @@ -37,6 +39,7 @@ export interface ModuleCache { export interface ViteNodeRunnerOptions { fetchModule: FetchFunction + resolveId: ResolveIdFunction root: string base?: string moduleCache?: Map diff --git a/packages/vitest/src/node/execute.ts b/packages/vitest/src/node/execute.ts index 4108b0c88604..89b3ec2c3506 100644 --- a/packages/vitest/src/node/execute.ts +++ b/packages/vitest/src/node/execute.ts @@ -1,14 +1,11 @@ import { ViteNodeRunner } from 'vite-node/client' -import type { ModuleCache, ViteNodeResolveId, ViteNodeRunnerOptions } from 'vite-node' +import type { ModuleCache, ViteNodeRunnerOptions } from 'vite-node' import type { SuiteMocks } from './mocker' import { VitestMocker } from './mocker' -export type ResolveIdFunction = (id: string, importer?: string) => Promise - export interface ExecuteOptions extends ViteNodeRunnerOptions { files: string[] mockMap: SuiteMocks - resolveId: ResolveIdFunction } export async function executeInViteNode(options: ExecuteOptions) { diff --git a/packages/vitest/src/node/plugins/globalSetup.ts b/packages/vitest/src/node/plugins/globalSetup.ts index d9bfafcf85dc..3ad6ac033d19 100644 --- a/packages/vitest/src/node/plugins/globalSetup.ts +++ b/packages/vitest/src/node/plugins/globalSetup.ts @@ -18,6 +18,9 @@ async function loadGlobalSetupFiles(ctx: Vitest): Promise { fetchModule(id) { return node.fetchModule(id) }, + resolveId(id, importer) { + return node.resolveId(id, importer) + }, }) const globalSetupFiles = toArray(server.config.test?.globalSetup) return Promise.all(globalSetupFiles.map(file => loadGlobalSetupFile(file, runner))) diff --git a/test/core/test/imports.test.ts b/test/core/test/imports.test.ts new file mode 100644 index 000000000000..6820922cac98 --- /dev/null +++ b/test/core/test/imports.test.ts @@ -0,0 +1,28 @@ +import { expect, test } from 'vitest' + +test('dynamic relative import works', async() => { + const { timeout } = await import('./../src/timeout') + + const timeoutPath = './../src/timeout' + const { timeout: dynamicTimeout } = await import(timeoutPath) + + expect(timeout).toBe(dynamicTimeout) +}) + +test('dynamic aliased import works', async() => { + const { timeout } = await import('./../src/timeout') + + const timeoutPath = '@/timeout' + const { timeout: dynamicTimeout } = await import(timeoutPath) + + expect(timeout).toBe(dynamicTimeout) +}) + +test('dynamic absolute import works', async() => { + const { timeout } = await import('./../src/timeout') + + const timeoutPath = '/src/timeout' + const { timeout: dynamicTimeout } = await import(timeoutPath) + + expect(timeout).toBe(dynamicTimeout) +}) diff --git a/test/core/vitest.config.ts b/test/core/vitest.config.ts index 61dd6529fa35..eef048ef29fd 100644 --- a/test/core/vitest.config.ts +++ b/test/core/vitest.config.ts @@ -1,3 +1,4 @@ +import { resolve } from 'pathe' import { defineConfig } from 'vite' export default defineConfig({ @@ -30,6 +31,11 @@ export default defineConfig({ 'SOME.VARIABLE': '"variable"', 'SOME.SOME.VARIABLE': '"nested variable"', }, + resolve: { + alias: [ + { find: '@', replacement: resolve(__dirname, 'src') }, + ], + }, test: { testTimeout: 2000, // threads: false,