diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 9a32356b4207..a06653a2ba04 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -188,22 +188,31 @@ export class ViteNodeRunner { if (importee) mod.importers.add(importee) - // the callstack reference itself circularly - if (callstack.includes(fsPath) && mod.exports) - return mod.exports + const getStack = () => `stack:\n${[...callstack, fsPath].reverse().map(p => ` - ${p}`).join('\n')}` - // cached module - if (mod.promise) - return mod.promise + // check circular dependency + if (callstack.includes(fsPath) || callstack.some(c => this.moduleCache.get(c).importers?.has(fsPath))) { + if (mod.exports) + return mod.exports + } - const promise = this.directRequest(id, fsPath, callstack) - Object.assign(mod, { promise, evaluated: false }) + let debugTimer: any + if (this.debug) + debugTimer = setTimeout(() => console.warn(`[vite-node] module ${fsPath} takes over 2s to load.\n${getStack()}`), 2000) try { + // cached module + if (mod.promise) + return await mod.promise + + const promise = this.directRequest(id, fsPath, callstack) + Object.assign(mod, { promise, evaluated: false }) return await promise } finally { mod.evaluated = true + if (debugTimer) + clearTimeout(debugTimer) } } @@ -245,28 +254,7 @@ export class ViteNodeRunner { /** @internal */ async dependencyRequest(id: string, fsPath: string, callstack: string[]) { - const getStack = () => { - return `stack:\n${[...callstack, fsPath].reverse().map(p => `- ${p}`).join('\n')}` - } - - let debugTimer: any - if (this.debug) - debugTimer = setTimeout(() => console.warn(() => `module ${fsPath} takes over 2s to load.\n${getStack()}`), 2000) - - try { - if (callstack.includes(fsPath)) { - const depExports = this.moduleCache.get(fsPath)?.exports - if (depExports) - return depExports - throw new Error(`[vite-node] Failed to resolve circular dependency, ${getStack()}`) - } - - return await this.cachedRequest(id, fsPath, callstack) - } - finally { - if (debugTimer) - clearTimeout(debugTimer) - } + return await this.cachedRequest(id, fsPath, callstack) } /** @internal */ diff --git a/test/vite-node/src/circular/a.ts b/test/vite-node/src/circular1/a.ts similarity index 100% rename from test/vite-node/src/circular/a.ts rename to test/vite-node/src/circular1/a.ts diff --git a/test/vite-node/src/circular/b.ts b/test/vite-node/src/circular1/b.ts similarity index 100% rename from test/vite-node/src/circular/b.ts rename to test/vite-node/src/circular1/b.ts diff --git a/test/vite-node/src/circular/index.ts b/test/vite-node/src/circular1/index.ts similarity index 100% rename from test/vite-node/src/circular/index.ts rename to test/vite-node/src/circular1/index.ts diff --git a/test/vite-node/src/circular2/a.ts b/test/vite-node/src/circular2/a.ts new file mode 100644 index 000000000000..e4a043d476fc --- /dev/null +++ b/test/vite-node/src/circular2/a.ts @@ -0,0 +1,5 @@ +import { c } from './reg' + +await new Promise(resolve => setTimeout(resolve, 10)) + +export const a = `a${c}` diff --git a/test/vite-node/src/circular2/b.ts b/test/vite-node/src/circular2/b.ts new file mode 100644 index 000000000000..6240612cc34a --- /dev/null +++ b/test/vite-node/src/circular2/b.ts @@ -0,0 +1,6 @@ +// eslint-disable-next-line unused-imports/no-unused-imports +import { a } from './a' + +await new Promise(resolve => setTimeout(resolve, 10)) + +export const b = 'b' diff --git a/test/vite-node/src/circular2/c.ts b/test/vite-node/src/circular2/c.ts new file mode 100644 index 000000000000..ccd54c49e462 --- /dev/null +++ b/test/vite-node/src/circular2/c.ts @@ -0,0 +1,3 @@ +export const c = 'c' + +await new Promise(resolve => setTimeout(resolve, 10)) diff --git a/test/vite-node/src/circular2/index.ts b/test/vite-node/src/circular2/index.ts new file mode 100644 index 000000000000..7953b422551d --- /dev/null +++ b/test/vite-node/src/circular2/index.ts @@ -0,0 +1,11 @@ +/* eslint-disable no-console */ +import { a } from './a' +import { b } from './b' + +/** + * index -> a -> b + * ^ ^v + * reg -> c + */ + +console.log(a, b) diff --git a/test/vite-node/src/circular2/reg.ts b/test/vite-node/src/circular2/reg.ts new file mode 100644 index 000000000000..3ad0133f6f04 --- /dev/null +++ b/test/vite-node/src/circular2/reg.ts @@ -0,0 +1,6 @@ +export { a } from './a' + +await new Promise(resolve => setTimeout(resolve, 20)) + +export { b } from './b' +export { c } from './c' diff --git a/test/vite-node/test/circular.test.ts b/test/vite-node/test/circular.test.ts index 7374e8e28416..297c7dd93cda 100644 --- a/test/vite-node/test/circular.test.ts +++ b/test/vite-node/test/circular.test.ts @@ -1,13 +1,17 @@ -import { describe, expect, test } from 'vitest' +import { expect, test } from 'vitest' import { execa } from 'execa' import { resolve } from 'pathe' const cliPath = resolve(__dirname, '../../../packages/vite-node/src/cli.ts') -const entryPath = resolve(__dirname, '../src/circular/index.ts') -describe('circular', async () => { - test('should works', async () => { - const result = await execa('npx', ['esno', cliPath, entryPath], { reject: true }) - expect(result.stdout).toMatchInlineSnapshot('"A Bindex index"') - }, 60_000) -}) +test('circular 1', async () => { + const entryPath = resolve(__dirname, '../src/circular1/index.ts') + const result = await execa('npx', ['esno', cliPath, entryPath], { reject: true }) + expect(result.stdout).toMatchInlineSnapshot('"A Bindex index"') +}, 60_000) + +test('circular 2', async () => { + const entryPath = resolve(__dirname, '../src/circular2/index.ts') + const result = await execa('npx', ['esno', cliPath, entryPath], { reject: true }) + expect(result.stdout).toMatchInlineSnapshot('"ac b"') +}, 60_000)