Skip to content

Commit 50f0700

Browse files
authoredJun 6, 2023
fix(vite-node): circular import stuck (#3480)
1 parent 7c687ad commit 50f0700

File tree

4 files changed

+38
-11
lines changed

4 files changed

+38
-11
lines changed
 

‎examples/mocks/src/main.js

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { funcA } from './A'
2+
import { funcB } from './B'
23

34
export function main() {
45
return funcA()
56
}
7+
8+
export function mainB() {
9+
return funcB()
10+
}

‎examples/mocks/test/circular.spec.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
import { expect, test, vi } from 'vitest'
2-
import { main } from '../src/main.js'
2+
import { main, mainB } from '../src/main.js'
33
import x from '../src/export-default-circle-index'
44

55
vi.mock('../src/A', async () => ({
66
...(await vi.importActual<any>('../src/A')),
77
funcA: () => 'mockedA',
88
}))
99

10+
vi.mock('../src/B', async () => ({
11+
...(await vi.importActual<any>('../src/B')),
12+
funcB: () => 'mockedB',
13+
}))
14+
1015
vi.mock('../src/export-default-circle-b')
1116

1217
test('can import actual inside mock factory', () => {
1318
expect(main()).toBe('mockedA')
1419
})
1520

21+
test('can import in top level and inside mock factory', () => {
22+
expect(mainB()).toBe('mockedB')
23+
})
24+
1625
test('can mock a circular dependency', () => {
1726
expect(x()).toBe(undefined)
1827
})

‎packages/vite-node/src/client.ts

+22-10
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ export class ModuleCacheMap extends Map<string, ModuleCache> {
5858
/**
5959
* Assign partial data to the map
6060
*/
61-
update(fsPath: string, mod: Partial<ModuleCache>) {
61+
update(fsPath: string, mod: ModuleCache) {
6262
fsPath = this.normalizePath(fsPath)
6363
if (!super.has(fsPath))
64-
super.set(fsPath, mod)
64+
this.setByModuleId(fsPath, mod)
6565
else
6666
Object.assign(super.get(fsPath) as ModuleCache, mod)
6767
return this
@@ -75,13 +75,21 @@ export class ModuleCacheMap extends Map<string, ModuleCache> {
7575
return this.setByModuleId(this.normalizePath(fsPath), mod)
7676
}
7777

78-
getByModuleId(modulePath: string): ModuleCache {
78+
getByModuleId(modulePath: string) {
7979
if (!super.has(modulePath))
80-
super.set(modulePath, {})
81-
return super.get(modulePath)!
80+
this.setByModuleId(modulePath, {})
81+
82+
const mod = super.get(modulePath)!
83+
if (!mod.imports) {
84+
Object.assign(mod, {
85+
imports: new Set(),
86+
importers: new Set(),
87+
})
88+
}
89+
return mod as ModuleCache & Required<Pick<ModuleCache, 'imports' | 'importers'>>
8290
}
8391

84-
get(fsPath: string): ModuleCache {
92+
get(fsPath: string) {
8593
return this.getByModuleId(this.normalizePath(fsPath))
8694
}
8795

@@ -99,6 +107,7 @@ export class ModuleCacheMap extends Map<string, ModuleCache> {
99107
delete mod.promise
100108
delete mod.exports
101109
mod.importers?.clear()
110+
mod.imports?.clear()
102111
return true
103112
}
104113

@@ -185,16 +194,15 @@ export class ViteNodeRunner {
185194
const importee = callstack[callstack.length - 1]
186195

187196
const mod = this.moduleCache.get(fsPath)
197+
const { imports, importers } = mod
188198

189-
if (!mod.importers)
190-
mod.importers = new Set()
191199
if (importee)
192-
mod.importers.add(importee)
200+
importers.add(importee)
193201

194202
const getStack = () => `stack:\n${[...callstack, fsPath].reverse().map(p => ` - ${p}`).join('\n')}`
195203

196204
// check circular dependency
197-
if (callstack.includes(fsPath) || callstack.some(c => this.moduleCache.get(c).importers?.has(fsPath))) {
205+
if (callstack.includes(fsPath) || Array.from(imports.values()).some(i => importers.has(i))) {
198206
if (mod.exports)
199207
return mod.exports
200208
}
@@ -269,6 +277,10 @@ export class ViteNodeRunner {
269277

270278
const request = async (dep: string) => {
271279
const [id, depFsPath] = await this.resolveUrl(`${dep}`, fsPath)
280+
const depMod = this.moduleCache.getByModuleId(depFsPath)
281+
depMod.importers.add(moduleId)
282+
mod.imports.add(depFsPath)
283+
272284
return this.dependencyRequest(id, depFsPath, callstack)
273285
}
274286

‎packages/vite-node/src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export interface ModuleCache {
6666
* Module ids that imports this module
6767
*/
6868
importers?: Set<string>
69+
imports?: Set<string>
6970
}
7071

7172
export interface ViteNodeRunnerOptions {

0 commit comments

Comments
 (0)
Please sign in to comment.