diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 21d61b579adbb0..99eef51a4a2bd7 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -3,6 +3,8 @@ import MagicString from 'magic-string' import type { EmittedAsset, OutputChunk, TransformPluginContext } from 'rollup' 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, @@ -10,7 +12,6 @@ import { isRelativeBase, parseRequest } from '../utils' -import { ENV_PUBLIC_PATH } from '../constants' import { onRollupWarning } from '../build' import { fileToUrl } from './asset' @@ -27,7 +28,9 @@ interface WorkerCache { fileNameHash: Map } -const WorkerFileId = 'worker_file' +export type WorkerType = 'classic' | 'module' | 'ignore' + +export const WORKER_FILE_ID = 'worker_file' const workerCache = new WeakMap() function saveEmitWorkerAsset( @@ -188,10 +191,16 @@ export async function workerFileToUrl( export function webWorkerPlugin(config: ResolvedConfig): Plugin { const isBuild = config.command === 'build' + let server: ViteDevServer const isWorker = config.isWorker + return { name: 'vite:worker', + configureServer(_server) { + server = _server + }, + buildStart() { if (isWorker) { return @@ -215,11 +224,30 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { } }, - async transform(_, id) { + async transform(raw, id) { const query = parseRequest(id) - if (query && query[WorkerFileId] != null) { + if (query && query[WORKER_FILE_ID] != null && query['type'] != null) { + const workerType = query['type'] as WorkerType + let injectEnv = '' + + if (workerType === 'classic') { + injectEnv = `importScripts('${ENV_PUBLIC_PATH}')\n` + } else if (workerType === 'module') { + injectEnv = `import '${ENV_PUBLIC_PATH}'\n` + } else if (workerType === 'ignore') { + if (isBuild) { + injectEnv = '' + } else if (server) { + // dynamic worker type we can't know how import the env + // so we copy /@vite/env code of server transform result into file header + const { moduleGraph } = server + const module = moduleGraph.getModuleById(ENV_ENTRY) + injectEnv = module?.transformResult?.code || '' + } + } + return { - code: `import '${ENV_PUBLIC_PATH}'\n` + _ + code: injectEnv + raw } } if ( @@ -259,7 +287,14 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { } } else { url = await fileToUrl(cleanUrl(id), config, this) - url = injectQuery(url, WorkerFileId) + url = injectQuery(url, WORKER_FILE_ID) + } + + if (query.url != null) { + return { + code: `export default ${JSON.stringify(url)}`, + map: { mappings: '' } // Empty sourcemap to suppress Rollup warning + } } const workerConstructor = diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index 92bac1ab997ae1..3e7426f52cda24 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -6,16 +6,12 @@ import { stripLiteral } from 'strip-literal' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' import { cleanUrl, injectQuery, normalizePath, parseRequest } from '../utils' -import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants' -import type { ViteDevServer } from '..' -import { workerFileToUrl } from './worker' +import type { WorkerType } from './worker' +import { WORKER_FILE_ID, workerFileToUrl } from './worker' import { fileToUrl } from './asset' -type WorkerType = 'classic' | 'module' | 'ignore' const ignoreFlagRE = /\/\*\s*@vite-ignore\s*\*\// -const WORKER_FILE_ID = 'worker_url_file' - function getWorkerType(raw: string, clean: string, i: number): WorkerType { function err(e: string, pos: number) { const error = new Error(e) as RollupError @@ -68,41 +64,12 @@ function getWorkerType(raw: string, clean: string, i: number): WorkerType { export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { const isBuild = config.command === 'build' - let server: ViteDevServer return { name: 'vite:worker-import-meta-url', - configureServer(_server) { - server = _server - }, - async transform(code, id, options) { const query = parseRequest(id) - if (query && query[WORKER_FILE_ID] != null && query['type'] != null) { - const workerType = query['type'] as WorkerType - let injectEnv = '' - - if (workerType === 'classic') { - injectEnv = `importScripts('${ENV_PUBLIC_PATH}')\n` - } else if (workerType === 'module') { - injectEnv = `import '${ENV_PUBLIC_PATH}'\n` - } else if (workerType === 'ignore') { - if (isBuild) { - injectEnv = '' - } else if (server) { - // dynamic worker type we can't know how import the env - // so we copy /@vite/env code of server transform result into file header - const { moduleGraph } = server - const module = moduleGraph.getModuleById(ENV_ENTRY) - injectEnv = module?.transformResult?.code || '' - } - } - - return { - code: injectEnv + code - } - } let s: MagicString | undefined if ( (code.includes('new Worker') || code.includes('new SharedWorker')) && diff --git a/playground/worker/__tests__/es/es-worker.spec.ts b/playground/worker/__tests__/es/es-worker.spec.ts index 767fd386606afc..b901a7deaeedbc 100644 --- a/playground/worker/__tests__/es/es-worker.spec.ts +++ b/playground/worker/__tests__/es/es-worker.spec.ts @@ -66,7 +66,7 @@ describe.runIf(isBuild)('build', () => { test('inlined code generation', async () => { const assetsDir = path.resolve(testDir, 'dist/es/assets') const files = fs.readdirSync(assetsDir) - expect(files.length).toBe(25) + expect(files.length).toBe(27) const index = files.find((f) => f.includes('main-module')) const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') const worker = files.find((f) => f.includes('my-worker')) @@ -116,6 +116,12 @@ test('emit chunk', async () => { ) }) +test('url query worker', async () => { + expect(await page.textContent('.simple-worker-url')).toMatch( + 'Hello from simple worker!' + ) +}) + test('import.meta.glob in worker', async () => { expect(await page.textContent('.importMetaGlob-worker')).toMatch('["') }) diff --git a/playground/worker/__tests__/iife/worker.spec.ts b/playground/worker/__tests__/iife/worker.spec.ts index 3e5458bbe9a1a2..0aba9611368737 100644 --- a/playground/worker/__tests__/iife/worker.spec.ts +++ b/playground/worker/__tests__/iife/worker.spec.ts @@ -65,7 +65,7 @@ describe.runIf(isBuild)('build', () => { test('inlined code generation', async () => { const assetsDir = path.resolve(testDir, 'dist/iife/assets') const files = fs.readdirSync(assetsDir) - expect(files.length).toBe(13) + expect(files.length).toBe(15) const index = files.find((f) => f.includes('main-module')) const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') const worker = files.find((f) => f.includes('my-worker')) @@ -106,6 +106,12 @@ test('classic worker', async () => { expect(await page.textContent('.classic-shared-worker')).toMatch('A classic') }) +test('url query worker', async () => { + expect(await page.textContent('.simple-worker-url')).toMatch( + 'Hello from simple worker!' + ) +}) + test('import.meta.glob eager in worker', async () => { expect(await page.textContent('.importMetaGlobEager-worker')).toMatch('["') }) diff --git a/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts b/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts index c60be7016d6477..b0a591ac18dd50 100644 --- a/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts +++ b/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts @@ -9,7 +9,7 @@ describe.runIf(isBuild)('build', () => { const files = fs.readdirSync(assetsDir) // should have 2 worker chunk - expect(files.length).toBe(26) + expect(files.length).toBe(30) const index = files.find((f) => f.includes('main-module')) const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') const indexSourcemap = getSourceMapUrl(content) diff --git a/playground/worker/__tests__/sourcemap-inline/sourcemap-inline-worker.spec.ts b/playground/worker/__tests__/sourcemap-inline/sourcemap-inline-worker.spec.ts index d82185a3f3f26b..d9f4e9826703e9 100644 --- a/playground/worker/__tests__/sourcemap-inline/sourcemap-inline-worker.spec.ts +++ b/playground/worker/__tests__/sourcemap-inline/sourcemap-inline-worker.spec.ts @@ -9,7 +9,7 @@ describe.runIf(isBuild)('build', () => { const files = fs.readdirSync(assetsDir) // should have 2 worker chunk - expect(files.length).toBe(13) + expect(files.length).toBe(15) const index = files.find((f) => f.includes('main-module')) const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') const indexSourcemap = getSourceMapUrl(content) diff --git a/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts b/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts index 3b0a300c5e10f0..af4a074549b167 100644 --- a/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts +++ b/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts @@ -8,7 +8,7 @@ describe.runIf(isBuild)('build', () => { const assetsDir = path.resolve(testDir, 'dist/iife-sourcemap/assets') const files = fs.readdirSync(assetsDir) // should have 2 worker chunk - expect(files.length).toBe(26) + expect(files.length).toBe(30) const index = files.find((f) => f.includes('main-module')) const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') const indexSourcemap = getSourceMapUrl(content) diff --git a/playground/worker/index.html b/playground/worker/index.html index 243f3657ff0161..1fd387d5a9c08b 100644 --- a/playground/worker/index.html +++ b/playground/worker/index.html @@ -72,6 +72,12 @@

format iife:

+

+ new Worker(new URL('../simple-worker.js', import.meta.url).href) + .simple-worker-url +

+ +

use import.meta.glob with eager in iife worker .importMetaGlobEager-worker diff --git a/playground/worker/simple-worker.js b/playground/worker/simple-worker.js new file mode 100644 index 00000000000000..3b7e9a75fd8c5f --- /dev/null +++ b/playground/worker/simple-worker.js @@ -0,0 +1 @@ +self.postMessage('Hello from simple worker!') diff --git a/playground/worker/worker/main-url.js b/playground/worker/worker/main-url.js new file mode 100644 index 00000000000000..ba395926009754 --- /dev/null +++ b/playground/worker/worker/main-url.js @@ -0,0 +1,11 @@ +import workerUrl from '../simple-worker?worker&url' + +function text(el, text) { + document.querySelector(el).textContent = text +} + +const worker = new Worker(workerUrl, { type: 'classic' }) + +worker.addEventListener('message', (ev) => { + text('.simple-worker-url', JSON.stringify(ev.data)) +}) diff --git a/playground/worker/worker/main.js b/playground/worker/worker/main.js index 953b5ef1bf3b53..ce434c38e79ce6 100644 --- a/playground/worker/worker/main.js +++ b/playground/worker/worker/main.js @@ -1,3 +1,4 @@ /* flag: will replace in vite config import("./format-es.js") */ import('./main-module') import('./main-classic') +import('./main-url')