Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(vite-node): normalize drive letter to keep the same reference to a module #3836

Merged
merged 2 commits into from Jul 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/vite-node/src/utils.ts
Expand Up @@ -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, '/')
}
Expand All @@ -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\//, '')
Expand Down
1 change: 1 addition & 0 deletions test/core/src/esm/esm.js
@@ -0,0 +1 @@
export const test = 1
3 changes: 3 additions & 0 deletions test/core/src/esm/package.json
@@ -0,0 +1,3 @@
{
"type": "module"
}
34 changes: 33 additions & 1 deletion 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
Expand Down Expand Up @@ -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)
})
})
2 changes: 1 addition & 1 deletion test/core/vitest.config.ts
Expand Up @@ -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'],
},
Expand Down