Skip to content

Commit cbb593a

Browse files
authoredApr 15, 2023
fix(vite-node): circular imports (#3196)
1 parent ac04717 commit cbb593a

File tree

10 files changed

+61
-38
lines changed

10 files changed

+61
-38
lines changed
 

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

+18-30
Original file line numberDiff line numberDiff line change
@@ -188,22 +188,31 @@ export class ViteNodeRunner {
188188
if (importee)
189189
mod.importers.add(importee)
190190

191-
// the callstack reference itself circularly
192-
if (callstack.includes(fsPath) && mod.exports)
193-
return mod.exports
191+
const getStack = () => `stack:\n${[...callstack, fsPath].reverse().map(p => ` - ${p}`).join('\n')}`
194192

195-
// cached module
196-
if (mod.promise)
197-
return mod.promise
193+
// check circular dependency
194+
if (callstack.includes(fsPath) || callstack.some(c => this.moduleCache.get(c).importers?.has(fsPath))) {
195+
if (mod.exports)
196+
return mod.exports
197+
}
198198

199-
const promise = this.directRequest(id, fsPath, callstack)
200-
Object.assign(mod, { promise, evaluated: false })
199+
let debugTimer: any
200+
if (this.debug)
201+
debugTimer = setTimeout(() => console.warn(`[vite-node] module ${fsPath} takes over 2s to load.\n${getStack()}`), 2000)
201202

202203
try {
204+
// cached module
205+
if (mod.promise)
206+
return await mod.promise
207+
208+
const promise = this.directRequest(id, fsPath, callstack)
209+
Object.assign(mod, { promise, evaluated: false })
203210
return await promise
204211
}
205212
finally {
206213
mod.evaluated = true
214+
if (debugTimer)
215+
clearTimeout(debugTimer)
207216
}
208217
}
209218

@@ -245,28 +254,7 @@ export class ViteNodeRunner {
245254

246255
/** @internal */
247256
async dependencyRequest(id: string, fsPath: string, callstack: string[]) {
248-
const getStack = () => {
249-
return `stack:\n${[...callstack, fsPath].reverse().map(p => `- ${p}`).join('\n')}`
250-
}
251-
252-
let debugTimer: any
253-
if (this.debug)
254-
debugTimer = setTimeout(() => console.warn(() => `module ${fsPath} takes over 2s to load.\n${getStack()}`), 2000)
255-
256-
try {
257-
if (callstack.includes(fsPath)) {
258-
const depExports = this.moduleCache.get(fsPath)?.exports
259-
if (depExports)
260-
return depExports
261-
throw new Error(`[vite-node] Failed to resolve circular dependency, ${getStack()}`)
262-
}
263-
264-
return await this.cachedRequest(id, fsPath, callstack)
265-
}
266-
finally {
267-
if (debugTimer)
268-
clearTimeout(debugTimer)
269-
}
257+
return await this.cachedRequest(id, fsPath, callstack)
270258
}
271259

272260
/** @internal */
File renamed without changes.
File renamed without changes.
File renamed without changes.

‎test/vite-node/src/circular2/a.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { c } from './reg'
2+
3+
await new Promise(resolve => setTimeout(resolve, 10))
4+
5+
export const a = `a${c}`

‎test/vite-node/src/circular2/b.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// eslint-disable-next-line unused-imports/no-unused-imports
2+
import { a } from './a'
3+
4+
await new Promise(resolve => setTimeout(resolve, 10))
5+
6+
export const b = 'b'

‎test/vite-node/src/circular2/c.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const c = 'c'
2+
3+
await new Promise(resolve => setTimeout(resolve, 10))

‎test/vite-node/src/circular2/index.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* eslint-disable no-console */
2+
import { a } from './a'
3+
import { b } from './b'
4+
5+
/**
6+
* index -> a -> b
7+
* ^ ^v
8+
* reg -> c
9+
*/
10+
11+
console.log(a, b)

‎test/vite-node/src/circular2/reg.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export { a } from './a'
2+
3+
await new Promise(resolve => setTimeout(resolve, 20))
4+
5+
export { b } from './b'
6+
export { c } from './c'

‎test/vite-node/test/circular.test.ts

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
import { describe, expect, test } from 'vitest'
1+
import { expect, test } from 'vitest'
22
import { execa } from 'execa'
33
import { resolve } from 'pathe'
44

55
const cliPath = resolve(__dirname, '../../../packages/vite-node/src/cli.ts')
6-
const entryPath = resolve(__dirname, '../src/circular/index.ts')
76

8-
describe('circular', async () => {
9-
test('should works', async () => {
10-
const result = await execa('npx', ['esno', cliPath, entryPath], { reject: true })
11-
expect(result.stdout).toMatchInlineSnapshot('"A Bindex index"')
12-
}, 60_000)
13-
})
7+
test('circular 1', async () => {
8+
const entryPath = resolve(__dirname, '../src/circular1/index.ts')
9+
const result = await execa('npx', ['esno', cliPath, entryPath], { reject: true })
10+
expect(result.stdout).toMatchInlineSnapshot('"A Bindex index"')
11+
}, 60_000)
12+
13+
test('circular 2', async () => {
14+
const entryPath = resolve(__dirname, '../src/circular2/index.ts')
15+
const result = await execa('npx', ['esno', cliPath, entryPath], { reject: true })
16+
expect(result.stdout).toMatchInlineSnapshot('"ac b"')
17+
}, 60_000)

0 commit comments

Comments
 (0)
Please sign in to comment.