diff --git a/packages/vite/src/node/__tests__/plugins/dynamicImportVar/__snapshots__/parse.spec.ts.snap b/packages/vite/src/node/__tests__/plugins/dynamicImportVar/__snapshots__/parse.spec.ts.snap index 8abb29e5465cac..1ab7e73b372ea2 100644 --- a/packages/vite/src/node/__tests__/plugins/dynamicImportVar/__snapshots__/parse.spec.ts.snap +++ b/packages/vite/src/node/__tests__/plugins/dynamicImportVar/__snapshots__/parse.spec.ts.snap @@ -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\`)"`; diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index cb0d811bada606..effeec1203c066 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -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 ''` diff --git a/packages/vite/src/node/plugins/dynamicImportVars.ts b/packages/vite/src/node/plugins/dynamicImportVars.ts index f0a38246a476f7..a92992800a7473 100644 --- a/packages/vite/src/node/plugins/dynamicImportVars.ts +++ b/packages/vite/src/node/plugins/dynamicImportVars.ts @@ -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, + } } } diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 4a4bb446a2b775..62849bb06e7875 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -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() @@ -43,7 +47,6 @@ function saveEmitWorkerAsset( async function bundleWorkerEntry( config: ResolvedConfig, id: string, - query: Record | null, ): Promise { // 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 | null, chunk: OutputChunk, ): OutputChunk { const { map: sourcemap } = chunk @@ -144,12 +146,11 @@ function encodeWorkerAssetFileName( export async function workerFileToUrl( config: ResolvedConfig, id: string, - query: Record | null, ): Promise { 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 diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index 6b3bf7c0432506..c015dd91068d60 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -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, diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 9b8fd2192adb61..73c3806cc9fd44 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -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 | 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 { diff --git a/playground/worker/__tests__/iife/iife-worker.spec.ts b/playground/worker/__tests__/iife/iife-worker.spec.ts index 01c9eb25edc798..3434fe756194da 100644 --- a/playground/worker/__tests__/iife/iife-worker.spec.ts +++ b/playground/worker/__tests__/iife/iife-worker.spec.ts @@ -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)