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

perf: avoid parseRequest #15617

Merged
merged 5 commits into from
Jan 16, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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\`)"`;

Expand All @@ -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\`)"`;
9 changes: 6 additions & 3 deletions packages/vite/src/node/plugins/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ import {
isObject,
joinUrlSegments,
normalizePath,
parseRequest,
processSrcSet,
removeDirectQuery,
removeUrlQuery,
Expand Down Expand Up @@ -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/
Expand Down Expand Up @@ -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 ''`
Expand Down
37 changes: 20 additions & 17 deletions packages/vite/src/node/plugins/dynamicImportVars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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',
Expand All @@ -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,
}
}
}

Expand Down
59 changes: 22 additions & 37 deletions packages/vite/src/node/plugins/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -28,6 +28,10 @@ interface WorkerCache {

export type WorkerType = 'classic' | 'module' | 'ignore'

export const workerOrSharedWorkerRE = /(?:\?|&)(worker|sharedworker)(?:&|$)/
const workerFileRE = /(?:\?|&)worker_file&type=(\w+)(?:&|$)/
Copy link
Member

@patak-dev patak-dev Jan 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference to others, we already depend on the order of query suffixes in other cases, like in html proxies. See

const htmlProxyRE =

So this should be ok.

const inlineRE = /[?&]inline\b/

export const WORKER_FILE_ID = 'worker_file'
const workerCache = new WeakMap<ResolvedConfig, WorkerCache>()

Expand All @@ -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')
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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',

Expand All @@ -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(
Expand Down Expand Up @@ -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'
Expand All @@ -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')}";`
Expand Down Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions packages/vite/src/node/plugins/workerImportMetaUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
cleanUrl,
evalValue,
injectQuery,
parseRequest,
slash,
transformStableResult,
} from '../utils'
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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,
Expand Down
10 changes: 1 addition & 9 deletions packages/vite/src/node/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion playground/worker/__tests__/iife/iife-worker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down