From 6c9685750636cb391b84855971fd311096a5a35f Mon Sep 17 00:00:00 2001 From: Dunqing Date: Wed, 31 May 2023 23:02:32 +0800 Subject: [PATCH 1/9] Revert "revert: "fix(vite-node): circular import stuck" #3418 (#3479)" This reverts commit 03d34249acbf0a94d07d5c8c9c781a19ebac7258. --- examples/mocks/src/main.js | 5 +++++ examples/mocks/test/circular.spec.ts | 11 ++++++++++- packages/vite-node/src/client.ts | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/examples/mocks/src/main.js b/examples/mocks/src/main.js index be77a608ffc2..5401d983c887 100644 --- a/examples/mocks/src/main.js +++ b/examples/mocks/src/main.js @@ -1,5 +1,10 @@ import { funcA } from './A' +import { funcB } from './B' export function main() { return funcA() } + +export function mainB() { + return funcB() +} diff --git a/examples/mocks/test/circular.spec.ts b/examples/mocks/test/circular.spec.ts index 3c34b0916105..e8f4651dfef9 100644 --- a/examples/mocks/test/circular.spec.ts +++ b/examples/mocks/test/circular.spec.ts @@ -1,5 +1,5 @@ import { expect, test, vi } from 'vitest' -import { main } from '../src/main.js' +import { main, mainB } from '../src/main.js' import x from '../src/export-default-circle-index' vi.mock('../src/A', async () => ({ @@ -7,12 +7,21 @@ vi.mock('../src/A', async () => ({ funcA: () => 'mockedA', })) +vi.mock('../src/B', async () => ({ + ...(await vi.importActual('../src/B')), + funcB: () => 'mockedB', +})) + vi.mock('../src/export-default-circle-b') test('can import actual inside mock factory', () => { expect(main()).toBe('mockedA') }) +test('can import in top level and inside mock factory', () => { + expect(mainB()).toBe('mockedB') +}) + test('can mock a circular dependency', () => { expect(x()).toBe(undefined) }) diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 3f2fbc139791..8a9065620e53 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -194,7 +194,7 @@ export class ViteNodeRunner { const getStack = () => `stack:\n${[...callstack, fsPath].reverse().map(p => ` - ${p}`).join('\n')}` // check circular dependency - if (callstack.includes(fsPath) || callstack.some(c => this.moduleCache.get(c).importers?.has(fsPath))) { + if (callstack.includes(fsPath) || callstack.some(c => this.moduleCache.get(c).importers?.has(fsPath)) || mod.importers.has(importee)) { if (mod.exports) return mod.exports } From 9616e32f1bb86b2927eb4f994c6d2fcb8d3bc56b Mon Sep 17 00:00:00 2001 From: Dunqing Date: Wed, 31 May 2023 23:03:30 +0800 Subject: [PATCH 2/9] fix(vite-node): circular import stuck --- packages/vite-node/src/client.ts | 14 ++++++++++---- packages/vite-node/src/types.ts | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 8a9065620e53..78a1b79e19cf 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -186,15 +186,14 @@ export class ViteNodeRunner { const mod = this.moduleCache.get(fsPath) - if (!mod.importers) - mod.importers = new Set() + mod.importers ??= new Set() if (importee) mod.importers.add(importee) const getStack = () => `stack:\n${[...callstack, fsPath].reverse().map(p => ` - ${p}`).join('\n')}` // check circular dependency - if (callstack.includes(fsPath) || callstack.some(c => this.moduleCache.get(c).importers?.has(fsPath)) || mod.importers.has(importee)) { + if (callstack.includes(fsPath) || callstack.some(c => this.moduleCache.get(c).importers?.has(fsPath))) { if (mod.exports) return mod.exports } @@ -205,8 +204,11 @@ export class ViteNodeRunner { try { // cached module - if (mod.promise) + if (mod.promise) { + if (Array.from(mod.imports?.values() || []).some(i => mod.importers!.has(i))) + return mod.exports return await mod.promise + } const promise = this.directRequest(id, fsPath, callstack) Object.assign(mod, { promise, evaluated: false }) @@ -269,6 +271,10 @@ export class ViteNodeRunner { const request = async (dep: string) => { const [id, depFsPath] = await this.resolveUrl(`${dep}`, fsPath) + const depMod = this.moduleCache.getByModuleId(depFsPath) + ;(depMod.importers ??= new Set()).add(moduleId) + ;(mod.imports ??= new Set()).add(depFsPath) + return this.dependencyRequest(id, depFsPath, callstack) } diff --git a/packages/vite-node/src/types.ts b/packages/vite-node/src/types.ts index 077f974ec0a0..18d6016a5377 100644 --- a/packages/vite-node/src/types.ts +++ b/packages/vite-node/src/types.ts @@ -66,6 +66,7 @@ export interface ModuleCache { * Module ids that imports this module */ importers?: Set + imports?: Set } export interface ViteNodeRunnerOptions { From e7f6d96c1ae53909794294048c04e7135b320713 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Thu, 1 Jun 2023 00:12:42 +0800 Subject: [PATCH 3/9] feat: new Set automatically when set module --- packages/vite-node/src/client.ts | 23 +++++++++++------------ packages/vite-node/src/types.ts | 4 ++-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 78a1b79e19cf..1d8fc10daa65 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -61,23 +61,23 @@ export class ModuleCacheMap extends Map { update(fsPath: string, mod: Partial) { fsPath = this.normalizePath(fsPath) if (!super.has(fsPath)) - super.set(fsPath, mod) + this.setByModuleId(fsPath, mod) else Object.assign(super.get(fsPath) as ModuleCache, mod) return this } - setByModuleId(modulePath: string, mod: ModuleCache) { - return super.set(modulePath, mod) + setByModuleId(modulePath: string, mod: Partial) { + return super.set(modulePath, { imports: new Set(), importers: new Set(), ...mod }) } - set(fsPath: string, mod: ModuleCache) { + set(fsPath: string, mod: Partial) { return this.setByModuleId(this.normalizePath(fsPath), mod) } getByModuleId(modulePath: string): ModuleCache { if (!super.has(modulePath)) - super.set(modulePath, {}) + this.setByModuleId(modulePath, {}) return super.get(modulePath)! } @@ -98,7 +98,7 @@ export class ModuleCacheMap extends Map { delete mod.resolving delete mod.promise delete mod.exports - mod.importers?.clear() + mod.importers.clear() return true } @@ -129,7 +129,7 @@ export class ModuleCacheMap extends Map { continue invalidated.add(id) const subIds = Array.from(super.entries()) - .filter(([,mod]) => mod.importers?.has(id)) + .filter(([,mod]) => mod.importers.has(id)) .map(([key]) => key) subIds.length && this.invalidateSubDepTree(subIds, invalidated) super.delete(id) @@ -186,14 +186,13 @@ export class ViteNodeRunner { const mod = this.moduleCache.get(fsPath) - mod.importers ??= new Set() if (importee) mod.importers.add(importee) const getStack = () => `stack:\n${[...callstack, fsPath].reverse().map(p => ` - ${p}`).join('\n')}` // check circular dependency - if (callstack.includes(fsPath) || callstack.some(c => this.moduleCache.get(c).importers?.has(fsPath))) { + if (callstack.includes(fsPath) || callstack.some(c => this.moduleCache.get(c).importers.has(fsPath))) { if (mod.exports) return mod.exports } @@ -205,7 +204,7 @@ export class ViteNodeRunner { try { // cached module if (mod.promise) { - if (Array.from(mod.imports?.values() || []).some(i => mod.importers!.has(i))) + if (Array.from(mod.imports.values()).some(i => mod.importers!.has(i))) return mod.exports return await mod.promise } @@ -272,8 +271,8 @@ export class ViteNodeRunner { const request = async (dep: string) => { const [id, depFsPath] = await this.resolveUrl(`${dep}`, fsPath) const depMod = this.moduleCache.getByModuleId(depFsPath) - ;(depMod.importers ??= new Set()).add(moduleId) - ;(mod.imports ??= new Set()).add(depFsPath) + depMod.importers.add(moduleId) + mod.imports.add(depFsPath) return this.dependencyRequest(id, depFsPath, callstack) } diff --git a/packages/vite-node/src/types.ts b/packages/vite-node/src/types.ts index 18d6016a5377..b9bf479319b6 100644 --- a/packages/vite-node/src/types.ts +++ b/packages/vite-node/src/types.ts @@ -65,8 +65,8 @@ export interface ModuleCache { /** * Module ids that imports this module */ - importers?: Set - imports?: Set + importers: Set + imports: Set } export interface ViteNodeRunnerOptions { From 90ef4c4c0cb35e43533e65ffa5d7b8e40ab77cf1 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Thu, 1 Jun 2023 00:34:03 +0800 Subject: [PATCH 4/9] feat: simply check circular --- packages/vite-node/src/client.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 1d8fc10daa65..c87fb48edd10 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -185,14 +185,15 @@ export class ViteNodeRunner { const importee = callstack[callstack.length - 1] const mod = this.moduleCache.get(fsPath) + const { imports, importers } = mod if (importee) - mod.importers.add(importee) + importers.add(importee) const getStack = () => `stack:\n${[...callstack, fsPath].reverse().map(p => ` - ${p}`).join('\n')}` // check circular dependency - if (callstack.includes(fsPath) || callstack.some(c => this.moduleCache.get(c).importers.has(fsPath))) { + if (callstack.includes(fsPath) || Array.from(imports.values()).some(i => importers.has(i))) { if (mod.exports) return mod.exports } @@ -203,11 +204,8 @@ export class ViteNodeRunner { try { // cached module - if (mod.promise) { - if (Array.from(mod.imports.values()).some(i => mod.importers!.has(i))) - return mod.exports + if (mod.promise) return await mod.promise - } const promise = this.directRequest(id, fsPath, callstack) Object.assign(mod, { promise, evaluated: false }) From 3ce86a4f22611f2eda6233000342ffef99b83d75 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Thu, 1 Jun 2023 01:35:24 +0800 Subject: [PATCH 5/9] test: fix ci From 0acdd555b6857b7a3ac1cac680c5d6089d401d79 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Thu, 1 Jun 2023 17:01:53 +0800 Subject: [PATCH 6/9] fix: should clear imports --- packages/vite-node/src/client.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index c87fb48edd10..d8093f9e212b 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -99,6 +99,7 @@ export class ModuleCacheMap extends Map { delete mod.promise delete mod.exports mod.importers.clear() + mod.imports.clear() return true } From 61d341446ba0c65972bf38db7d11f9621ca0dbc4 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Thu, 1 Jun 2023 17:13:53 +0800 Subject: [PATCH 7/9] feat: only exists in get --- packages/vite-node/src/client.ts | 28 ++++++++++++++++++---------- packages/vite-node/src/types.ts | 4 ++-- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index d8093f9e212b..a38bda894dc9 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -58,7 +58,7 @@ export class ModuleCacheMap extends Map { /** * Assign partial data to the map */ - update(fsPath: string, mod: Partial) { + update(fsPath: string, mod: ModuleCache) { fsPath = this.normalizePath(fsPath) if (!super.has(fsPath)) this.setByModuleId(fsPath, mod) @@ -67,18 +67,26 @@ export class ModuleCacheMap extends Map { return this } - setByModuleId(modulePath: string, mod: Partial) { - return super.set(modulePath, { imports: new Set(), importers: new Set(), ...mod }) + setByModuleId(modulePath: string, mod: ModuleCache) { + return super.set(modulePath, mod) } - set(fsPath: string, mod: Partial) { + set(fsPath: string, mod: ModuleCache) { return this.setByModuleId(this.normalizePath(fsPath), mod) } - getByModuleId(modulePath: string): ModuleCache { + getByModuleId(modulePath: string) { if (!super.has(modulePath)) this.setByModuleId(modulePath, {}) - return super.get(modulePath)! + + const mod = super.get(modulePath)! + if (!mod.imports) { + Object.assign(mod, { + imports: new Set(), + importers: new Set(), + }) + } + return mod as ModuleCache & Required> } get(fsPath: string): ModuleCache { @@ -98,8 +106,8 @@ export class ModuleCacheMap extends Map { delete mod.resolving delete mod.promise delete mod.exports - mod.importers.clear() - mod.imports.clear() + mod.importers?.clear() + mod.imports?.clear() return true } @@ -130,7 +138,7 @@ export class ModuleCacheMap extends Map { continue invalidated.add(id) const subIds = Array.from(super.entries()) - .filter(([,mod]) => mod.importers.has(id)) + .filter(([,mod]) => mod.importers?.has(id)) .map(([key]) => key) subIds.length && this.invalidateSubDepTree(subIds, invalidated) super.delete(id) @@ -185,7 +193,7 @@ export class ViteNodeRunner { async cachedRequest(id: string, fsPath: string, callstack: string[]) { const importee = callstack[callstack.length - 1] - const mod = this.moduleCache.get(fsPath) + const mod = this.moduleCache.getByModuleId(fsPath) const { imports, importers } = mod if (importee) diff --git a/packages/vite-node/src/types.ts b/packages/vite-node/src/types.ts index b9bf479319b6..18d6016a5377 100644 --- a/packages/vite-node/src/types.ts +++ b/packages/vite-node/src/types.ts @@ -65,8 +65,8 @@ export interface ModuleCache { /** * Module ids that imports this module */ - importers: Set - imports: Set + importers?: Set + imports?: Set } export interface ViteNodeRunnerOptions { From a5f728213cb6362a670eb18d31dd8d4a423b862e Mon Sep 17 00:00:00 2001 From: Dunqing Date: Thu, 1 Jun 2023 21:59:38 +0800 Subject: [PATCH 8/9] ci: empty From 1df33719989ad02b7a0fa0526d9d770094f8a2a6 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Thu, 1 Jun 2023 23:21:28 +0800 Subject: [PATCH 9/9] fix: get failed --- packages/vite-node/src/client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index a38bda894dc9..c6ba7ebc0da7 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -89,7 +89,7 @@ export class ModuleCacheMap extends Map { return mod as ModuleCache & Required> } - get(fsPath: string): ModuleCache { + get(fsPath: string) { return this.getByModuleId(this.normalizePath(fsPath)) } @@ -193,7 +193,7 @@ export class ViteNodeRunner { async cachedRequest(id: string, fsPath: string, callstack: string[]) { const importee = callstack[callstack.length - 1] - const mod = this.moduleCache.getByModuleId(fsPath) + const mod = this.moduleCache.get(fsPath) const { imports, importers } = mod if (importee)