Skip to content

Commit

Permalink
fix: resolve import path for dynamic imports (#817)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va committed Feb 21, 2022
1 parent d03bc30 commit e37f4de
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 5 deletions.
3 changes: 3 additions & 0 deletions packages/vite-node/src/cli.ts
Expand Up @@ -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
Expand Down
16 changes: 15 additions & 1 deletion 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'

Expand Down Expand Up @@ -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')}`)
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions packages/vite-node/src/types.ts
Expand Up @@ -29,6 +29,8 @@ export interface FetchResult {

export type FetchFunction = (id: string) => Promise<FetchResult>

export type ResolveIdFunction = (id: string, importer?: string) => Promise<ViteNodeResolveId | null>

export interface ModuleCache {
promise?: Promise<any>
exports?: any
Expand All @@ -37,6 +39,7 @@ export interface ModuleCache {

export interface ViteNodeRunnerOptions {
fetchModule: FetchFunction
resolveId: ResolveIdFunction
root: string
base?: string
moduleCache?: Map<string, ModuleCache>
Expand Down
5 changes: 1 addition & 4 deletions 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<ViteNodeResolveId | null>

export interface ExecuteOptions extends ViteNodeRunnerOptions {
files: string[]
mockMap: SuiteMocks
resolveId: ResolveIdFunction
}

export async function executeInViteNode(options: ExecuteOptions) {
Expand Down
3 changes: 3 additions & 0 deletions packages/vitest/src/node/plugins/globalSetup.ts
Expand Up @@ -18,6 +18,9 @@ async function loadGlobalSetupFiles(ctx: Vitest): Promise<GlobalSetupFile[]> {
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)))
Expand Down
28 changes: 28 additions & 0 deletions 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)
})
6 changes: 6 additions & 0 deletions test/core/vitest.config.ts
@@ -1,3 +1,4 @@
import { resolve } from 'pathe'
import { defineConfig } from 'vite'

export default defineConfig({
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit e37f4de

Please sign in to comment.