diff --git a/docs/config/worker-options.md b/docs/config/worker-options.md index b0896cb10aa507..04c3d60c5ce746 100644 --- a/docs/config/worker-options.md +++ b/docs/config/worker-options.md @@ -14,7 +14,7 @@ Output format for worker bundle. - **Type:** `'blob' | 'base64'` - **Default:** `'blob'` -URL type for inline worker, when setting to `'blob'`, the worker will be loaded as a blob URL with a fallback to data URL. Set to `'base64'` to load the worker as a data URL. +URL type for inline worker, when setting to `'blob'`, the worker will be loaded as a blob URL with a fallback to data URL. Set to `'base64'` to load the worker as a data URL. When not set, `inlineUrl` will be `'blob'` for Worker and `'base64'` for SharedWorker as blob URL not works with a shared worker. ## worker.plugins diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 0df29b7e348cf9..3f1176fb8cb2df 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -320,9 +320,9 @@ export interface LegacyOptions { export interface ResolveWorkerOptions extends PluginHookUtils { format: 'es' | 'iife' - inlineUrl: 'blob' | 'base64' plugins: Plugin[] rollupOptions: RollupOptions + inlineUrl?: 'blob' | 'base64' } export interface InlineConfig extends UserConfig { @@ -637,7 +637,7 @@ export async function resolveConfig( workerConfig = await runConfigHook(workerConfig, workerUserPlugins, configEnv) const resolvedWorkerOptions: ResolveWorkerOptions = { format: workerConfig.worker?.format || 'iife', - inlineUrl: workerConfig.worker?.inlineUrl || 'blob', + inlineUrl: workerConfig.worker?.inlineUrl, plugins: [], rollupOptions: workerConfig.worker?.rollupOptions || {}, getSortedPlugins: undefined!, diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 18ac599890ef84..c09e9d08526736 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -1,4 +1,5 @@ import path from 'node:path' +import colors from 'picocolors' import MagicString from 'magic-string' import type { EmittedAsset, OutputChunk } from 'rollup' import type { ResolvedConfig } from '../config' @@ -281,6 +282,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { const encodedJs = `const encodedJs = "${Buffer.from( chunk.code, ).toString('base64')}";` + const blobCode = `${encodedJs} const blob = typeof window !== "undefined" && window.Blob && new Blob([atob(encodedJs)], { type: "text/javascript;charset=utf-8" }); export default function WorkerWrapper() { @@ -298,9 +300,26 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { } ` - // inline as blob data url + // If inlineUrl is not specified, use base64 for SharedWorker and blob for Worker + const resolvedInlineUrl = inlineUrl + ? inlineUrl + : workerConstructor === 'SharedWorker' + ? 'base64' + : 'blob' + + if ( + workerConstructor === 'SharedWorker' && + resolvedInlineUrl === 'blob' + ) { + config.logger.warn( + colors.yellow( + `\nThe inlined SharedWorker: ${id} does not work with blob URL, considering set 'worker.inlineUrl' to 'base64'. \n`, + ), + ) + } + return { - code: inlineUrl === 'blob' ? blobCode : base64Code, + code: resolvedInlineUrl === 'blob' ? blobCode : base64Code, // Empty sourcemap to suppress Rollup warning map: { mappings: '' }, } diff --git a/playground/worker/__tests__/es/es-worker.spec.ts b/playground/worker/__tests__/es/es-worker.spec.ts index 754aa4584b48e2..d9c00892d8f862 100644 --- a/playground/worker/__tests__/es/es-worker.spec.ts +++ b/playground/worker/__tests__/es/es-worker.spec.ts @@ -76,7 +76,7 @@ describe.runIf(isBuild)('build', () => { expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`) expect(content).toMatch(`window.Blob`) expect(content).toMatch( - /try\{return e\?new Worker\(.+\):new Worker\("data:application\/javascript;base64,"\+/, + /try\{return .*\?new Worker\(.+\):new Worker\("data:application\/javascript;base64,"\+/, ) }) diff --git a/playground/worker/__tests__/inline-url/inline-url-worker.spec.ts b/playground/worker/__tests__/inline-url/inline-url-worker.spec.ts index de66ee5d1a3d3c..d59832a5325faf 100644 --- a/playground/worker/__tests__/inline-url/inline-url-worker.spec.ts +++ b/playground/worker/__tests__/inline-url/inline-url-worker.spec.ts @@ -11,9 +11,14 @@ describe.runIf(isBuild)('build', () => { const index = files.find((f) => f.includes('main-module')) const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') - // inlined + // inline worker expect(content).toMatch( - `return new Worker("data:application/javascript;base64,"+`, + /try\{return .*\?new Worker\(.+\):new Worker\("data:application\/javascript;base64,"\+/, + ) + + // inline sharedworker + expect(content).toMatch( + `return new SharedWorker("data:application/javascript;base64,"+`, ) }) }) diff --git a/playground/worker/__tests__/inline-url/vite.config.js b/playground/worker/__tests__/inline-url/vite.config.js index 8676577f7bce4b..c6c9d32e918015 100644 --- a/playground/worker/__tests__/inline-url/vite.config.js +++ b/playground/worker/__tests__/inline-url/vite.config.js @@ -1 +1 @@ -module.exports = require('../../vite.config-inline-url') +export { default } from '../../vite.config-inline-url' diff --git a/playground/worker/index.html b/playground/worker/index.html index 5c9c85b4799fd5..ed7079ae8dd38c 100644 --- a/playground/worker/index.html +++ b/playground/worker/index.html @@ -26,6 +26,12 @@

format iife:

+

+ import InlineSharedWorker from '../my-shared-worker?sharedworker&inline' + .pong-shared-inline +

+ +

import TSOutputWorker from '../possible-ts-output-worker?worker' .pong-ts-output diff --git a/playground/worker/my-shared-worker.ts b/playground/worker/my-shared-worker.ts index 552c3e20f57853..0062ec2636089f 100644 --- a/playground/worker/my-shared-worker.ts +++ b/playground/worker/my-shared-worker.ts @@ -1,14 +1,11 @@ -const ports = new Set() +let sharedWorkerCount = 0 // @ts-expect-error onconnect exists in worker self.onconnect = (event) => { + sharedWorkerCount++ const port = event.ports[0] - ports.add(port) - port.postMessage('pong') - port.onmessage = () => { - ports.forEach((p: any) => { - p.postMessage('pong') - }) + if (sharedWorkerCount === 2) { + port.postMessage('pong') } } diff --git a/playground/worker/vite.config-es.js b/playground/worker/vite.config-es.js index 329377ee37732a..3adb2b157f671d 100644 --- a/playground/worker/vite.config-es.js +++ b/playground/worker/vite.config-es.js @@ -1,6 +1,8 @@ import vite from 'vite' import workerPluginTestPlugin from './worker-plugin-test-plugin' +/** @type {import('vite').UserConfig} */ +// @ts-expect-error typecast export default vite.defineConfig({ base: '/es/', resolve: { diff --git a/playground/worker/vite.config-inline-url.js b/playground/worker/vite.config-inline-url.js index cf68bf6499d653..b6592d9ae7fe4f 100644 --- a/playground/worker/vite.config-inline-url.js +++ b/playground/worker/vite.config-inline-url.js @@ -1,5 +1,11 @@ -const config = require('./vite.config-es') -config.worker.inlineUrl = 'base64' -config.base = '/inline-url/' -config.build.outDir = 'dist/inline-url' -module.exports = config +import { defineConfig } from 'vite' +import config from './vite.config-es' + +export default defineConfig({ + ...config, + base: '/inline-url/', + build: { + ...config.build, + outDir: 'dist/inline-url', + }, +}) diff --git a/playground/worker/worker/main-module.js b/playground/worker/worker/main-module.js index 1e5242935cdbb8..effad0c33d7ce3 100644 --- a/playground/worker/worker/main-module.js +++ b/playground/worker/worker/main-module.js @@ -1,5 +1,6 @@ import myWorker from '../my-worker.ts?worker' import InlineWorker from '../my-worker.ts?worker&inline' +import InlineSharedWorker from '../my-shared-worker?sharedworker&inline' import mySharedWorker from '../my-shared-worker?sharedworker&name=shared' import TSOutputWorker from '../possible-ts-output-worker?worker' import NestedWorker from '../worker-nested-worker?worker' @@ -26,11 +27,26 @@ inlineWorker.addEventListener('message', (e) => { text('.pong-inline', e.data.msg) }) -const sharedWorker = new mySharedWorker() -sharedWorker.port.addEventListener('message', (event) => { - text('.tick-count', event.data) -}) -sharedWorker.port.start() +const startSharedWorker = () => { + const sharedWorker = new mySharedWorker() + sharedWorker.port.addEventListener('message', (event) => { + text('.tick-count', event.data) + }) + sharedWorker.port.start() +} +startSharedWorker() +startSharedWorker() + +const startInlineSharedWorker = () => { + const inlineSharedWorker = new InlineSharedWorker() + inlineSharedWorker.port.addEventListener('message', (event) => { + text('.pong-shared-inline', event.data) + }) + inlineSharedWorker.port.start() +} + +startInlineSharedWorker() +startInlineSharedWorker() const tsOutputWorker = new TSOutputWorker() tsOutputWorker.postMessage('ping')