From 703aab46553b0d5e82e38c183e9a4dee5ec12795 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Sat, 21 Jan 2023 14:24:01 +0100 Subject: [PATCH] fix: correctly resolve paths relative to root, when used outside of root directory (#2687) Fixes https://github.com/vitest-dev/vitest/issues/2686 --- packages/vite-node/src/client.ts | 11 +++++++++-- packages/vitest/src/runtime/execute.ts | 2 +- packages/vitest/src/runtime/loader.ts | 4 ++-- test/core/test/imports.test.ts | 7 +++++++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index de78b7d6a012..e5d5f935bb97 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -207,12 +207,19 @@ export class ViteNodeRunner { } private async _resolveUrl(id: string, importee?: string): Promise<[url: string, fsPath: string]> { - if (!this.shouldResolveId(id)) - return [id, id] // we don't pass down importee here, because otherwise Vite doesn't resolve it correctly + // should be checked before normalization, because it removes this prefix if (importee && id.startsWith(VALID_ID_PREFIX)) importee = undefined id = normalizeRequestId(id, this.options.base) + // should be checked after normalization + // provide importer only for relative and absolute paths + // paths like "src/user" are valid for transformRequest, but they will be resolved incorrectly, + // if importer is provided - because they will be treated as relative to the importer instead of root + if (!id.startsWith('/') && !id.startsWith('./') && !id.startsWith('../')) + importee = undefined + if (!this.shouldResolveId(id)) + return [id, id] const { path, exists } = toFilePath(id, this.root) if (!this.options.resolveId || exists) return [id, path] diff --git a/packages/vitest/src/runtime/execute.ts b/packages/vitest/src/runtime/execute.ts index e624057ee83a..de3c5bffe4b3 100644 --- a/packages/vitest/src/runtime/execute.ts +++ b/packages/vitest/src/runtime/execute.ts @@ -39,7 +39,7 @@ export class VitestRunner extends ViteNodeRunner { const environment = getCurrentEnvironment() // do not try and resolve node builtins in Node // import('url') returns Node internal even if 'url' package is installed - return environment === 'node' ? !isNodeBuiltin(id) : true + return environment === 'node' ? !isNodeBuiltin(id) : !id.startsWith('node:') } async resolveUrl(id: string, importee?: string) { diff --git a/packages/vitest/src/runtime/loader.ts b/packages/vitest/src/runtime/loader.ts index 00f7f2a3bc34..1191a3ce42d1 100644 --- a/packages/vitest/src/runtime/loader.ts +++ b/packages/vitest/src/runtime/loader.ts @@ -1,4 +1,4 @@ -import { pathToFileURL } from 'node:url' +import { fileURLToPath, pathToFileURL } from 'node:url' import { readFile } from 'node:fs/promises' import { hasCJSSyntax, isNodeBuiltin } from 'mlly' import { normalizeModuleId } from 'vite-node/utils' @@ -64,7 +64,7 @@ export const resolve: Resolver = async (url, context, next) => { } else { const { url: resolvedUrl, format } = await next(url, context, next) - filepath = new URL(resolvedUrl).pathname + filepath = fileURLToPath(resolvedUrl) result = { url: resolvedUrl, format, diff --git a/test/core/test/imports.test.ts b/test/core/test/imports.test.ts index 874f236b0c30..2fe0b6215d1d 100644 --- a/test/core/test/imports.test.ts +++ b/test/core/test/imports.test.ts @@ -46,6 +46,13 @@ test('dynamic absolute with extension mport works', async () => { expect(stringTimeoutMod).toBe(variableTimeoutMod) }) +test('dynamic baseUrl import works', async () => { + const staticMod = await import('./../src/timeout') + // @ts-expect-error there is no tsconfig in test/core to handle baseUrl + const dynamicMod = await import('src/timeout') + expect(staticMod).toBe(dynamicMod) +}) + test('data with dynamic import works', async () => { const dataUri = 'data:text/javascript;charset=utf-8,export default "hi"' const { default: hi } = await import(dataUri)