From bc1f20ea6e26e9e75c27cc273f16848b79ba3c6e Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 8 Sep 2026 14:29:27 +0200 Subject: [PATCH] fix: accidental cache if length is of specific length --- .../runtime/moduleRunner/cachedResolver.ts | 10 ++- test/unit/test/cached-resolver.test.ts | 63 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 test/unit/test/cached-resolver.test.ts diff --git a/packages/vitest/src/runtime/moduleRunner/cachedResolver.ts b/packages/vitest/src/runtime/moduleRunner/cachedResolver.ts index 78212bd19c67..95bae4295233 100644 --- a/packages/vitest/src/runtime/moduleRunner/cachedResolver.ts +++ b/packages/vitest/src/runtime/moduleRunner/cachedResolver.ts @@ -9,6 +9,14 @@ const normalizedDistDir = normalize(distDir) const relativeIds: Record = {} const externalizeMap = new Map() +function getRelativeDistDir(root: string): string { + const normalizedRoot = normalize(root).replace(/\/+$/, '') || '/' + const rootPrefix = normalizedRoot === '/' ? normalizedRoot : `${normalizedRoot}/` + return normalizedDistDir.startsWith(rootPrefix) + ? normalizedDistDir.slice(normalizedRoot.length) + : '' +} + // all Vitest imports always need to be externalized export function getCachedVitestImport( id: string, @@ -24,7 +32,7 @@ export function getCachedVitestImport( // always externalize Vitest because we import from there before running tests // so we already have it cached by Node.js const root = state().config.root - const relativeRoot = relativeIds[root] ?? (relativeIds[root] = normalizedDistDir.slice(root.length)) + const relativeRoot = relativeIds[root] ?? (relativeIds[root] = getRelativeDistDir(root)) if (id.includes(distDir) || id.includes(normalizedDistDir)) { const { file, postfix } = splitFileAndPostfix(id) const externalize = id.startsWith('file://') diff --git a/test/unit/test/cached-resolver.test.ts b/test/unit/test/cached-resolver.test.ts new file mode 100644 index 000000000000..ed2c445598bc --- /dev/null +++ b/test/unit/test/cached-resolver.test.ts @@ -0,0 +1,63 @@ +import type { WorkerGlobalState } from '../../../packages/vitest/src/types/worker' +import { pathToFileURL } from 'node:url' +import { dirname, normalize } from 'pathe' +import { describe, expect, test } from 'vitest' +import { distDir } from '../../../packages/vitest/src/paths' +import { getCachedVitestImport } from '../../../packages/vitest/src/runtime/moduleRunner/cachedResolver' + +const normalizedDistDir = normalize(distDir) + +function stateWithRoot(root: string): () => WorkerGlobalState { + return () => ({ config: { root } }) as WorkerGlobalState +} + +function unrelatedRootForSuffix(suffix: string, fill: string): string { + return `/${fill.repeat(normalizedDistDir.length - suffix.length - 1)}` +} + +describe('getCachedVitestImport', () => { + test('does not resolve a bare builtin relative to an unrelated root', () => { + const root = unrelatedRootForSuffix('t', 'a') + expect(normalizedDistDir.slice(root.length)).toBe('t') + + expect(getCachedVitestImport('timers', stateWithRoot(root))).toBeNull() + }) + + test('does not resolve a builtin subpath relative to an unrelated root', () => { + const root = unrelatedRootForSuffix('st', 'b') + expect(normalizedDistDir.slice(root.length)).toBe('st') + + expect(getCachedVitestImport('stream/promises', stateWithRoot(root))).toBeNull() + }) + + test('does not derive a relative path from a sibling root prefix', () => { + const root = normalizedDistDir.slice(0, -'st/dist'.length) + expect(normalizedDistDir.slice(root.length)).toBe('st/dist') + + expect(getCachedVitestImport('st/dist/index.js', stateWithRoot(root))).toBeNull() + }) + + test('resolves a dist path relative to an ancestor root', () => { + const root = dirname(normalizedDistDir) + const id = `${normalizedDistDir.slice(root.length)}/index.js?relative` + + expect(getCachedVitestImport(id, stateWithRoot(root))).toEqual({ + externalize: `${pathToFileURL(`${normalizedDistDir}/index.js`)}?relative`, + type: 'module', + }) + }) + + test('still resolves absolute and bare Vitest imports', () => { + const root = unrelatedRootForSuffix('test', 'c') + const id = `${normalizedDistDir}/index.js?absolute` + + expect(getCachedVitestImport(id, stateWithRoot(root))).toEqual({ + externalize: `${pathToFileURL(`${normalizedDistDir}/index.js`)}?absolute`, + type: 'module', + }) + expect(getCachedVitestImport('vitest/config', stateWithRoot(root))).toEqual({ + externalize: 'vitest/config', + type: 'module', + }) + }) +})