perf: avoid parseRequest (#15617)

This commit is contained in:
Bjorn Lu 2024-01-16 18:00:19 +08:00 committed by GitHub
parent bdb826ca0a
commit 0cacfadf3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 61 additions and 78 deletions

View File

@ -1,10 +1,10 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`parse positives > ? in url 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mo\\\\?ds/*.js", {"query":"url","import":"*"})), \`./mo?ds/\${base ?? foo}.js\`)"`;
exports[`parse positives > ? in url 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mo\\\\?ds/*.js", {"query":"?url","import":"*"})), \`./mo?ds/\${base ?? foo}.js\`)"`;
exports[`parse positives > ? in variables 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js", {"query":"raw","import":"*"})), \`./mods/\${base ?? foo}.js\`)"`;
exports[`parse positives > ? in variables 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js", {"query":"?raw","import":"*"})), \`./mods/\${base ?? foo}.js\`)"`;
exports[`parse positives > ? in worker 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mo\\\\?ds/*.js", {"query":"worker","import":"*"})), \`./mo?ds/\${base ?? foo}.js\`)"`;
exports[`parse positives > ? in worker 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mo\\\\?ds/*.js", {"query":"?worker","import":"*"})), \`./mo?ds/\${base ?? foo}.js\`)"`;
exports[`parse positives > alias path 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js")), \`./mods/\${base}.js\`)"`;
@ -14,8 +14,8 @@ exports[`parse positives > with ../ and itself 1`] = `"__variableDynamicImportRu
exports[`parse positives > with multi ../ and itself 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("../../plugins/dynamicImportVar/*.js")), \`./\${name}.js\`)"`;
exports[`parse positives > with query 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js", {"query":{"foo":"bar"}})), \`./mods/\${base}.js\`)"`;
exports[`parse positives > with query 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js", {"query":"?foo=bar"})), \`./mods/\${base}.js\`)"`;
exports[`parse positives > with query raw 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js", {"query":"raw","import":"*"})), \`./mods/\${base}.js\`)"`;
exports[`parse positives > with query raw 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js", {"query":"?raw","import":"*"})), \`./mods/\${base}.js\`)"`;
exports[`parse positives > with query url 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js", {"query":"url","import":"*"})), \`./mods/\${base}.js\`)"`;
exports[`parse positives > with query url 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js", {"query":"?url","import":"*"})), \`./mods/\${base}.js\`)"`;

View File

@ -58,7 +58,6 @@ import {
isObject,
joinUrlSegments,
normalizePath,
parseRequest,
processSrcSet,
removeDirectQuery,
removeUrlQuery,
@ -171,6 +170,7 @@ export function resolveCSSOptions(
const cssModuleRE = new RegExp(`\\.module${CSS_LANGS_RE.source}`)
const directRequestRE = /[?&]direct\b/
const htmlProxyRE = /[?&]html-proxy\b/
const htmlProxyIndexRE = /&index=(\d+)/
const commonjsProxyRE = /\?commonjs-proxy/
const inlineRE = /[?&]inline\b/
const inlineCSSRE = /[?&]inline-css\b/
@ -474,12 +474,15 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
const inlineCSS = inlineCSSRE.test(id)
const isHTMLProxy = htmlProxyRE.test(id)
if (inlineCSS && isHTMLProxy) {
const query = parseRequest(id)
if (styleAttrRE.test(id)) {
css = css.replace(/"/g, '"')
}
const index = htmlProxyIndexRE.exec(id)?.[1]
if (index == null) {
throw new Error(`HTML proxy index in "${id}" not found`)
}
addToHTMLProxyTransformResult(
`${getHash(cleanUrl(id))}_${Number.parseInt(query!.index)}`,
`${getHash(cleanUrl(id))}_${Number.parseInt(index)}`,
css,
)
return `export default ''`

View File

@ -10,13 +10,15 @@ import { CLIENT_ENTRY } from '../constants'
import {
createFilter,
normalizePath,
parseRequest,
rawRE,
requestQueryMaybeEscapedSplitRE,
requestQuerySplitRE,
transformStableResult,
urlRE,
} from '../utils'
import { toAbsoluteGlob } from './importMetaGlob'
import { hasViteIgnoreRE } from './importAnalysis'
import { workerOrSharedWorkerRE } from './worker'
export const dynamicImportHelperId = '\0vite/dynamic-import-helper.js'
@ -53,9 +55,6 @@ function parseDynamicImportPattern(
strings: string,
): DynamicImportPattern | null {
const filename = strings.slice(1, -1)
const rawQuery = parseRequest(filename)
let globParams: DynamicImportRequest | null = null
const ast = (
parseJS(strings, {
ecmaVersion: 'latest',
@ -73,19 +72,23 @@ function parseDynamicImportPattern(
requestQueryMaybeEscapedSplitRE,
2,
)
const [rawPattern] = filename.split(requestQuerySplitRE, 2)
const globQuery = (['worker', 'url', 'raw'] as const).find(
(key) => rawQuery && key in rawQuery,
)
if (globQuery) {
globParams = {
query: globQuery,
import: '*',
}
} else if (rawQuery) {
globParams = {
query: rawQuery,
let [rawPattern, search] = filename.split(requestQuerySplitRE, 2)
let globParams: DynamicImportRequest | null = null
if (search) {
search = '?' + search
if (
workerOrSharedWorkerRE.test(search) ||
urlRE.test(search) ||
rawRE.test(search)
) {
globParams = {
query: search,
import: '*',
}
} else {
globParams = {
query: search,
}
}
}

View File

@ -5,7 +5,7 @@ import type { ResolvedConfig } from '../config'
import type { Plugin } from '../plugin'
import type { ViteDevServer } from '../server'
import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants'
import { cleanUrl, getHash, injectQuery, parseRequest } from '../utils'
import { cleanUrl, getHash, injectQuery, urlRE } from '../utils'
import {
createToImportMetaURLBasedRelativeRuntime,
onRollupWarning,
@ -28,6 +28,10 @@ interface WorkerCache {
export type WorkerType = 'classic' | 'module' | 'ignore'
export const workerOrSharedWorkerRE = /(?:\?|&)(worker|sharedworker)(?:&|$)/
const workerFileRE = /(?:\?|&)worker_file&type=(\w+)(?:&|$)/
const inlineRE = /[?&]inline\b/
export const WORKER_FILE_ID = 'worker_file'
const workerCache = new WeakMap<ResolvedConfig, WorkerCache>()
@ -43,7 +47,6 @@ function saveEmitWorkerAsset(
async function bundleWorkerEntry(
config: ResolvedConfig,
id: string,
query: Record<string, string> | null,
): Promise<OutputChunk> {
// bundle the file as entry to support imports
const { rollup } = await import('rollup')
@ -99,12 +102,11 @@ async function bundleWorkerEntry(
} finally {
await bundle.close()
}
return emitSourcemapForWorkerEntry(config, query, chunk)
return emitSourcemapForWorkerEntry(config, chunk)
}
function emitSourcemapForWorkerEntry(
config: ResolvedConfig,
query: Record<string, string> | null,
chunk: OutputChunk,
): OutputChunk {
const { map: sourcemap } = chunk
@ -144,12 +146,11 @@ function encodeWorkerAssetFileName(
export async function workerFileToUrl(
config: ResolvedConfig,
id: string,
query: Record<string, string> | null,
): Promise<string> {
const workerMap = workerCache.get(config.mainConfig || config)!
let fileName = workerMap.bundle.get(id)
if (!fileName) {
const outputChunk = await bundleWorkerEntry(config, id, query)
const outputChunk = await bundleWorkerEntry(config, id)
fileName = outputChunk.fileName
saveEmitWorkerAsset(config, {
fileName,
@ -191,18 +192,6 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
let server: ViteDevServer
const isWorker = config.isWorker
const isWorkerQueryId = (id: string) => {
const parsedQuery = parseRequest(id)
if (
parsedQuery &&
(parsedQuery.worker ?? parsedQuery.sharedworker) != null
) {
return true
}
return false
}
return {
name: 'vite:worker',
@ -222,23 +211,23 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
},
load(id) {
if (isBuild && isWorkerQueryId(id)) {
if (isBuild && workerOrSharedWorkerRE.test(id)) {
return ''
}
},
shouldTransformCachedModule({ id }) {
if (isBuild && config.build.watch && isWorkerQueryId(id)) {
if (isBuild && config.build.watch && workerOrSharedWorkerRE.test(id)) {
return true
}
},
async transform(raw, id, options) {
const query = parseRequest(id)
if (query && query[WORKER_FILE_ID] != null) {
async transform(raw, id) {
const workerFileMatch = workerFileRE.exec(id)
if (workerFileMatch) {
// if import worker by worker constructor will have query.type
// other type will be import worker by esm
const workerType = query['type']! as WorkerType
const workerType = workerFileMatch[1] as WorkerType
let injectEnv = ''
const scriptPath = JSON.stringify(
@ -270,18 +259,15 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
}
return
}
if (
query == null ||
(query && (query.worker ?? query.sharedworker) == null)
) {
return
}
const workerMatch = workerOrSharedWorkerRE.exec(id)
if (!workerMatch) return
// stringified url or `new URL(...)`
let url: string
const { format } = config.worker
const workerConstructor =
query.sharedworker != null ? 'SharedWorker' : 'Worker'
workerMatch[1] === 'sharedworker' ? 'SharedWorker' : 'Worker'
const workerType = isBuild
? format === 'es'
? 'module'
@ -293,8 +279,8 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
}`
if (isBuild) {
if (query.inline != null) {
const chunk = await bundleWorkerEntry(config, id, query)
if (inlineRE.test(id)) {
const chunk = await bundleWorkerEntry(config, id)
const encodedJs = `const encodedJs = "${Buffer.from(
chunk.code,
).toString('base64')}";`
@ -349,15 +335,14 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
map: { mappings: '' },
}
} else {
url = await workerFileToUrl(config, id, query)
url = await workerFileToUrl(config, id)
}
} else {
url = await fileToUrl(cleanUrl(id), config, this)
url = injectQuery(url, WORKER_FILE_ID)
url = injectQuery(url, `type=${workerType}`)
url = injectQuery(url, `${WORKER_FILE_ID}&type=${workerType}`)
}
if (query.url != null) {
if (urlRE.test(id)) {
return {
code: `export default ${JSON.stringify(url)}`,
map: { mappings: '' }, // Empty sourcemap to suppress Rollup warning

View File

@ -8,7 +8,6 @@ import {
cleanUrl,
evalValue,
injectQuery,
parseRequest,
slash,
transformStableResult,
} from '../utils'
@ -131,7 +130,6 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
async transform(code, id, options) {
if (!options?.ssr && isIncludeWorkerImportMetaUrl(code)) {
const query = parseRequest(id)
let s: MagicString | undefined
const cleanString = stripLiteral(code)
const workerImportMetaUrlRE =
@ -174,11 +172,13 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
let builtUrl: string
if (isBuild) {
builtUrl = await workerFileToUrl(config, file, query)
builtUrl = await workerFileToUrl(config, file)
} else {
builtUrl = await fileToUrl(cleanUrl(file), config, this)
builtUrl = injectQuery(builtUrl, WORKER_FILE_ID)
builtUrl = injectQuery(builtUrl, `type=${workerType}`)
builtUrl = injectQuery(
builtUrl,
`${WORKER_FILE_ID}&type=${workerType}`,
)
}
s.update(
expStart,

View File

@ -3,7 +3,7 @@ import os from 'node:os'
import path from 'node:path'
import { exec } from 'node:child_process'
import { createHash } from 'node:crypto'
import { URL, URLSearchParams, fileURLToPath } from 'node:url'
import { URL, fileURLToPath } from 'node:url'
import { builtinModules, createRequire } from 'node:module'
import { promises as dns } from 'node:dns'
import { performance } from 'node:perf_hooks'
@ -1041,14 +1041,6 @@ export const singlelineCommentsRE = /\/\/.*/g
export const requestQuerySplitRE = /\?(?!.*[/|}])/
export const requestQueryMaybeEscapedSplitRE = /\\?\?(?!.*[/|}])/
export function parseRequest(id: string): Record<string, string> | null {
const [_, search] = id.split(requestQuerySplitRE, 2)
if (!search) {
return null
}
return Object.fromEntries(new URLSearchParams(search))
}
export const blankReplacer = (match: string): string => ' '.repeat(match.length)
export function getHash(text: Buffer | string, length = 8): string {

View File

@ -161,7 +161,7 @@ test('import.meta.glob eager in worker', async () => {
})
test.runIf(isServe)('sourcemap boundary', async () => {
const response = page.waitForResponse(/my-worker.ts\?type=module&worker_file/)
const response = page.waitForResponse(/my-worker.ts\?worker_file&type=module/)
await page.goto(viteTestUrl)
const content = await (await response).text()
const { mappings } = decodeSourceMapUrl(content)