diff --git a/packages/vite/src/node/__tests__/plugins/worker.spec.ts b/packages/vite/src/node/__tests__/plugins/worker.spec.ts index 7fc52aa7f6c608..a6e8319e9fc2b1 100644 --- a/packages/vite/src/node/__tests__/plugins/worker.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/worker.spec.ts @@ -1,10 +1,33 @@ import { resolve } from 'node:path' -import { expect, test } from 'vitest' +import { describe, expect, test } from 'vitest' import type { OutputChunk, RolldownOutput } from 'rolldown' import { build } from '../../build' +import { splitWorkerRequest } from '../../plugins/worker' const fixturesDir = resolve(import.meta.dirname, 'fixtures') +describe('splitWorkerRequest', () => { + for (const [id, postfix] of [ + ['/worker.js', ''], + ['/worker.js#hash', ''], + ['/worker.js?worker', ''], + ['/worker.js?sharedworker', ''], + ['/worker.js?inline', ''], + ['/worker.js?url', ''], + ['/worker.js?worker&url', ''], + ['/worker.js?worker&inline', ''], + ['/worker.js?worker&foo&url&bar', '?foo&bar'], + ['/worker.js?worker&foo&inline', '?foo'], + ['/worker.js?foo&bar', '?foo&bar'], + ['/worker.js?foo=foo&worker&bar=bar', '?foo=foo&bar=bar'], + ['/worker.js?foo&bar&worker', '?foo&bar'], + ]) { + test(`splits ${id}`, () => { + expect(splitWorkerRequest(id)).toEqual({ file: '/worker.js', postfix }) + }) + } +}) + test('?worker&url should produce the same hash in client and SSR builds', async () => { const root = resolve(fixturesDir, 'worker-url') diff --git a/packages/vite/src/node/__tests__/plugins/workerImportMetaUrl.spec.ts b/packages/vite/src/node/__tests__/plugins/workerImportMetaUrl.spec.ts index 348e3e39be0147..517947b1800667 100644 --- a/packages/vite/src/node/__tests__/plugins/workerImportMetaUrl.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/workerImportMetaUrl.spec.ts @@ -253,4 +253,14 @@ new Worker( )" `) }) + + test('preserves custom search params in worker URL', async () => { + expect( + await transform( + 'new Worker(new URL("./worker.js?foo=bar&baz=qux", import.meta.url), { type: "module" })', + ), + ).toMatchInlineSnapshot( + '"new Worker(new URL(/* @vite-ignore */ "/worker.js?worker_file&type=module&foo=bar&baz=qux", \'\' + import.meta.url), { type: "module" })"', + ) + }) }) diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 154c2230a0dbd3..55670a3f43968c 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -19,6 +19,7 @@ import { injectQuery, normalizePath, prettifyUrl, + trailingSeparatorRE, urlRE, } from '../utils' import { @@ -29,7 +30,7 @@ import { onRollupLog, toOutputFilePathInJS, } from '../build' -import { cleanUrl } from '../../shared/utils' +import { cleanUrl, splitFileAndPostfix } from '../../shared/utils' import type { Logger } from '../logger' import { fileToUrl, toOutputFilePathInJSForBundledDev } from './asset' @@ -238,6 +239,24 @@ export const workerOrSharedWorkerRE: RegExp = /(?:\?|&)(worker|sharedworker)(?:&|$)/ const workerFileRE = /(?:\?|&)worker_file&type=(\w+)(?:&|$)/ const inlineRE = /[?&]inline\b/ +const workerQueriesRE = + /(\?|&)(?:(?:worker|sharedworker|inline|url)=?(?:&|$))+/g + +export function splitWorkerRequest(id: string): { + file: string + postfix: string +} { + const { file, postfix } = splitFileAndPostfix(id) + if (!postfix || postfix[0] !== '?') { + return { file, postfix: '' } + } + return { + file, + postfix: postfix + .replace(workerQueriesRE, '$1') + .replace(trailingSeparatorRE, ''), + } +} export const WORKER_FILE_ID = 'worker_file' const workerOutputCaches = new WeakMap() @@ -631,8 +650,12 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { } } } else { - let url = await fileToUrl(this, cleanUrl(id)) - url = injectQuery(url, `${WORKER_FILE_ID}&type=${workerType}`) + const { file, postfix } = splitWorkerRequest(id) + let url = await fileToUrl(this, file) + url = injectQuery( + `${url}${postfix}`, + `${WORKER_FILE_ID}&type=${workerType}`, + ) urlCode = JSON.stringify(url) } diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index 863d0ea47a7935..a80f99e609b6df 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -9,7 +9,7 @@ import type { Plugin } from '../plugin' import { evalValue, injectQuery, transformStableResult } from '../utils' import { createBackCompatIdResolver } from '../idResolver' import type { ResolveIdFn } from '../idResolver' -import { cleanUrl, slash } from '../../shared/utils' +import { cleanUrl, slash, splitFileAndPostfix } from '../../shared/utils' import type { WorkerType } from './worker' import { WORKER_FILE_ID, @@ -234,9 +234,11 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { s ||= new MagicString(code) const workerType = await getWorkerType(code, cleanString, endIndex) const url = rawUrl.slice(1, -1) + const { file: urlWithoutPostfix, postfix } = splitFileAndPostfix(url) + const queryPostfix = postfix[0] === '?' ? postfix : '' let file: string | undefined - if (url[0] === '.') { - file = path.resolve(path.dirname(id), url) + if (urlWithoutPostfix[0] === '.') { + file = path.resolve(path.dirname(id), urlWithoutPostfix) file = slash(tryFsResolve(file, fsResolveOptions) ?? file) } else { workerResolver ??= createBackCompatIdResolver(config, { @@ -244,11 +246,11 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { tryIndex: false, preferRelative: true, }) - file = await workerResolver(this.environment, url, id) + file = await workerResolver(this.environment, urlWithoutPostfix, id) file ??= - url[0] === '/' - ? slash(path.join(config.publicDir, url)) - : slash(path.resolve(path.dirname(id), url)) + urlWithoutPostfix[0] === '/' + ? slash(path.join(config.publicDir, urlWithoutPostfix)) + : slash(path.resolve(path.dirname(id), urlWithoutPostfix)) } if ( @@ -282,7 +284,7 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { } else { builtUrl = await fileToUrl(this, cleanUrl(file)) builtUrl = injectQuery( - builtUrl, + `${builtUrl}${queryPostfix}`, `${WORKER_FILE_ID}&type=${workerType}`, ) } diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 779020aaa0fda9..d3856602ebe2eb 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -314,7 +314,7 @@ const internalPrefixes = [ ENV_PUBLIC_PATH, ] const InternalPrefixRE = new RegExp(`^(?:${internalPrefixes.join('|')})`) -const trailingSeparatorRE = /[?&]$/ +export const trailingSeparatorRE: RegExp = /[?&]$/ export const isImportRequest = (url: string): boolean => importQueryRE.test(url) export const isInternalRequest = (url: string): boolean => InternalPrefixRE.test(url)