diff --git a/packages/vite/src/node/__tests__/utils.spec.ts b/packages/vite/src/node/__tests__/utils.spec.ts index ac10229fc1b2e5..29a5defac4750b 100644 --- a/packages/vite/src/node/__tests__/utils.spec.ts +++ b/packages/vite/src/node/__tests__/utils.spec.ts @@ -113,6 +113,12 @@ describe('injectQuery', () => { '/usr/vite/東京 %20 hello?direct', ) }) + + test('path with url-encoded path as query parameter', () => { + const src = '/src/module.ts?url=https%3A%2F%2Fusr.vite%2F' + const expected = '/src/module.ts?t=1234&url=https%3A%2F%2Fusr.vite%2F' + expect(injectQuery(src, 't=1234')).toEqual(expected) + }) }) describe('resolveHostname', () => { diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 254b52b468e4fa..efe90935dbba68 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -19,7 +19,13 @@ import type MagicString from 'magic-string' import type { TransformResult } from 'rollup' import { createFilter as _createFilter } from '@rollup/pluginutils' -import { cleanUrl, isWindows, slash, withTrailingSlash } from '../shared/utils' +import { + cleanUrl, + isWindows, + slash, + splitFileAndPostfix, + withTrailingSlash, +} from '../shared/utils' import { VALID_ID_PREFIX } from '../shared/constants' import { CLIENT_ENTRY, @@ -311,20 +317,10 @@ export function removeRawQuery(url: string): string { return url.replace(rawRE, '$1').replace(trailingSeparatorRE, '') } -const replacePercentageRE = /%/g export function injectQuery(url: string, queryToInject: string): string { - // encode percents for consistent behavior with pathToFileURL - // see #2614 for details - const resolvedUrl = new URL( - url.replace(replacePercentageRE, '%25'), - 'relative:///', - ) - const { search, hash } = resolvedUrl - let pathname = cleanUrl(url) - pathname = isWindows ? slash(pathname) : pathname - return `${pathname}?${queryToInject}${search ? `&` + search.slice(1) : ''}${ - hash ?? '' - }` + const { file, postfix } = splitFileAndPostfix(url) + const normalizedFile = isWindows ? slash(file) : file + return `${normalizedFile}?${queryToInject}${postfix[0] === '?' ? `&${postfix.slice(1)}` : /* hash only */ postfix}` } const timestampRE = /\bt=\d{13}&?\b/