From 4fb9148da4909c1e641bb73a9f91dcbe56ca9bf7 Mon Sep 17 00:00:00 2001 From: patak Date: Tue, 21 Mar 2023 21:02:11 +0100 Subject: [PATCH 1/3] perf: more regex improvements --- packages/vite/scripts/util.ts | 3 +- .../src/node/optimizer/esbuildDepPlugin.ts | 4 +- packages/vite/src/node/optimizer/index.ts | 6 ++- packages/vite/src/node/plugins/asset.ts | 13 +++-- .../vite/src/node/plugins/clientInjections.ts | 5 +- packages/vite/src/node/plugins/define.ts | 8 +-- packages/vite/src/node/plugins/html.ts | 3 +- .../server/__tests__/pluginContainer.spec.ts | 2 +- packages/vite/src/node/server/hmr.ts | 4 +- .../src/node/server/middlewares/static.ts | 7 ++- packages/vite/src/node/utils.ts | 51 +++++++++++++------ 11 files changed, 71 insertions(+), 35 deletions(-) diff --git a/packages/vite/scripts/util.ts b/packages/vite/scripts/util.ts index 135c701098fc57..c742b3338e8717 100644 --- a/packages/vite/scripts/util.ts +++ b/packages/vite/scripts/util.ts @@ -17,8 +17,9 @@ export function rewriteImports( }) } +const windowsSlashRE = /\\/g export function slash(p: string): string { - return p.replace(/\\/g, '/') + return p.replace(windowsSlashRE, '/') } export function walkDir(dir: string, handleFile: (file: string) => void): void { diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index a01f48eb5a556b..c7a749411e95ee 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -4,6 +4,7 @@ import { CSS_LANGS_RE, KNOWN_ASSET_TYPES } from '../constants' import { getDepOptimizationConfig } from '..' import type { PackageCache, ResolvedConfig } from '..' import { + escapeForRegex, flattenId, isBuiltin, isExternalUrl, @@ -290,8 +291,7 @@ export function esbuildCjsExternalPlugin( return { name: 'cjs-external', setup(build) { - const escape = (text: string) => - `^${text.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')}$` + const escape = (text: string) => `^${escapeForRegex(text)}$` const filter = new RegExp(externals.map(escape).join('|')) build.onResolve({ filter: new RegExp(`^${nonFacadePrefix}`) }, (args) => { diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 04ba88cb38eb08..fcd7167cae5069 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -16,6 +16,7 @@ import { emptyDir, flattenId, getHash, + initialSlashRemoved, isOptimizable, lookupFile, normalizeId, @@ -41,6 +42,7 @@ const isDebugEnabled = _debug('vite:deps').enabled const jsExtensionRE = /\.js$/i const jsMapExtensionRE = /\.js\.map$/i +const reExportRE = /export\s+\*\s+from/ export type ExportsData = { hasImports: boolean @@ -956,7 +958,7 @@ export function createIsOptimizedDepUrl( const depsCacheDirPrefix = depsCacheDirRelative.startsWith('../') ? // if the cache directory is outside root, the url prefix would be something // like '/@fs/absolute/path/to/node_modules/.vite' - `/@fs/${normalizePath(depsCacheDir).replace(/^\//, '')}` + `/@fs/${initialSlashRemoved(normalizePath(depsCacheDir))}` : // if the cache directory is inside root, the url prefix would be something // like '/node_modules/.vite' `/${depsCacheDirRelative}` @@ -1140,7 +1142,7 @@ export async function extractExportsData( facade, hasReExports: imports.some(({ ss, se }) => { const exp = entryContent.slice(ss, se) - return /export\s+\*\s+from/.test(exp) + return reExportRE.test(exp) }), jsxLoader: usedJsxLoader, } diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 134625c87dd078..51e9f11ea7d621 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -16,7 +16,13 @@ import { } from '../build' import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' -import { cleanUrl, getHash, joinUrlSegments, normalizePath } from '../utils' +import { + cleanUrl, + getHash, + initialSlashRemoved, + joinUrlSegments, + normalizePath, +} from '../utils' import { FS_PREFIX } from '../constants' export const assetUrlRE = /__VITE_ASSET__([a-z\d]+)__(?:\$_(.*?)__)?/g @@ -24,6 +30,7 @@ export const assetUrlRE = /__VITE_ASSET__([a-z\d]+)__(?:\$_(.*?)__)?/g const rawRE = /(?:\?|&)raw(?:&|$)/ const urlRE = /(\?|&)url(?:&|$)/ const jsSourceMapRE = /\.[cm]?js\.map$/ +const unnededFinalQueryCharRE = /[?&]$/ const assetCache = new WeakMap>() @@ -167,7 +174,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin { return } - id = id.replace(urlRE, '$1').replace(/[?&]$/, '') + id = id.replace(urlRE, '$1').replace(unnededFinalQueryCharRE, '') const url = await fileToUrl(id, config, this) return `export default ${JSON.stringify(url)}` }, @@ -253,7 +260,7 @@ function fileToDevUrl(id: string, config: ResolvedConfig) { rtn = path.posix.join(FS_PREFIX, id) } const base = joinUrlSegments(config.server?.origin ?? '', config.base) - return joinUrlSegments(base, rtn.replace(/^\//, '')) + return joinUrlSegments(base, initialSlashRemoved(rtn)) } export function getPublicAssetFilename( diff --git a/packages/vite/src/node/plugins/clientInjections.ts b/packages/vite/src/node/plugins/clientInjections.ts index dfd3b878b6f459..5ac79c8d14ef8e 100644 --- a/packages/vite/src/node/plugins/clientInjections.ts +++ b/packages/vite/src/node/plugins/clientInjections.ts @@ -4,6 +4,9 @@ import type { ResolvedConfig } from '../config' import { CLIENT_ENTRY, ENV_ENTRY } from '../constants' import { isObject, normalizePath, resolveHostname } from '../utils' +const process_env_NODE_ENV_RE = + /(\bglobal(This)?\.)?\bprocess\.env\.NODE_ENV\b/g + // ids in transform are normalized to unix style const normalizedClientEntry = normalizePath(CLIENT_ENTRY) const normalizedEnvEntry = normalizePath(ENV_ENTRY) @@ -86,7 +89,7 @@ export function clientInjectionsPlugin(config: ResolvedConfig): Plugin { // for it to avoid shimming a `process` object during dev, // avoiding inconsistencies between dev and build return code.replace( - /(\bglobal(This)?\.)?\bprocess\.env\.NODE_ENV\b/g, + process_env_NODE_ENV_RE, config.define?.['process.env.NODE_ENV'] || JSON.stringify(process.env.NODE_ENV || config.mode), ) diff --git a/packages/vite/src/node/plugins/define.ts b/packages/vite/src/node/plugins/define.ts index f47a3ff97fed64..79827170029bb0 100644 --- a/packages/vite/src/node/plugins/define.ts +++ b/packages/vite/src/node/plugins/define.ts @@ -1,7 +1,7 @@ import MagicString from 'magic-string' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' -import { transformStableResult } from '../utils' +import { escapeForRegex, transformStableResult } from '../utils' import { isCSSRequest } from './css' import { isHTMLRequest } from './html' @@ -113,11 +113,7 @@ export function definePlugin(config: ResolvedConfig): Plugin { // Mustn't be preceded by a char that can be part of an identifier // or a '.' that isn't part of a spread operator '(? { - return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&') - }) - .join('|') + + replacementsKeys.map(escapeForRegex).join('|') + // Mustn't be followed by a char that can be part of an identifier // or an assignment (but allow equality operators) ')(?:(?<=\\.)|(?![\\p{L}\\p{N}_$]|\\s*?=[^=]))', diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 9737dcb3a9e9b7..759bc1e93892c9 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -16,6 +16,7 @@ import { cleanUrl, generateCodeFrame, getHash, + initialSlashRemoved, isDataUrl, isExternalUrl, normalizePath, @@ -537,7 +538,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { if ( content !== '' && // Empty attribute !namedOutput.includes(content) && // Direct reference to named output - !namedOutput.includes(content.replace(/^\//, '')) // Allow for absolute references as named output can't be an absolute path + !namedOutput.includes(initialSlashRemoved(content)) // Allow for absolute references as named output can't be an absolute path ) { try { const url = diff --git a/packages/vite/src/node/server/__tests__/pluginContainer.spec.ts b/packages/vite/src/node/server/__tests__/pluginContainer.spec.ts index 089a45497a593e..a20fc919f7fa10 100644 --- a/packages/vite/src/node/server/__tests__/pluginContainer.spec.ts +++ b/packages/vite/src/node/server/__tests__/pluginContainer.spec.ts @@ -196,7 +196,7 @@ async function getPluginContainer( ) // @ts-expect-error This plugin requires a ViteDevServer instance. - config.plugins = config.plugins.filter((p) => !/pre-alias/.test(p.name)) + config.plugins = config.plugins.filter((p) => !p.name.includes('pre-alias')) resolveId = (id) => container.resolveId(id) const container = await createPluginContainer(config, moduleGraph) diff --git a/packages/vite/src/node/server/hmr.ts b/packages/vite/src/node/server/hmr.ts index c0accc63e5bc71..fd956a8477b8d1 100644 --- a/packages/vite/src/node/server/hmr.ts +++ b/packages/vite/src/node/server/hmr.ts @@ -14,6 +14,8 @@ import type { ModuleNode } from './moduleGraph' export const debugHmr = createDebugger('vite:hmr') +const whitespaceRE = /\s/ + const normalizedClientDir = normalizePath(CLIENT_DIR) export interface HmrOptions { @@ -388,7 +390,7 @@ export function lexAcceptedHmrDeps( } else if (char === '`') { prevState = state state = LexerState.inTemplateString - } else if (/\s/.test(char)) { + } else if (whitespaceRE.test(char)) { continue } else { if (state === LexerState.inCall) { diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts index 229b860dbcec56..f2021a27638a3a 100644 --- a/packages/vite/src/node/server/middlewares/static.ts +++ b/packages/vite/src/node/server/middlewares/static.ts @@ -9,6 +9,7 @@ import { cleanUrl, fsPathFromId, fsPathFromUrl, + initialSlashRemoved, isFileReadable, isImportRequest, isInternalRequest, @@ -18,6 +19,8 @@ import { slash, } from '../../utils' +const knownJavascriptExtensionRE = /\.[tj]sx?$/ + const sirvOptions = ({ headers, shouldServe, @@ -35,7 +38,7 @@ const sirvOptions = ({ // for the MIME type video/mp2t. In almost all cases, we can expect // these files to be TypeScript files, and for Vite to serve them with // this Content-Type. - if (/\.[tj]sx?$/.test(pathname)) { + if (knownJavascriptExtensionRE.test(pathname)) { res.setHeader('Content-Type', 'application/javascript') } if (headers) { @@ -119,7 +122,7 @@ export function serveStaticMiddleware( } const resolvedPathname = redirectedPathname || pathname - let fileUrl = path.resolve(dir, resolvedPathname.replace(/^\//, '')) + let fileUrl = path.resolve(dir, initialSlashRemoved(resolvedPathname)) if (resolvedPathname.endsWith('/') && !fileUrl.endsWith('/')) { fileUrl = fileUrl + '/' } diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 02b7f6d48c80fe..3a3c2a3b79bed7 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -50,8 +50,9 @@ export const createFilter = _createFilter as ( options?: { resolve?: string | false | null }, ) => (id: string | unknown) => boolean +const windowsSlashRE = /\\/g export function slash(p: string): string { - return p.replace(/\\/g, '/') + return p.replace(windowsSlashRE, '/') } /** @@ -74,15 +75,19 @@ export function unwrapId(id: string): string { : id } +const replaceSlashOrColonRE = /[/:]/g +const replaceDotRE = /\./g +const replaceNestedIdRE = /(\s*>\s*)/g +const replaceHashRE = /#/g export const flattenId = (id: string): string => id - .replace(/[/:]/g, '_') - .replace(/\./g, '__') - .replace(/(\s*>\s*)/g, '___') - .replace(/#/g, '____') + .replace(replaceSlashOrColonRE, '_') + .replace(replaceDotRE, '__') + .replace(replaceNestedIdRE, '___') + .replace(replaceHashRE, '____') export const normalizeId = (id: string): string => - id.replace(/(\s*>\s*)/g, ' > ') + id.replace(replaceNestedIdRE, ' > ') //TODO: revisit later to see if the edge case that "compiling using node v12 code to be run in node v16 in the server" is what we intend to support. const builtins = new Set([ @@ -300,10 +305,14 @@ export function removeDirectQuery(url: string): string { return url.replace(directRequestRE, '$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(/%/g, '%25'), 'relative:///') + const resolvedUrl = new URL( + url.replace(replacePercentageRE, '%25'), + 'relative:///', + ) const { search, hash } = resolvedUrl let pathname = cleanUrl(url) pathname = isWindows ? slash(pathname) : pathname @@ -659,13 +668,12 @@ export function processSrcSetSync( ) } +const cleanSrcSetRE = + /(?:url|image|gradient|cross-fade)\([^)]*\)|"([^"]|(?<=\\)")*"|'([^']|(?<=\\)')*'/g function splitSrcSet(srcs: string) { const parts: string[] = [] // There could be a ',' inside of url(data:...), linear-gradient(...) or "data:..." - const cleanedSrcs = srcs.replace( - /(?:url|image|gradient|cross-fade)\([^)]*\)|"([^"]|(?<=\\)")*"|'([^']|(?<=\\)')*'/g, - blankReplacer, - ) + const cleanedSrcs = srcs.replace(cleanSrcSetRE, blankReplacer) let startIndex = 0 let splitIndex: number do { @@ -678,22 +686,26 @@ function splitSrcSet(srcs: string) { return parts } +const windowsDriveRE = /^[A-Z]:/ +const replaceWindowsDriveRE = /^([A-Z]):\// +const linuxAbsolutePathRE = /^\/[^/]/ function escapeToLinuxLikePath(path: string) { - if (/^[A-Z]:/.test(path)) { - return path.replace(/^([A-Z]):\//, '/windows/$1/') + if (windowsDriveRE.test(path)) { + return path.replace(replaceWindowsDriveRE, '/windows/$1/') } - if (/^\/[^/]/.test(path)) { + if (linuxAbsolutePathRE.test(path)) { return `/linux${path}` } return path } +const revertWindowsDriveRE = /^\/windows\/([A-Z])\// function unescapeToLinuxLikePath(path: string) { if (path.startsWith('/linux/')) { return path.slice('/linux'.length) } if (path.startsWith('/windows/')) { - return path.replace(/^\/windows\/([A-Z])\//, '$1:/') + return path.replace(revertWindowsDriveRE, '$1:/') } return path } @@ -1222,6 +1234,10 @@ export function joinUrlSegments(a: string, b: string): string { return a + b } +export function initialSlashRemoved(str: string): string { + return str.startsWith('/') ? str.slice(1) : str +} + export function stripBase(path: string, base: string): string { if (path === base) { return '/' @@ -1246,3 +1262,8 @@ export function evalValue(rawValue: string): T { `) return fn() } + +const escapeForRegexRE = /[-/\\^$*+?.()|[\]{}]/g +export function escapeForRegex(str: string): string { + return str.replace(escapeForRegexRE, '\\$&') +} From e34cc5cdf3ea96dfe7ca96f67754e6b71e7c2199 Mon Sep 17 00:00:00 2001 From: patak Date: Wed, 22 Mar 2023 06:57:01 +0100 Subject: [PATCH 2/3] chore: update --- packages/vite/src/node/optimizer/esbuildDepPlugin.ts | 7 ++++--- packages/vite/src/node/plugins/define.ts | 4 ++-- packages/vite/src/node/utils.ts | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index c7a749411e95ee..e3bb4730eef1d7 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -4,7 +4,7 @@ import { CSS_LANGS_RE, KNOWN_ASSET_TYPES } from '../constants' import { getDepOptimizationConfig } from '..' import type { PackageCache, ResolvedConfig } from '..' import { - escapeForRegex, + escapeRegex, flattenId, isBuiltin, isExternalUrl, @@ -282,6 +282,8 @@ module.exports = Object.create(new Proxy({}, { } } +const matchesEntireLine = (text: string) => `^${escapeRegex(text)}$` + // esbuild doesn't transpile `require('foo')` into `import` statements if 'foo' is externalized // https://github.com/evanw/esbuild/issues/566#issuecomment-735551834 export function esbuildCjsExternalPlugin( @@ -291,8 +293,7 @@ export function esbuildCjsExternalPlugin( return { name: 'cjs-external', setup(build) { - const escape = (text: string) => `^${escapeForRegex(text)}$` - const filter = new RegExp(externals.map(escape).join('|')) + const filter = new RegExp(externals.map(matchesEntireLine).join('|')) build.onResolve({ filter: new RegExp(`^${nonFacadePrefix}`) }, (args) => { return { diff --git a/packages/vite/src/node/plugins/define.ts b/packages/vite/src/node/plugins/define.ts index 79827170029bb0..c0c37183b81aaf 100644 --- a/packages/vite/src/node/plugins/define.ts +++ b/packages/vite/src/node/plugins/define.ts @@ -1,7 +1,7 @@ import MagicString from 'magic-string' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' -import { escapeForRegex, transformStableResult } from '../utils' +import { escapeRegex, transformStableResult } from '../utils' import { isCSSRequest } from './css' import { isHTMLRequest } from './html' @@ -113,7 +113,7 @@ export function definePlugin(config: ResolvedConfig): Plugin { // Mustn't be preceded by a char that can be part of an identifier // or a '.' that isn't part of a spread operator '(?(rawValue: string): T { return fn() } -const escapeForRegexRE = /[-/\\^$*+?.()|[\]{}]/g -export function escapeForRegex(str: string): string { - return str.replace(escapeForRegexRE, '\\$&') +const escapeRegexRE = /[-/\\^$*+?.()|[\]{}]/g +export function escapeRegex(str: string): string { + return str.replace(escapeRegexRE, '\\$&') } From 2497321e38da3799aeac8c80638203e740703520 Mon Sep 17 00:00:00 2001 From: patak Date: Wed, 22 Mar 2023 09:20:01 +0100 Subject: [PATCH 3/3] chore: update --- packages/vite/src/node/optimizer/index.ts | 4 ++-- packages/vite/src/node/plugins/asset.ts | 4 ++-- packages/vite/src/node/plugins/html.ts | 4 ++-- packages/vite/src/node/server/middlewares/static.ts | 4 ++-- packages/vite/src/node/utils.ts | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index fcd7167cae5069..c9e189a5da18e6 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -16,12 +16,12 @@ import { emptyDir, flattenId, getHash, - initialSlashRemoved, isOptimizable, lookupFile, normalizeId, normalizePath, removeDir, + removeLeadingSlash, renameDir, writeFile, } from '../utils' @@ -958,7 +958,7 @@ export function createIsOptimizedDepUrl( const depsCacheDirPrefix = depsCacheDirRelative.startsWith('../') ? // if the cache directory is outside root, the url prefix would be something // like '/@fs/absolute/path/to/node_modules/.vite' - `/@fs/${initialSlashRemoved(normalizePath(depsCacheDir))}` + `/@fs/${removeLeadingSlash(normalizePath(depsCacheDir))}` : // if the cache directory is inside root, the url prefix would be something // like '/node_modules/.vite' `/${depsCacheDirRelative}` diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 51e9f11ea7d621..c4d8ba5a8e9cd9 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -19,9 +19,9 @@ import type { ResolvedConfig } from '../config' import { cleanUrl, getHash, - initialSlashRemoved, joinUrlSegments, normalizePath, + removeLeadingSlash, } from '../utils' import { FS_PREFIX } from '../constants' @@ -260,7 +260,7 @@ function fileToDevUrl(id: string, config: ResolvedConfig) { rtn = path.posix.join(FS_PREFIX, id) } const base = joinUrlSegments(config.server?.origin ?? '', config.base) - return joinUrlSegments(base, initialSlashRemoved(rtn)) + return joinUrlSegments(base, removeLeadingSlash(rtn)) } export function getPublicAssetFilename( diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 759bc1e93892c9..18dc2405ba988f 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -16,11 +16,11 @@ import { cleanUrl, generateCodeFrame, getHash, - initialSlashRemoved, isDataUrl, isExternalUrl, normalizePath, processSrcSet, + removeLeadingSlash, } from '../utils' import type { ResolvedConfig } from '../config' import { toOutputFilePathInHtml } from '../build' @@ -538,7 +538,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { if ( content !== '' && // Empty attribute !namedOutput.includes(content) && // Direct reference to named output - !namedOutput.includes(initialSlashRemoved(content)) // Allow for absolute references as named output can't be an absolute path + !namedOutput.includes(removeLeadingSlash(content)) // Allow for absolute references as named output can't be an absolute path ) { try { const url = diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts index f2021a27638a3a..27ac7cbceecab5 100644 --- a/packages/vite/src/node/server/middlewares/static.ts +++ b/packages/vite/src/node/server/middlewares/static.ts @@ -9,12 +9,12 @@ import { cleanUrl, fsPathFromId, fsPathFromUrl, - initialSlashRemoved, isFileReadable, isImportRequest, isInternalRequest, isParentDirectory, isWindows, + removeLeadingSlash, shouldServeFile, slash, } from '../../utils' @@ -122,7 +122,7 @@ export function serveStaticMiddleware( } const resolvedPathname = redirectedPathname || pathname - let fileUrl = path.resolve(dir, initialSlashRemoved(resolvedPathname)) + let fileUrl = path.resolve(dir, removeLeadingSlash(resolvedPathname)) if (resolvedPathname.endsWith('/') && !fileUrl.endsWith('/')) { fileUrl = fileUrl + '/' } diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 8db1c376c37c2f..1b385c19bb6736 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -1234,8 +1234,8 @@ export function joinUrlSegments(a: string, b: string): string { return a + b } -export function initialSlashRemoved(str: string): string { - return str.startsWith('/') ? str.slice(1) : str +export function removeLeadingSlash(str: string): string { + return str[0] === '/' ? str.slice(1) : str } export function stripBase(path: string, base: string): string {