Skip to content

Commit

Permalink
perf: more pattern hoisting
Browse files Browse the repository at this point in the history
  • Loading branch information
GalacticHypernova committed Oct 25, 2024
1 parent 732a82e commit ada1fef
Show file tree
Hide file tree
Showing 25 changed files with 128 additions and 78 deletions.
3 changes: 2 additions & 1 deletion packages/vite/src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ErrorPayload, HotPayload } from 'types/hmrPayload'
import type { ViteHotContext } from 'types/hot'
import type { InferCustomEventPayload } from 'types/customEvent'
import { HMRClient, HMRContext } from '../shared/hmr'
import { postfixRE } from '../shared/utils'
import { ErrorOverlay, overlayId } from './overlay'
import '@vite/env'

Expand Down Expand Up @@ -456,7 +457,7 @@ export function injectQuery(url: string, queryToInject: string): string {
}

// can't use pathname from URL since it may be relative like ../
const pathname = url.replace(/[?#].*$/, '')
const pathname = url.replace(postfixRE, '')
const { search, hash } = new URL(url, 'http://vite.dev')

return `${pathname}?${queryToInject}${search ? `&` + search.slice(1) : ''}${
Expand Down
13 changes: 8 additions & 5 deletions packages/vite/src/module-runner/evaluatedModules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,10 @@ export class EvaluatedModules {

// unique id that is not available as "$bare_import" like "test"
const prefixedBuiltins = new Set(['node:test', 'node:sqlite'])

const fsRE = /^\/@fs\//
const nodeRE = /^node:/
const leadingSlashRE = /^\/+/
const fileRE = /^file:\//
// transform file url to id
// virtual:custom -> virtual:custom
// \0custom -> \0custom
Expand All @@ -138,10 +141,10 @@ function normalizeModuleId(file: string): string {

// unix style, but Windows path still starts with the drive letter to check the root
const unixFile = slash(file)
.replace(/^\/@fs\//, isWindows ? '' : '/')
.replace(/^node:/, '')
.replace(/^\/+/, '/')
.replace(fsRE, isWindows ? '' : '/')
.replace(nodeRE, '')
.replace(leadingSlashRE, '/')

// if it's not in the root, keep it as a path, not a URL
return unixFile.replace(/^file:\//, '/')
return unixFile.replace(fileRE, '/')
}
21 changes: 13 additions & 8 deletions packages/vite/src/module-runner/sourcemap/interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,16 @@ interface CachedMapEntry {
vite?: boolean
}

const protocolRE = /^\w+:\/\/[^/]*/
const fileUrlRE = /^\/\w:/
// Support URLs relative to a directory, but be careful about a protocol prefix
function supportRelativeURL(file: string, url: string) {
if (!file) return url
const dir = posixDirname(slash(file))
const match = /^\w+:\/\/[^/]*/.exec(dir)
const match = protocolRE.exec(dir)
let protocol = match ? match[0] : ''
const startPath = dir.slice(protocol.length)
if (protocol && /^\/\w:/.test(startPath)) {
if (protocol && fileUrlRE.test(startPath)) {
// handle file:///C:/ paths
protocol += '/'
return protocol + slash(posixResolve(startPath, url))
Expand Down Expand Up @@ -125,17 +127,17 @@ function retrieveFile(path: string): string | null | undefined | false {
return null
}

const sourceMapUrlRE =
/\/\/[@#]\s*sourceMappingURL=([^\s'"]+)\s*$|\/\*[@#]\s*sourceMappingURL=[^\s*'"]+\s*\*\/\s*$/gm
function retrieveSourceMapURL(source: string) {
// Get the URL of the source map
const fileData = retrieveFile(source)
if (!fileData) return null
const re =
/\/\/[@#]\s*sourceMappingURL=([^\s'"]+)\s*$|\/\*[@#]\s*sourceMappingURL=[^\s*'"]+\s*\*\/\s*$/gm
// Keep executing the search to find the *last* sourceMappingURL to avoid
// picking up sourceMappingURLs from comments, strings, etc.
let lastMatch, match

while ((match = re.exec(fileData))) lastMatch = match
while ((match = sourceMapUrlRE.exec(fileData))) lastMatch = match
if (!lastMatch) return null
return lastMatch[1]
}
Expand Down Expand Up @@ -234,11 +236,13 @@ function mapSourcePosition(position: OriginalMapping) {
return position
}

const evalCallRE = /^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/
const nestedEvalRE = /^eval at ([^(]+) \((.+)\)$/
// Parses code generated by FormatEvalOrigin(), a function inside V8:
// https://code.google.com/p/v8/source/browse/trunk/src/messages.js
function mapEvalOrigin(origin: string): string {
// Most eval() calls are in this format
let match = /^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/.exec(origin)
let match = evalCallRE.exec(origin)
if (match) {
const position = mapSourcePosition({
name: null,
Expand All @@ -250,7 +254,7 @@ function mapEvalOrigin(origin: string): string {
}

// Parse nested eval() calls using recursion
match = /^eval at ([^(]+) \((.+)\)$/.exec(origin)
match = nestedEvalRE.exec(origin)
if (match) return `eval at ${match[1]} (${mapEvalOrigin(match[2])})`

// Make sure we still return useful information if we didn't find anything
Expand Down Expand Up @@ -329,12 +333,13 @@ function CallSiteToString(this: CallSite) {
return line
}

const isGetRE = /^(?:is|get)/
function cloneCallSite(frame: CallSite) {
const object = {} as CallSite
Object.getOwnPropertyNames(Object.getPrototypeOf(frame)).forEach((name) => {
const key = name as keyof CallSite
// @ts-expect-error difficult to type
object[key] = /^(?:is|get)/.test(name)
object[key] = isGetRE.test(name)
? function () {
return frame[key].call(frame)
}
Expand Down
19 changes: 11 additions & 8 deletions packages/vite/src/module-runner/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import * as pathe from 'pathe'
import { isWindows, slash } from '../shared/utils'
import {
backslashRE,
hashRE,
isWindows,
newlineRegEx,
slash,
} from '../shared/utils'

export function normalizeAbsoluteUrl(url: string, root: string): string {
url = slash(url)
Expand Down Expand Up @@ -31,19 +37,16 @@ const CHAR_FORWARD_SLASH = 47
const CHAR_BACKWARD_SLASH = 92

const percentRegEx = /%/g
const backslashRegEx = /\\/g
const newlineRegEx = /\n/g
const carriageReturnRegEx = /\r/g
const tabRegEx = /\t/g
const questionRegex = /\?/g
const hashRegex = /#/g

function encodePathChars(filepath: string) {
if (filepath.indexOf('%') !== -1)
filepath = filepath.replace(percentRegEx, '%25')
// In posix, backslash is a valid character in paths:
if (!isWindows && filepath.indexOf('\\') !== -1)
filepath = filepath.replace(backslashRegEx, '%5C')
filepath = filepath.replace(backslashRE, '%5C')
if (filepath.indexOf('\n') !== -1)
filepath = filepath.replace(newlineRegEx, '%0A')
if (filepath.indexOf('\r') !== -1)
Expand Down Expand Up @@ -76,11 +79,11 @@ export function posixPathToFileHref(posixPath: string): string {
// later triggering pathname setter, which impacts performance
if (resolved.indexOf('?') !== -1)
resolved = resolved.replace(questionRegex, '%3F')
if (resolved.indexOf('#') !== -1)
resolved = resolved.replace(hashRegex, '%23')
if (resolved.indexOf('#') !== -1) resolved = resolved.replace(hashRE, '%23')
return new URL(`file://${resolved}`).href
}

const forwardSlashRE = /\//g
export function toWindowsPath(path: string): string {
return path.replace(/\//g, '\\')
return path.replace(forwardSlashRE, '\\')
}
8 changes: 4 additions & 4 deletions packages/vite/src/node/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import commonjsPlugin from '@rollup/plugin-commonjs'
import type { RollupCommonJSOptions } from 'dep-types/commonjs'
import type { RollupDynamicImportVarsOptions } from 'dep-types/dynamicImportVars'
import type { TransformOptions } from 'esbuild'
import { withTrailingSlash } from '../shared/utils'
import { backslashRE, withTrailingSlash } from '../shared/utils'
import {
DEFAULT_ASSETS_INLINE_LIMIT,
ESBUILD_MODULES_TARGET,
Expand Down Expand Up @@ -617,12 +617,13 @@ async function buildEnvironment(
return stack
}

const esbuildNewlineRE = /^\n|\n$/g
/**
* Esbuild code frames have newlines at the start and end of the frame, rollup doesn't
* This function normalizes the frame to match the esbuild format which has more pleasing padding
*/
const normalizeCodeFrame = (frame: string) => {
const trimmedPadding = frame.replace(/^\n|\n$/g, '')
const trimmedPadding = frame.replace(esbuildNewlineRE, '')
return `\n${trimmedPadding}\n`
}

Expand Down Expand Up @@ -1248,11 +1249,10 @@ function injectSsrFlag<T extends Record<string, any>>(
*/
const needsEscapeRegEx = /[\n\r'\\\u2028\u2029]/
const quoteNewlineRegEx = /([\n\r'\u2028\u2029])/g
const backSlashRegEx = /\\/g

function escapeId(id: string): string {
if (!needsEscapeRegEx.test(id)) return id
return id.replace(backSlashRegEx, '\\\\').replace(quoteNewlineRegEx, '\\$1')
return id.replace(backslashRE, '\\\\').replace(quoteNewlineRegEx, '\\$1')
}

const getResolveUrl = (path: string, URL = 'URL') => `new ${URL}(${path}).href`
Expand Down
3 changes: 2 additions & 1 deletion packages/vite/src/node/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,14 @@ export function createLogger(
return logger
}

const portRE = /:(\d+)\//
export function printServerUrls(
urls: ResolvedServerUrls,
optionsHost: string | boolean | undefined,
info: Logger['info'],
): void {
const colorUrl = (url: string) =>
colors.cyan(url.replace(/:(\d+)\//, (_, port) => `:${colors.bold(port)}/`))
colors.cyan(url.replace(portRE, (_, port) => `:${colors.bold(port)}/`))
for (const url of urls.local) {
info(` ${colors.green('➜')} ${colors.bold('Local')}: ${colorUrl(url)}`)
}
Expand Down
10 changes: 6 additions & 4 deletions packages/vite/src/node/optimizer/esbuildDepPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import type { ImportKind, Plugin } from 'esbuild'
import { JS_TYPES_RE, KNOWN_ASSET_TYPES } from '../constants'
import type { PackageCache } from '../packages'
import {
anyRE,
escapeRegex,
flattenId,
isBuiltin,
isExternalUrl,
moduleListContains,
normalizePath,
windowsVolumeRE,
} from '../utils'
import { browserExternalId, optionalPeerDepId } from '../plugins/resolve'
import { isCSSRequest, isModuleCSSRequest } from '../plugins/css'
Expand Down Expand Up @@ -207,7 +209,7 @@ export function esbuildDepPlugin(
}

build.onResolve(
{ filter: /^[\w@][^:]/ },
{ filter: windowsVolumeRE },
async ({ path: id, importer, kind }) => {
if (moduleListContains(external, id)) {
return {
Expand Down Expand Up @@ -237,7 +239,7 @@ export function esbuildDepPlugin(
)

build.onLoad(
{ filter: /.*/, namespace: 'browser-external' },
{ filter: anyRE, namespace: 'browser-external' },
({ path }) => {
if (isProduction) {
return {
Expand Down Expand Up @@ -280,7 +282,7 @@ module.exports = Object.create(new Proxy({}, {
)

build.onLoad(
{ filter: /.*/, namespace: 'optional-peer-dep' },
{ filter: anyRE, namespace: 'optional-peer-dep' },
({ path }) => {
if (isProduction) {
return {
Expand Down Expand Up @@ -334,7 +336,7 @@ export function esbuildCjsExternalPlugin(
})

build.onLoad(
{ filter: /.*/, namespace: cjsExternalFacadeNamespace },
{ filter: anyRE, namespace: cjsExternalFacadeNamespace },
(args) => ({
contents:
`import * as m from ${JSON.stringify(
Expand Down
3 changes: 2 additions & 1 deletion packages/vite/src/node/optimizer/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function createOptimizeDepsIncludeResolver(
}
}

const asteriskRE = /\*/g
/**
* Expand the glob syntax in `optimizeDeps.include` to proper import paths
*/
Expand Down Expand Up @@ -82,7 +83,7 @@ export function expandGlobIds(id: string, config: ResolvedConfig): string[] {
// "./dist/glob/*-browser/*.js" => "./dist/glob/**/*-browser/**/*.js"
// NOTE: in some cases, this could expand to consecutive /**/*/**/* etc
// but it's fine since `tinyglobby` handles it the same.
const exportValuePattern = exportsValue.replace(/\*/g, '**/*')
const exportValuePattern = exportsValue.replace(asteriskRE, '**/*')
// "./dist/glob/*-browser/*.js" => /dist\/glob\/(.*)-browser\/(.*)\.js/
const exportsValueGlobRe = new RegExp(
exportsValue.split('*').map(escapeRegex).join('(.*)'),
Expand Down
10 changes: 6 additions & 4 deletions packages/vite/src/node/optimizer/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
SPECIAL_QUERY_RE,
} from '../constants'
import {
anyRE,
arraify,
createDebugger,
dataUrlRE,
Expand All @@ -33,6 +34,7 @@ import {
singlelineCommentsRE,
virtualModulePrefix,
virtualModuleRE,
windowsVolumeRE,
} from '../utils'
import { resolveEnvironmentPlugins } from '../plugin'
import type { EnvironmentPluginContainer } from '../server/pluginContainer'
Expand Down Expand Up @@ -473,7 +475,7 @@ function esbuildScanPlugin(
}
})

build.onLoad({ filter: /.*/, namespace: 'script' }, ({ path }) => {
build.onLoad({ filter: anyRE, namespace: 'script' }, ({ path }) => {
return scripts[path]
})

Expand Down Expand Up @@ -631,7 +633,7 @@ function esbuildScanPlugin(
build.onResolve(
{
// avoid matching windows volume
filter: /^[\w@][^:]/,
filter: windowsVolumeRE,
},
async ({ path: id, importer, pluginData }) => {
if (moduleListContains(exclude, id)) {
Expand Down Expand Up @@ -704,7 +706,7 @@ function esbuildScanPlugin(

build.onResolve(
{
filter: /.*/,
filter: anyRE,
},
async ({ path: id, importer, pluginData }) => {
// use vite resolver to support urls and omitted extensions
Expand Down Expand Up @@ -767,7 +769,7 @@ function esbuildScanPlugin(
// onResolve is not called for glob imports.
// we need to add that here as well until esbuild calls onResolve for glob imports.
// https://github.com/evanw/esbuild/issues/3317
build.onLoad({ filter: /.*/, namespace: 'file' }, () => {
build.onLoad({ filter: anyRE, namespace: 'file' }, () => {
return {
loader: 'js',
contents: 'export default {}',
Expand Down
7 changes: 4 additions & 3 deletions packages/vite/src/node/plugins/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
rawRE,
removeLeadingSlash,
removeUrlQuery,
spaceRE,
urlRE,
} from '../utils'
import { DEFAULT_ASSETS_INLINE_LIMIT, FS_PREFIX } from '../constants'
Expand Down Expand Up @@ -443,7 +444,7 @@ const shouldInline = (
}

const nestedQuotesRE = /"[^"']*'[^"]*"|'[^'"]*"[^']*'/

const chevronRE = />\s+</g
// Inspired by https://github.com/iconify/iconify/blob/main/packages/utils/src/svg/url.ts
function svgToDataURL(content: Buffer): string {
const stringContent = content.toString()
Expand All @@ -460,7 +461,7 @@ function svgToDataURL(content: Buffer): string {
'data:image/svg+xml,' +
stringContent
.trim()
.replaceAll(/>\s+</g, '><')
.replaceAll(chevronRE, '><')
.replaceAll('"', "'")
.replaceAll('%', '%25')
.replaceAll('#', '%23')
Expand All @@ -469,7 +470,7 @@ function svgToDataURL(content: Buffer): string {
// Spaces are not valid in srcset it has some use cases
// it can make the uncompressed URI slightly higher than base64, but will compress way better
// https://github.com/vitejs/vite/pull/14643#issuecomment-1766288673
.replaceAll(/\s+/g, '%20')
.replaceAll(spaceRE, '%20')
)
}
}
4 changes: 2 additions & 2 deletions packages/vite/src/node/plugins/clientInjections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const normalizedEnvEntry = normalizePath(ENV_ENTRY)
*/
export function clientInjectionsPlugin(config: ResolvedConfig): Plugin {
let injectConfigValues: (code: string) => string

const baseRE = /__BASE__/g
return {
name: 'vite:client-inject',
async buildStart() {
Expand Down Expand Up @@ -76,7 +76,7 @@ export function clientInjectionsPlugin(config: ResolvedConfig): Plugin {
injectConfigValues = (code: string) => {
return code
.replace(`__MODE__`, modeReplacement)
.replace(/__BASE__/g, baseReplacement)
.replace(baseRE, baseReplacement)
.replace(`__DEFINES__`, definesReplacement)
.replace(`__SERVER_HOST__`, serverHostReplacement)
.replace(`__HMR_PROTOCOL__`, hmrProtocolReplacement)
Expand Down
Loading

0 comments on commit ada1fef

Please sign in to comment.