mirror of
https://github.com/vitest-dev/vitest.git
synced 2026-02-01 17:36:51 +00:00
feat: resolve circular dependencies
This commit is contained in:
parent
576bd8ac3a
commit
0bc512400b
@ -7,21 +7,25 @@ import c from 'picocolors'
|
||||
|
||||
const { red, dim, yellow } = c
|
||||
|
||||
export interface ModuleCache {
|
||||
promise?: Promise<any>
|
||||
exports?: any
|
||||
transformResult?: TransformResult
|
||||
}
|
||||
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface Process {
|
||||
__vite_node__: {
|
||||
server: ViteDevServer
|
||||
watch?: boolean
|
||||
moduleCache: Map<string, Promise<any>>
|
||||
modulesTransformResult: Map<string, TransformResult>
|
||||
moduleCache: Map<string, ModuleCache>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const moduleCache = new Map<string, Promise<any>>()
|
||||
const modulesTransformResult = new Map<string, TransformResult>()
|
||||
const moduleCache = new Map<string, ModuleCache>()
|
||||
|
||||
export interface ViteNodeOptions {
|
||||
silent?: boolean
|
||||
@ -55,7 +59,6 @@ export async function run(argv: ViteNodeOptions) {
|
||||
process.__vite_node__ = {
|
||||
server,
|
||||
moduleCache,
|
||||
modulesTransformResult,
|
||||
}
|
||||
|
||||
try {
|
||||
@ -126,6 +129,13 @@ async function transform(server: ViteDevServer, id: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function setCache(id: string, mod: Partial<ModuleCache>) {
|
||||
if (!moduleCache.has(id))
|
||||
moduleCache.set(id, mod)
|
||||
else
|
||||
Object.assign(moduleCache.get(id), mod)
|
||||
}
|
||||
|
||||
async function execute(files: string[], server: ViteDevServer, options: ViteNodeOptions) {
|
||||
const result = []
|
||||
for (const file of files)
|
||||
@ -136,10 +146,13 @@ async function execute(files: string[], server: ViteDevServer, options: ViteNode
|
||||
callstack = [...callstack, id]
|
||||
const request = async(dep: string) => {
|
||||
if (callstack.includes(dep)) {
|
||||
throw new Error(`${red('Circular dependency detected')}\nStack:\n${[...callstack, dep].reverse().map((i) => {
|
||||
const path = relative(server.config.root, toFilePath(normalizeId(i), server))
|
||||
return dim(' -> ') + (i === dep ? yellow(path) : path)
|
||||
}).join('\n')}\n`)
|
||||
if (!moduleCache.get(dep)) {
|
||||
throw new Error(`${red('Circular dependency detected')}\nStack:\n${[...callstack, dep].reverse().map((i) => {
|
||||
const path = relative(server.config.root, toFilePath(normalizeId(i), server))
|
||||
return dim(' -> ') + (i === dep ? yellow(path) : path)
|
||||
}).join('\n')}\n`)
|
||||
}
|
||||
return moduleCache.get(dep)!.exports
|
||||
}
|
||||
return cachedRequest(dep, callstack)
|
||||
}
|
||||
@ -151,11 +164,11 @@ async function execute(files: string[], server: ViteDevServer, options: ViteNode
|
||||
if (!result)
|
||||
throw new Error(`failed to load ${id}`)
|
||||
|
||||
modulesTransformResult.set(id, result)
|
||||
|
||||
const url = pathToFileURL(fsPath)
|
||||
const exports = {}
|
||||
|
||||
setCache(id, { transformResult: result, exports })
|
||||
|
||||
const context = {
|
||||
require: createRequire(url),
|
||||
__filename: fsPath,
|
||||
@ -187,10 +200,11 @@ async function execute(files: string[], server: ViteDevServer, options: ViteNode
|
||||
if (options.shouldExternalize!(fsPath, server))
|
||||
return import(fsPath)
|
||||
|
||||
if (moduleCache.has(fsPath))
|
||||
return moduleCache.get(fsPath)
|
||||
moduleCache.set(fsPath, directRequest(id, fsPath, callstack))
|
||||
return await moduleCache.get(fsPath)
|
||||
if (moduleCache.get(fsPath)?.promise)
|
||||
return moduleCache.get(fsPath)?.promise
|
||||
const promise = directRequest(id, fsPath, callstack)
|
||||
setCache(fsPath, { promise })
|
||||
return await promise
|
||||
}
|
||||
|
||||
function exportAll(exports: any, sourceModule: any) {
|
||||
|
||||
@ -18,15 +18,15 @@ export async function printError(error: unknown) {
|
||||
return
|
||||
}
|
||||
|
||||
const { modulesTransformResult } = process.__vite_node__
|
||||
const { moduleCache } = process.__vite_node__
|
||||
|
||||
const e = error as ErrorWithDiff
|
||||
|
||||
let codeFramePrinted = false
|
||||
const stacks = parseStack(e.stack || '')
|
||||
const nearest = stacks.find(stack => modulesTransformResult.has(stack.file))
|
||||
const nearest = stacks.find(stack => moduleCache.has(stack.file))
|
||||
if (nearest) {
|
||||
const transformResult = modulesTransformResult.get(nearest.file)
|
||||
const transformResult = moduleCache.get(nearest.file)?.transformResult
|
||||
const pos = await getOriginalPos(transformResult?.map, nearest)
|
||||
if (pos && existsSync(nearest.file)) {
|
||||
const sourceCode = await fs.readFile(nearest.file, 'utf-8')
|
||||
|
||||
7
test/core/src/circularA.ts
Normal file
7
test/core/src/circularA.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { circularB } from './circularB'
|
||||
|
||||
export const CalledB: number[] = []
|
||||
|
||||
export function circularA() {
|
||||
return circularB()
|
||||
}
|
||||
5
test/core/src/circularB.ts
Normal file
5
test/core/src/circularB.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { CalledB } from './circularA'
|
||||
|
||||
export function circularB() {
|
||||
return CalledB.push(CalledB.length)
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import { expect, test, assert, suite } from 'vitest'
|
||||
import { two } from '../test/submodule'
|
||||
import { two } from '../src/submodule'
|
||||
|
||||
test('Math.sqrt()', () => {
|
||||
assert.equal(Math.sqrt(4), two)
|
||||
|
||||
15
test/core/test/cicular.test.ts
Normal file
15
test/core/test/cicular.test.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { test, expect } from 'vitest'
|
||||
import { CalledB, circularA } from '../src/circularA'
|
||||
|
||||
test('circular', () => {
|
||||
CalledB.length = 0
|
||||
|
||||
circularA()
|
||||
|
||||
expect(CalledB.length).toBe(1)
|
||||
|
||||
circularA()
|
||||
circularA()
|
||||
|
||||
expect(CalledB).toEqual([0, 1, 2])
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user