diff --git a/packages/vite-node/src/utils.ts b/packages/vite-node/src/utils.ts index 78acb5a4b887..1f2b76362cc3 100644 --- a/packages/vite-node/src/utils.ts +++ b/packages/vite-node/src/utils.ts @@ -6,6 +6,15 @@ import type { Arrayable, Nullable } from './types' export const isWindows = process.platform === 'win32' +const drive = isWindows ? process.cwd()[0] : null +const driveOpposite = drive + ? (drive === drive.toUpperCase() + ? drive.toLowerCase() + : drive.toUpperCase()) + : null +const driveRegexp = drive ? new RegExp(`(?:^|/@fs/)${drive}(\:[\\/])`) : null +const driveOppositeRegext = driveOpposite ? new RegExp(`(?:^|/@fs/)${driveOpposite}(\:[\\/])`) : null + export function slash(str: string) { return str.replace(/\\/g, '/') } @@ -16,6 +25,10 @@ export function normalizeRequestId(id: string, base?: string): string { if (base && id.startsWith(base)) id = `/${id.slice(base.length)}` + // keep drive the same as in process cwd + if (driveRegexp && !driveRegexp?.test(id) && driveOppositeRegext?.test(id)) + id = id.replace(driveOppositeRegext, `${drive}$1`) + return id .replace(/^\/@id\/__x00__/, '\0') // virtual modules start with `\0` .replace(/^\/@id\//, '') diff --git a/test/core/src/esm/esm.js b/test/core/src/esm/esm.js new file mode 100644 index 000000000000..ae67ddada465 --- /dev/null +++ b/test/core/src/esm/esm.js @@ -0,0 +1 @@ +export const test = 1 diff --git a/test/core/src/esm/package.json b/test/core/src/esm/package.json new file mode 100644 index 000000000000..3dbc1ca591c0 --- /dev/null +++ b/test/core/src/esm/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/test/core/test/imports.test.ts b/test/core/test/imports.test.ts index 75fc6ec8e984..a6d2970cec73 100644 --- a/test/core/test/imports.test.ts +++ b/test/core/test/imports.test.ts @@ -1,6 +1,7 @@ import { mkdir, writeFile } from 'node:fs/promises' +import { fileURLToPath } from 'node:url' import { resolve } from 'pathe' -import { describe, expect, test } from 'vitest' +import { describe, expect, test, vi } from 'vitest' import { dynamicRelativeImport } from '../src/relative-import' // @ts-expect-error module is not typed @@ -130,3 +131,34 @@ describe('importing special files from node_modules', async () => { expect(mod.default).toBe('/src/node_modules/file.mp3') }) }) + +describe.runIf(process.platform === 'win32')('importing files with different drive casing', async () => { + test('importing a local file with different drive casing works', async () => { + const path = new URL('./../src/timeout', import.meta.url) + const filepath = fileURLToPath(path) + const drive = filepath[0].toLowerCase() + const upperDrive = drive.toUpperCase() + const lowercasePath = filepath.replace(`${upperDrive}:`, `${drive}:`) + const uppercasePath = filepath.replace(`${drive}:`, `${upperDrive}:`) + expect(lowercasePath).not.toBe(uppercasePath) + const mod1 = await import(lowercasePath) + const mod2 = await import(uppercasePath) + const mod3 = await import('./../src/timeout') + expect(mod1).toBe(mod2) + expect(mod1).toBe(mod3) + }) + + test('importing an external file with different drive casing works', async () => { + const path = new URL('./../src/esm/esm.js', import.meta.url) + const filepath = fileURLToPath(path) + const drive = filepath[0].toLowerCase() + const upperDrive = drive.toUpperCase() + const lowercasePath = filepath.replace(`${upperDrive}:`, `${drive}:`) + const uppercasePath = filepath.replace(`${drive}:`, `${upperDrive}:`) + expect(lowercasePath).not.toBe(uppercasePath) + const mod1 = await import(lowercasePath) + vi.resetModules() // since they reference the same global ESM cache, it should not matter + const mod2 = await import(uppercasePath) + expect(mod1).toBe(mod2) + }) +}) diff --git a/test/core/vitest.config.ts b/test/core/vitest.config.ts index d2252dd7b2dc..837aed1e3085 100644 --- a/test/core/vitest.config.ts +++ b/test/core/vitest.config.ts @@ -66,7 +66,7 @@ export default defineConfig({ seed: 101, }, deps: { - external: ['tinyspy', /src\/external/], + external: ['tinyspy', /src\/external/, /esm\/esm/], inline: ['inline-lib'], moduleDirectories: ['node_modules', 'projects', 'packages'], },