diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 0949e062d436..f0d845b53b33 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -59,6 +59,7 @@ export class ViteNodeRunner { const resolvedDep = await this.options.resolveId(dep, id) dep = resolvedDep?.id?.replace(this.root, '') || dep } + if (callstack.includes(dep)) { if (!this.moduleCache.get(dep)?.exports) throw new Error(`[vite-node] Circular dependency detected\nStack:\n${[...callstack, dep].reverse().map(p => `- ${p}`).join('\n')}`) diff --git a/packages/vite-node/src/server.ts b/packages/vite-node/src/server.ts index 667ddc881ddc..a4b9d917dcf1 100644 --- a/packages/vite-node/src/server.ts +++ b/packages/vite-node/src/server.ts @@ -1,3 +1,4 @@ +import { join } from 'pathe' import type { TransformResult, ViteDevServer } from 'vite' import type { FetchResult, RawSourceMap, ViteNodeResolveId, ViteNodeServerOptions } from './types' import { shouldExternalize } from './externalize' @@ -24,6 +25,8 @@ export class ViteNodeServer { } async resolveId(id: string, importer?: string): Promise { + if (importer && !importer.startsWith(this.server.config.root)) + importer = join(this.server.config.root, importer) return this.server.pluginContainer.resolveId(id, importer, { ssr: true }) } diff --git a/test/core/src/relative-import.ts b/test/core/src/relative-import.ts new file mode 100644 index 000000000000..53c7740d37c3 --- /dev/null +++ b/test/core/src/relative-import.ts @@ -0,0 +1,3 @@ +export function dynamicRelativeImport(file: string) { + return import(`./${file}.ts`) +} diff --git a/test/core/test/imports.test.ts b/test/core/test/imports.test.ts index c2aa7b3cb59f..51137f509197 100644 --- a/test/core/test/imports.test.ts +++ b/test/core/test/imports.test.ts @@ -1,4 +1,5 @@ import { expect, test } from 'vitest' +import { dynamicRelativeImport } from '../src/relative-import' test('dynamic relative import works', async() => { const stringTimeoutMod = await import('./../src/timeout') @@ -9,6 +10,13 @@ test('dynamic relative import works', async() => { expect(stringTimeoutMod).toBe(variableTimeoutMod) }) +test('Relative imports in imported modules work', async() => { + const relativeImportFromFile = await dynamicRelativeImport('timeout') + const directImport = await import('./../src/timeout') + + expect(relativeImportFromFile).toBe(directImport) +}) + test('dynamic aliased import works', async() => { const stringTimeoutMod = await import('./../src/timeout')