From 49c67d257303b03ea2cf74630646b32977a0f6d2 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 7 Jul 2022 16:39:20 +0800 Subject: [PATCH] fix(vite-node): self circular reference (#1609) --- examples/mocks/src/example.ts | 2 +- examples/mocks/test/automocking.spec.ts | 8 +-- packages/vite-node/src/client.ts | 18 +++-- packages/vite-node/src/hmr/hmr.ts | 6 +- packages/vite-node/src/utils.ts | 6 +- packages/vitest/src/runtime/mocker.ts | 17 ++--- packages/vitest/src/runtime/setup.ts | 6 +- packages/vitest/src/runtime/worker.ts | 6 +- packages/web-worker/src/pure.ts | 6 +- pnpm-lock.yaml | 91 +++++++++++++++++++------ test/vite-node/package.json | 1 + test/vite-node/src/circular/a.ts | 1 + test/vite-node/src/circular/b.ts | 3 + test/vite-node/src/circular/index.ts | 14 ++++ test/vite-node/test/circular.test.ts | 13 ++++ 15 files changed, 143 insertions(+), 55 deletions(-) create mode 100644 test/vite-node/src/circular/a.ts create mode 100644 test/vite-node/src/circular/b.ts create mode 100644 test/vite-node/src/circular/index.ts create mode 100644 test/vite-node/test/circular.test.ts diff --git a/examples/mocks/src/example.ts b/examples/mocks/src/example.ts index 0e55cec2ce1c..22f15d20f6a9 100644 --- a/examples/mocks/src/example.ts +++ b/examples/mocks/src/example.ts @@ -5,7 +5,7 @@ export async function asyncSquare(a: number, b: number) { const result = (await a) * b return result } -export const someClasss = new (class Bar { +export const someClasses = new (class Bar { public array: number[] constructor() { this.array = [1, 2, 3] diff --git a/examples/mocks/test/automocking.spec.ts b/examples/mocks/test/automocking.spec.ts index 69a7573293a5..eb9dba8979b1 100644 --- a/examples/mocks/test/automocking.spec.ts +++ b/examples/mocks/test/automocking.spec.ts @@ -17,10 +17,10 @@ test('all mocked are valid', async () => { expect(example.asyncSquare.length).toEqual(0) // creates a new class with the same interface, member functions and properties are mocked. - expect(example.someClasss.constructor.name).toEqual('Bar') - expect(example.someClasss.foo.name).toEqual('foo') - expect(vi.isMockFunction(example.someClasss.foo)).toBe(true) - expect(example.someClasss.array.length).toEqual(0) + expect(example.someClasses.constructor.name).toEqual('Bar') + expect(example.someClasses.foo.name).toEqual('foo') + expect(vi.isMockFunction(example.someClasses.foo)).toBe(true) + expect(example.someClasses.array.length).toEqual(0) // creates a deeply cloned version of the original object. expect(example.object).toEqual({ diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 6cd38a905409..514d4edc511d 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -82,6 +82,11 @@ export class ViteNodeRunner { const id = normalizeRequestId(rawId, this.options.base) const fsPath = toFilePath(id, this.root) + // the callstack reference itself circularly + if (callstack.includes(fsPath) && this.moduleCache.get(fsPath)?.exports) + return this.moduleCache.get(fsPath)?.exports + + // cached module if (this.moduleCache.get(fsPath)?.promise) return this.moduleCache.get(fsPath)?.promise @@ -93,20 +98,21 @@ export class ViteNodeRunner { /** @internal */ async directRequest(id: string, fsPath: string, _callstack: string[]) { - const callstack = [..._callstack, normalizeModuleId(id)] + const callstack = [..._callstack, fsPath] const request = async (dep: string) => { + const fsPath = toFilePath(normalizeRequestId(dep, this.options.base), this.root) const getStack = () => { - return `stack:\n${[...callstack, dep].reverse().map(p => `- ${p}`).join('\n')}` + return `stack:\n${[...callstack, fsPath].reverse().map(p => `- ${p}`).join('\n')}` } let debugTimer: any if (this.debug) - debugTimer = setTimeout(() => this.debugLog(() => `module ${dep} takes over 2s to load.\n${getStack()}`), 2000) + debugTimer = setTimeout(() => this.debugLog(() => `module ${fsPath} takes over 2s to load.\n${getStack()}`), 2000) try { - if (callstack.includes(normalizeModuleId(dep))) { + if (callstack.includes(fsPath)) { this.debugLog(() => `circular dependency, ${getStack()}`) - const depExports = this.moduleCache.get(dep)?.exports + const depExports = this.moduleCache.get(fsPath)?.exports if (depExports) return depExports throw new Error(`[vite-node] Failed to resolve circular dependency, ${getStack()}`) @@ -165,7 +171,7 @@ export class ViteNodeRunner { const exports: any = Object.create(null) exports[Symbol.toStringTag] = 'Module' - this.moduleCache.set(id, { code: transformed, exports }) + this.moduleCache.set(fsPath, { code: transformed, exports }) const __filename = fileURLToPath(url) const moduleProxy = { diff --git a/packages/vite-node/src/hmr/hmr.ts b/packages/vite-node/src/hmr/hmr.ts index 347206f0923e..0fe4b5f3e244 100644 --- a/packages/vite-node/src/hmr/hmr.ts +++ b/packages/vite-node/src/hmr/hmr.ts @@ -74,9 +74,9 @@ export function sendMessageBuffer(runner: ViteNodeRunner, emitter: HMREmitter) { export async function reload(runner: ViteNodeRunner, files: string[]) { // invalidate module cache but not node_modules Array.from(runner.moduleCache.keys()) - .forEach((i) => { - if (!i.includes('node_modules')) - runner.moduleCache.delete(i) + .forEach((fsPath) => { + if (!fsPath.includes('node_modules')) + runner.moduleCache.delete(fsPath) }) return Promise.all(files.map(file => runner.executeId(file))) diff --git a/packages/vite-node/src/utils.ts b/packages/vite-node/src/utils.ts index 6f188ab8ac60..b5c2d933d59e 100644 --- a/packages/vite-node/src/utils.ts +++ b/packages/vite-node/src/utils.ts @@ -46,12 +46,12 @@ export function isPrimitive(v: any) { } export function toFilePath(id: string, root: string): string { - let absolute = slash(id).startsWith('/@fs/') + let absolute = id.startsWith('/@fs/') ? id.slice(4) : id.startsWith(root) ? id : id.startsWith('/') - ? slash(resolve(root, id.slice(1))) + ? resolve(root, id.slice(1)) : id if (absolute.startsWith('//')) @@ -59,7 +59,7 @@ export function toFilePath(id: string, root: string): string { // disambiguate the `:/` on windows: see nodejs/node#31710 return isWindows && absolute.startsWith('/') - ? fileURLToPath(pathToFileURL(absolute.slice(1)).href) + ? slash(fileURLToPath(pathToFileURL(absolute.slice(1)).href)) : absolute } diff --git a/packages/vitest/src/runtime/mocker.ts b/packages/vitest/src/runtime/mocker.ts index 7a062a3b6414..f7a65f7589c2 100644 --- a/packages/vitest/src/runtime/mocker.ts +++ b/packages/vitest/src/runtime/mocker.ts @@ -1,6 +1,6 @@ import { existsSync, readdirSync } from 'fs' import { isNodeBuiltin } from 'mlly' -import { basename, dirname, resolve } from 'pathe' +import { basename, dirname, join, resolve } from 'pathe' import { normalizeRequestId, toFilePath } from 'vite-node/utils' import type { ModuleCacheMap } from 'vite-node/client' import { getAllProperties, getType, getWorkerState, isWindows, mergeSlashes, slash } from '../utils' @@ -106,7 +106,7 @@ export class VitestMocker { // all mocks should be inside /__mocks__ if (external || isNodeBuiltin(mockPath) || !existsSync(mockPath)) { const mockDirname = dirname(path) // for nested mocks: @vueuse/integration/useJwt - const mockFolder = resolve(this.root, '__mocks__', mockDirname) + const mockFolder = join(this.root, '__mocks__', mockDirname) if (!existsSync(mockFolder)) return null @@ -117,7 +117,7 @@ export class VitestMocker { for (const file of files) { const [basename] = file.split('.') if (basename === baseFilename) - return resolve(mockFolder, file).replace(this.root, '') + return resolve(mockFolder, file) } return null @@ -126,7 +126,7 @@ export class VitestMocker { const dir = dirname(path) const baseId = basename(path) const fullPath = resolve(dir, '__mocks__', baseId) - return existsSync(fullPath) ? fullPath.replace(this.root, '') : null + return existsSync(fullPath) ? fullPath : null } public mockValue(value: any) { @@ -196,19 +196,20 @@ export class VitestMocker { public async importMock(id: string, importer: string): Promise { const { path, external } = await this.resolvePath(id, importer) - let mock = this.getDependencyMock(path) + const fsPath = this.getFsPath(path, external) + let mock = this.getDependencyMock(fsPath) if (mock === undefined) - mock = this.resolveMockPath(path, external) + mock = this.resolveMockPath(fsPath, external) if (mock === null) { await this.ensureSpy() - const fsPath = this.getFsPath(path, external) const mod = await this.request(fsPath) return this.mockValue(mod) } + if (typeof mock === 'function') - return this.callFunctionMock(path, mock) + return this.callFunctionMock(fsPath, mock) return this.requestWithMock(mock) } diff --git a/packages/vitest/src/runtime/setup.ts b/packages/vitest/src/runtime/setup.ts index 8ee93f7d8479..73b3b2491e6a 100644 --- a/packages/vitest/src/runtime/setup.ts +++ b/packages/vitest/src/runtime/setup.ts @@ -165,9 +165,9 @@ export async function withEnv( export async function runSetupFiles(config: ResolvedConfig) { const files = toArray(config.setupFiles) await Promise.all( - files.map(async (file) => { - getWorkerState().moduleCache.delete(file) - await import(file) + files.map(async (fsPath) => { + getWorkerState().moduleCache.delete(fsPath) + await import(fsPath) }), ) } diff --git a/packages/vitest/src/runtime/worker.ts b/packages/vitest/src/runtime/worker.ts index 4d3c463b70a0..9f39a6eac242 100644 --- a/packages/vitest/src/runtime/worker.ts +++ b/packages/vitest/src/runtime/worker.ts @@ -86,9 +86,9 @@ function init(ctx: WorkerContext) { } if (ctx.invalidates) { - ctx.invalidates.forEach((i) => { - moduleCache.delete(i) - moduleCache.delete(`${i}__mock`) + ctx.invalidates.forEach((fsPath) => { + moduleCache.delete(fsPath) + moduleCache.delete(`${fsPath}__mock`) }) } ctx.files.forEach(i => moduleCache.delete(i)) diff --git a/packages/web-worker/src/pure.ts b/packages/web-worker/src/pure.ts index 10e04499a323..e7e6d6c06e9d 100644 --- a/packages/web-worker/src/pure.ts +++ b/packages/web-worker/src/pure.ts @@ -133,10 +133,10 @@ export function defineWebWorker() { runner.executeFile(fsPath) .then(() => { - invalidates.forEach((path) => { + invalidates.forEach((fsPath) => { // worker should be new every time - moduleCache.delete(path) - moduleCache.delete(`${path}__mock`) + moduleCache.delete(fsPath) + moduleCache.delete(`${fsPath}__mock`) }) const q = this.messageQueue this.messageQueue = null diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7786481dc6e0..589d57686663 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -163,7 +163,7 @@ importers: lit: 2.2.5 devDependencies: '@vitest/ui': link:../../packages/ui - happy-dom: 6.0.0 + happy-dom: 6.0.2 vite: 2.9.12 vitest: link:../../packages/vitest @@ -295,7 +295,7 @@ importers: dependencies: '@emotion/react': 11.9.0_3dj5wppwohj5ocihzt4m54mr2a '@emotion/styled': 11.8.1_3zgpe2oef7sbs566rsy6a7qm7i - '@mui/lab': 5.0.0-alpha.88_xjii3a7ufdwggkrf6imrcribfm + '@mui/lab': 5.0.0-alpha.89_xjii3a7ufdwggkrf6imrcribfm '@mui/material': 5.8.3_rikzftoujo3cmwul4lespwcm6i history: 5.3.0 notistack: 2.0.5_ozyq7dscrvuimqma7xstouyo4e @@ -908,9 +908,11 @@ importers: test/vite-node: specifiers: + execa: ^6.1.0 vite-node: workspace:* vitest: workspace:* devDependencies: + execa: 6.1.0 vite-node: link:../../packages/vite-node vitest: link:../../packages/vitest @@ -3132,6 +3134,16 @@ packages: stylis: 4.0.13 dev: false + /@emotion/cache/11.9.3: + resolution: {integrity: sha512-0dgkI/JKlCXa+lEXviaMtGBL0ynpx4osh7rjOXE71q9bIF8G+XhJgvi+wDu0B0IdCVx37BffiwXlN9I3UuzFvg==} + dependencies: + '@emotion/memoize': 0.7.5 + '@emotion/sheet': 1.1.1 + '@emotion/utils': 1.1.0 + '@emotion/weak-memoize': 0.2.5 + stylis: 4.0.13 + dev: false + /@emotion/core/10.3.1_react@17.0.2: resolution: {integrity: sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww==} peerDependencies: @@ -3169,6 +3181,12 @@ packages: '@emotion/memoize': 0.7.5 dev: false + /@emotion/is-prop-valid/1.1.3: + resolution: {integrity: sha512-RFg04p6C+1uO19uG8N+vqanzKqiM9eeV1LDOG3bmkYmuOj7NbKNlFC/4EZq5gnwAIlcC/jOT24f8Td0iax2SXA==} + dependencies: + '@emotion/memoize': 0.7.5 + dev: false + /@emotion/memoize/0.7.4: resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} dev: true @@ -3228,6 +3246,10 @@ packages: resolution: {integrity: sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==} dev: false + /@emotion/sheet/1.1.1: + resolution: {integrity: sha512-J3YPccVRMiTZxYAY0IOq3kd+hUP8idY8Kz6B/Cyo+JuXq52Ek+zbPbSQUrVQp95aJ+lsAW7DPL1P2Z+U1jGkKA==} + dev: false + /@emotion/styled-base/10.3.0_gfrer23gq2rp2t523t6qbxrx6m: resolution: {integrity: sha512-PBRqsVKR7QRNkmfH78hTSSwHWcwDpecH9W6heujWAcyp2wdz/64PP73s7fWS1dIPm8/Exc8JAzYS8dEWXjv60w==} peerDependencies: @@ -3770,8 +3792,8 @@ packages: react-is: 17.0.2 dev: false - /@mui/base/5.0.0-alpha.87_sfoxds7t5ydpegc3knd667wn6m: - resolution: {integrity: sha512-PuxRYrvG63Yj/UTwf4hSwZ5ClMv88iXHK+5hUV1CrG3kNPo6FFQiIFNRaNpRt/3nsXj6+xygJByNFA8m4Leetg==} + /@mui/base/5.0.0-alpha.88_sfoxds7t5ydpegc3knd667wn6m: + resolution: {integrity: sha512-uL7ej2F/3GUnZewsDQSHUVHoSBT3AQcTIdfdy6QeCHy7X26mtbcIvTRcjl2PzbbNQplppavSTibPiQG/giJ+ng==} engines: {node: '>=12.0.0'} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 @@ -3782,19 +3804,19 @@ packages: optional: true dependencies: '@babel/runtime': 7.17.9 - '@emotion/is-prop-valid': 1.1.2 + '@emotion/is-prop-valid': 1.1.3 '@mui/types': 7.1.4 '@mui/utils': 5.8.6_react@17.0.2 '@popperjs/core': 2.11.5 - clsx: 1.1.1 + clsx: 1.2.1 prop-types: 15.8.1 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 react-is: 17.0.2 dev: false - /@mui/lab/5.0.0-alpha.88_xjii3a7ufdwggkrf6imrcribfm: - resolution: {integrity: sha512-YS2NPw0D0CHG9z9Y6Wjocl3g2LNzdXdkvORPoyc05ea9Xm+m8buddvMeTuL/r/e3S7yLK8HOMN2uHE0rwD/oVQ==} + /@mui/lab/5.0.0-alpha.89_xjii3a7ufdwggkrf6imrcribfm: + resolution: {integrity: sha512-u5bMi/V+Utwouo9awVzGasj/LudlRqPFyMo2L5/y60uFo0EaG17bt1jh/U7smQCdjd+7tvJ39HNMkEmIoGr7BQ==} engines: {node: '>=12.0.0'} peerDependencies: '@emotion/react': ^11.5.0 @@ -3826,12 +3848,12 @@ packages: '@babel/runtime': 7.17.9 '@emotion/react': 11.9.0_3dj5wppwohj5ocihzt4m54mr2a '@emotion/styled': 11.8.1_3zgpe2oef7sbs566rsy6a7qm7i - '@mui/base': 5.0.0-alpha.87_sfoxds7t5ydpegc3knd667wn6m + '@mui/base': 5.0.0-alpha.88_sfoxds7t5ydpegc3knd667wn6m '@mui/material': 5.8.3_rikzftoujo3cmwul4lespwcm6i - '@mui/system': 5.8.6_bgqmsvm4hz6izcmpcwescmz73y + '@mui/system': 5.8.7_bgqmsvm4hz6izcmpcwescmz73y '@mui/utils': 5.8.6_react@17.0.2 - '@mui/x-date-pickers': 5.0.0-alpha.1_a27cws6df4ygyi4adk5k5wbyme - clsx: 1.1.1 + '@mui/x-date-pickers': 5.0.0-alpha.1_26mscri4leya655bwcveqdxkdi + clsx: 1.2.1 date-fns: 2.28.0 prop-types: 15.8.1 react: 17.0.2 @@ -3913,6 +3935,28 @@ packages: react: 17.0.2 dev: false + /@mui/styled-engine/5.8.7_bgqmsvm4hz6izcmpcwescmz73y: + resolution: {integrity: sha512-tVqtowjbYmiRq+qcqXK731L9eWoL9H8xTRhuTgaDGKdch1zlt4I2UwInUe1w2N9N/u3/jHsFbLcl1Un3uOwpQg==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + dependencies: + '@babel/runtime': 7.17.9 + '@emotion/cache': 11.9.3 + '@emotion/react': 11.9.0_3dj5wppwohj5ocihzt4m54mr2a + '@emotion/styled': 11.8.1_3zgpe2oef7sbs566rsy6a7qm7i + csstype: 3.1.0 + prop-types: 15.8.1 + react: 17.0.2 + dev: false + /@mui/system/5.8.3_bgqmsvm4hz6izcmpcwescmz73y: resolution: {integrity: sha512-/tyGQcYqZT0nl98qV9XnGiedTO+V7VHc28k4POfhMJNedB1CRrwWRm767DeEdc5f/8CU2See3WD16ikP6pYiOA==} engines: {node: '>=12.0.0'} @@ -3942,8 +3986,8 @@ packages: react: 17.0.2 dev: false - /@mui/system/5.8.6_bgqmsvm4hz6izcmpcwescmz73y: - resolution: {integrity: sha512-+a+rD58XltKQHDrrjcuCta2cUBqdnLDUDwnphSLCMFigRl8/uk+R+fdQRlMNRXAOgnMb8ioWIgfjxri5pmTH4A==} + /@mui/system/5.8.7_bgqmsvm4hz6izcmpcwescmz73y: + resolution: {integrity: sha512-yFoFbfO42FWeSUDrFPixYjpqySQMqVMOSbSlAxiKnwFpvXGGn/bkfQTboCRNO31fvES29FJLQd4mwwMHd5mXng==} engines: {node: '>=12.0.0'} peerDependencies: '@emotion/react': ^11.5.0 @@ -3962,10 +4006,10 @@ packages: '@emotion/react': 11.9.0_3dj5wppwohj5ocihzt4m54mr2a '@emotion/styled': 11.8.1_3zgpe2oef7sbs566rsy6a7qm7i '@mui/private-theming': 5.8.6_react@17.0.2 - '@mui/styled-engine': 5.8.0_bgqmsvm4hz6izcmpcwescmz73y + '@mui/styled-engine': 5.8.7_bgqmsvm4hz6izcmpcwescmz73y '@mui/types': 7.1.4 '@mui/utils': 5.8.6_react@17.0.2 - clsx: 1.1.1 + clsx: 1.2.1 csstype: 3.1.0 prop-types: 15.8.1 react: 17.0.2 @@ -4017,7 +4061,7 @@ packages: react-is: 17.0.2 dev: false - /@mui/x-date-pickers/5.0.0-alpha.1_a27cws6df4ygyi4adk5k5wbyme: + /@mui/x-date-pickers/5.0.0-alpha.1_26mscri4leya655bwcveqdxkdi: resolution: {integrity: sha512-dLPkRiIn2Gr0momblxiOnIwrxn4SijVix+8e08mwAGWhiWcmWep1O9XTRDpZsjB0kjHYCf+kZjlRX4dxnj2acg==} engines: {node: '>=12.0.0'} peerDependencies: @@ -4044,9 +4088,9 @@ packages: '@date-io/luxon': 2.13.1 '@date-io/moment': 2.13.1 '@mui/material': 5.8.3_rikzftoujo3cmwul4lespwcm6i - '@mui/system': 5.8.6_bgqmsvm4hz6izcmpcwescmz73y + '@mui/system': 5.8.7_bgqmsvm4hz6izcmpcwescmz73y '@mui/utils': 5.8.6_react@17.0.2 - clsx: 1.1.1 + clsx: 1.2.1 date-fns: 2.28.0 prop-types: 15.8.1 react: 17.0.2 @@ -9342,6 +9386,11 @@ packages: engines: {node: '>=6'} dev: false + /clsx/1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + dev: false + /co/4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -12840,8 +12889,8 @@ packages: - encoding dev: true - /happy-dom/6.0.0: - resolution: {integrity: sha512-Nk+hyCemZGIyQ0gDLyb6K1PFOHepfec101dG93z5g8+LIYSUZWT3FDzWUIUwfYkcAnaRCM9hMDgmR2pFLUYE9w==} + /happy-dom/6.0.2: + resolution: {integrity: sha512-NLPdk85xDRRYmOo3FjQ4KILNTH0geVofs1jW6x8IVO3PSl6bSLbB89DcLZGlkSYNLMJ8fggwd0v5v6gZidriXA==} dependencies: css.escape: 1.5.1 he: 1.2.0 diff --git a/test/vite-node/package.json b/test/vite-node/package.json index 847c24e5adb3..ba58348fe130 100644 --- a/test/vite-node/package.json +++ b/test/vite-node/package.json @@ -9,6 +9,7 @@ "debug": "node --inspect-brk ../../packages/vite-node/dist/cli.cjs" }, "devDependencies": { + "execa": "^6.1.0", "vite-node": "workspace:*", "vitest": "workspace:*" } diff --git a/test/vite-node/src/circular/a.ts b/test/vite-node/src/circular/a.ts new file mode 100644 index 000000000000..37a0d0b8155c --- /dev/null +++ b/test/vite-node/src/circular/a.ts @@ -0,0 +1 @@ +export const a = () => 'A' diff --git a/test/vite-node/src/circular/b.ts b/test/vite-node/src/circular/b.ts new file mode 100644 index 000000000000..bbad03ca9eca --- /dev/null +++ b/test/vite-node/src/circular/b.ts @@ -0,0 +1,3 @@ +import { foo } from '.' + +export const b = () => `B${foo()}` diff --git a/test/vite-node/src/circular/index.ts b/test/vite-node/src/circular/index.ts new file mode 100644 index 000000000000..39e0b8f12985 --- /dev/null +++ b/test/vite-node/src/circular/index.ts @@ -0,0 +1,14 @@ +import { a } from './a' +import { b } from './b' + +export const index = 'index' + +export function foo() { + return index +} + +export * from './a' +export * from './b' + +// eslint-disable-next-line no-console +console.log(a(), b(), index) diff --git a/test/vite-node/test/circular.test.ts b/test/vite-node/test/circular.test.ts new file mode 100644 index 000000000000..ed77866358f6 --- /dev/null +++ b/test/vite-node/test/circular.test.ts @@ -0,0 +1,13 @@ +import { describe, 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"') + }) +})