From e03cc1a55572459b474fd21af91dbd5e6cbc29ec Mon Sep 17 00:00:00 2001 From: fi3ework Date: Fri, 10 Feb 2023 17:12:04 +0800 Subject: [PATCH 01/10] feat(worker): support inlineUrl of worker config --- docs/config/worker-options.md | 9 ++++++++- packages/vite/src/node/config.ts | 7 +++++++ packages/vite/src/node/plugins/worker.ts | 6 ++++-- .../worker/__tests__/es/es-worker.spec.ts | 3 +++ .../inline-url/inline-url-worker.spec.ts | 19 +++++++++++++++++++ .../__tests__/inline-url/vite.config.js | 1 + playground/worker/package.json | 3 +++ playground/worker/vite.config-inline-url.js | 5 +++++ 8 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 playground/worker/__tests__/inline-url/inline-url-worker.spec.ts create mode 100644 playground/worker/__tests__/inline-url/vite.config.js create mode 100644 playground/worker/vite.config-inline-url.js diff --git a/docs/config/worker-options.md b/docs/config/worker-options.md index 840caa60915ba2..46a8a4f33760ad 100644 --- a/docs/config/worker-options.md +++ b/docs/config/worker-options.md @@ -5,10 +5,17 @@ Options related to Web Workers. ## worker.format - **Type:** `'es' | 'iife'` -- **Default:** `iife` +- **Default:** `'iife'` Output format for worker bundle. +## worker.inlineUrl + +- **Type:** `'blob' | 'data'` +- **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 `'data'` to load the worker as a data URL. + ## worker.plugins - **Type:** [`(Plugin | Plugin[])[]`](./shared-options#plugins) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index fb56c92bca3ccf..2caccd4d63cacc 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -250,6 +250,11 @@ export interface UserConfig { * @default 'iife' */ format?: 'es' | 'iife' + /** + * URL type for inline worker + * @default 'blob' + */ + inlineUrl?: 'blob' | 'data' /** * Vite plugins that apply to worker bundle */ @@ -315,6 +320,7 @@ export interface LegacyOptions { export interface ResolveWorkerOptions extends PluginHookUtils { format: 'es' | 'iife' + inlineUrl: 'blob' | 'data' plugins: Plugin[] rollupOptions: RollupOptions } @@ -633,6 +639,7 @@ export async function resolveConfig( workerConfig = await runConfigHook(workerConfig, workerUserPlugins, configEnv) const resolvedWorkerOptions: ResolveWorkerOptions = { format: workerConfig.worker?.format || 'iife', + inlineUrl: workerConfig.worker?.inlineUrl || 'blob', 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 aae1a4a1423cda..1f7dbf8c2b4cab 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -278,7 +278,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { // stringified url or `new URL(...)` let url: string - const { format } = config.worker + const { format, inlineUrl } = config.worker const workerConstructor = query.sharedworker != null ? 'SharedWorker' : 'Worker' const workerType = isBuild @@ -300,7 +300,9 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { export default function WorkerWrapper() { const objURL = blob && (window.URL || window.webkitURL).createObjectURL(blob); try { - return objURL ? new ${workerConstructor}(objURL) : new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions}); + return objURL && ${ + inlineUrl === 'blob' + } ? new ${workerConstructor}(objURL) : new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions}); } finally { objURL && (window.URL || window.webkitURL).revokeObjectURL(objURL); } diff --git a/playground/worker/__tests__/es/es-worker.spec.ts b/playground/worker/__tests__/es/es-worker.spec.ts index 817198c23a07a4..754aa4584b48e2 100644 --- a/playground/worker/__tests__/es/es-worker.spec.ts +++ b/playground/worker/__tests__/es/es-worker.spec.ts @@ -75,6 +75,9 @@ describe.runIf(isBuild)('build', () => { // inlined 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,"\+/, + ) }) test('worker emitted and import.meta.url in nested worker (build)', async () => { diff --git a/playground/worker/__tests__/inline-url/inline-url-worker.spec.ts b/playground/worker/__tests__/inline-url/inline-url-worker.spec.ts new file mode 100644 index 00000000000000..03965f86c14f44 --- /dev/null +++ b/playground/worker/__tests__/inline-url/inline-url-worker.spec.ts @@ -0,0 +1,19 @@ +import fs from 'node:fs' +import path from 'node:path' +import { describe, expect, test } from 'vitest' +import { isBuild, testDir } from '~utils' + +describe.runIf(isBuild)('build', () => { + // assert correct files + test('inlined code generation', async () => { + const assetsDir = path.resolve(testDir, 'dist/inline-url/assets') + const files = fs.readdirSync(assetsDir) + const index = files.find((f) => f.includes('main-module')) + const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') + + // inlined + expect(content).toMatch( + `;try{return new Worker("data:application/javascript;base64,"+`, + ) + }) +}) diff --git a/playground/worker/__tests__/inline-url/vite.config.js b/playground/worker/__tests__/inline-url/vite.config.js new file mode 100644 index 00000000000000..8676577f7bce4b --- /dev/null +++ b/playground/worker/__tests__/inline-url/vite.config.js @@ -0,0 +1 @@ +module.exports = require('../../vite.config-inline-url') diff --git a/playground/worker/package.json b/playground/worker/package.json index 3299b9a57556a5..da4bb0c5c1ef70 100644 --- a/playground/worker/package.json +++ b/playground/worker/package.json @@ -21,6 +21,9 @@ "dev:relative-base": "WORKER_MODE=inline vite --config ./vite.config-relative-base.js dev", "build:relative-base": "WORKER_MODE=inline vite --config ./vite.config-relative-base.js build", "preview:relative-base": "WORKER_MODE=inline vite --config ./vite.config-relative-base.js preview", + "dev:inline-url": "vite --config ./vite.config-inline-url.js dev", + "build:inline-url": "vite --config ./vite.config-inline-url.js build", + "preview:inline-url": "vite --config ./vite.config-inline-url.js preview", "debug": "node --inspect-brk ../../packages/vite/bin/vite" }, "dependencies": { diff --git a/playground/worker/vite.config-inline-url.js b/playground/worker/vite.config-inline-url.js new file mode 100644 index 00000000000000..a890dbbe472832 --- /dev/null +++ b/playground/worker/vite.config-inline-url.js @@ -0,0 +1,5 @@ +const config = require('./vite.config-es') +config.worker.inlineUrl = 'data' +config.base = '/inline-url/' +config.build.outDir = 'dist/inline-url' +module.exports = config From ccc8fd5b6d315fb83894a8d26f7a95618f9846ac Mon Sep 17 00:00:00 2001 From: fi3ework Date: Fri, 24 Feb 2023 20:56:26 +0800 Subject: [PATCH 02/10] refactor: change 'data' to 'base64' --- docs/config/worker-options.md | 4 ++-- packages/vite/src/node/config.ts | 4 ++-- playground/worker/vite.config-inline-url.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/config/worker-options.md b/docs/config/worker-options.md index 46a8a4f33760ad..b0896cb10aa507 100644 --- a/docs/config/worker-options.md +++ b/docs/config/worker-options.md @@ -11,10 +11,10 @@ Output format for worker bundle. ## worker.inlineUrl -- **Type:** `'blob' | 'data'` +- **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 `'data'` 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. ## worker.plugins diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 2caccd4d63cacc..5594fd259ca536 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -254,7 +254,7 @@ export interface UserConfig { * URL type for inline worker * @default 'blob' */ - inlineUrl?: 'blob' | 'data' + inlineUrl?: 'blob' | 'base64' /** * Vite plugins that apply to worker bundle */ @@ -320,7 +320,7 @@ export interface LegacyOptions { export interface ResolveWorkerOptions extends PluginHookUtils { format: 'es' | 'iife' - inlineUrl: 'blob' | 'data' + inlineUrl: 'blob' | 'base64' plugins: Plugin[] rollupOptions: RollupOptions } diff --git a/playground/worker/vite.config-inline-url.js b/playground/worker/vite.config-inline-url.js index a890dbbe472832..cf68bf6499d653 100644 --- a/playground/worker/vite.config-inline-url.js +++ b/playground/worker/vite.config-inline-url.js @@ -1,5 +1,5 @@ const config = require('./vite.config-es') -config.worker.inlineUrl = 'data' +config.worker.inlineUrl = 'base64' config.base = '/inline-url/' config.build.outDir = 'dist/inline-url' module.exports = config From 6309d3487a6ac707ee33b560b1b630e47e3bfe1e Mon Sep 17 00:00:00 2001 From: fi3ework Date: Fri, 24 Feb 2023 22:36:56 +0800 Subject: [PATCH 03/10] fix: separate blob and base64 code --- packages/vite/src/node/plugins/worker.ts | 36 +++++++++++-------- .../inline-url/inline-url-worker.spec.ts | 2 +- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 1f7dbf8c2b4cab..96babefb9ce820 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -291,23 +291,29 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { getDepsOptimizer(config, ssr)?.registerWorkersSource(id) if (query.inline != null) { const chunk = await bundleWorkerEntry(config, id, query) + 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() { + const objURL = blob && (window.URL || window.webkitURL).createObjectURL(blob); + try { + return objURL ? new ${workerConstructor}(objURL) : new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions}); + } finally { + objURL && (window.URL || window.webkitURL).revokeObjectURL(objURL); + } + }` + + const base64Code = `${encodedJs} + export default function WorkerWrapper() { + return new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions}); + } + ` + // inline as blob data url return { - code: `const encodedJs = "${Buffer.from(chunk.code).toString( - 'base64', - )}"; - const blob = typeof window !== "undefined" && window.Blob && new Blob([atob(encodedJs)], { type: "text/javascript;charset=utf-8" }); - export default function WorkerWrapper() { - const objURL = blob && (window.URL || window.webkitURL).createObjectURL(blob); - try { - return objURL && ${ - inlineUrl === 'blob' - } ? new ${workerConstructor}(objURL) : new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions}); - } finally { - objURL && (window.URL || window.webkitURL).revokeObjectURL(objURL); - } - }`, - + code: inlineUrl === 'blob' ? blobCode : base64Code, // Empty sourcemap to suppress Rollup warning map: { mappings: '' }, } 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 03965f86c14f44..de66ee5d1a3d3c 100644 --- a/playground/worker/__tests__/inline-url/inline-url-worker.spec.ts +++ b/playground/worker/__tests__/inline-url/inline-url-worker.spec.ts @@ -13,7 +13,7 @@ describe.runIf(isBuild)('build', () => { // inlined expect(content).toMatch( - `;try{return new Worker("data:application/javascript;base64,"+`, + `return new Worker("data:application/javascript;base64,"+`, ) }) }) From 3e12174bf2bc689af32a0898662fbe0ae5889637 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Wed, 8 Mar 2023 01:31:39 +0800 Subject: [PATCH 04/10] fix: correct shared worker test --- docs/config/worker-options.md | 2 +- packages/vite/src/node/config.ts | 4 +-- packages/vite/src/node/plugins/worker.ts | 23 ++++++++++++++-- .../worker/__tests__/es/es-worker.spec.ts | 2 +- .../inline-url/inline-url-worker.spec.ts | 10 ++++++- .../__tests__/inline-url/vite.config.js | 2 +- playground/worker/index.html | 6 +++++ playground/worker/my-shared-worker.ts | 11 +++----- playground/worker/vite.config-es.js | 2 ++ playground/worker/vite.config-inline-url.js | 20 ++++++++++---- playground/worker/worker/main-module.js | 26 +++++++++++++++---- 11 files changed, 83 insertions(+), 25 deletions(-) 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 5594fd259ca536..8b551ef4668e0e 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 { @@ -639,7 +639,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 96babefb9ce820..6a0684844823de 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' @@ -294,6 +295,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() { @@ -311,9 +313,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..9236c8c136bdf7 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,17 @@ 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 in base64 expect(content).toMatch( `return new Worker("data:application/javascript;base64,"+`, ) + + // inline sharedworker in base64 + expect(content).toMatch( + `return new SharedWorker("data:application/javascript;base64,"+`, + ) + + // no blob URL inline worker + expect(content).not.toMatch(`window.Blob`) }) }) 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..cd1062b6e7b59c 100644 --- a/playground/worker/vite.config-inline-url.js +++ b/playground/worker/vite.config-inline-url.js @@ -1,5 +1,15 @@ -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/', + worker: { + ...config.worker, + inlineUrl: 'base64', + }, + 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') From c5164d291179512787acc269b599279096d0f222 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Thu, 9 Mar 2023 02:07:03 +0800 Subject: [PATCH 05/10] test: add more --- playground/worker/__tests__/es/es-worker.spec.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/playground/worker/__tests__/es/es-worker.spec.ts b/playground/worker/__tests__/es/es-worker.spec.ts index d9c00892d8f862..3f150578097c75 100644 --- a/playground/worker/__tests__/es/es-worker.spec.ts +++ b/playground/worker/__tests__/es/es-worker.spec.ts @@ -72,12 +72,16 @@ describe.runIf(isBuild)('build', () => { // chunk expect(content).toMatch(`new Worker("/es/assets`) expect(content).toMatch(`new SharedWorker("/es/assets`) - // inlined + // inlined worker expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`) expect(content).toMatch(`window.Blob`) expect(content).toMatch( /try\{return .*\?new Worker\(.+\):new Worker\("data:application\/javascript;base64,"\+/, ) + // inlined shared worker + expect(content).toMatch( + `return new SharedWorker("data:application/javascript;base64,"+`, + ) }) test('worker emitted and import.meta.url in nested worker (build)', async () => { From edfb5a842b01d419e7036e168e2f41b45da63084 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Mon, 13 Mar 2023 00:52:02 +0800 Subject: [PATCH 06/10] chore: remove inlineUrl --- docs/config/worker-options.md | 7 ----- packages/vite/src/node/config.ts | 7 ----- packages/vite/src/node/plugins/worker.ts | 25 +++-------------- .../worker/__tests__/es/es-worker.spec.ts | 4 +++ .../worker/__tests__/iife/iife-worker.spec.ts | 4 +++ .../inline-url/inline-url-worker.spec.ts | 27 ------------------- .../__tests__/inline-url/vite.config.js | 1 - .../relative-base-worker.spec.ts | 4 +++ .../sourcemap-hidden-worker.spec.ts | 2 +- .../sourcemap/sourcemap-worker.spec.ts | 2 +- playground/worker/index.html | 12 ++++----- playground/worker/my-inline-shared-worker.ts | 13 +++++++++ playground/worker/my-shared-worker.ts | 2 +- playground/worker/package.json | 3 --- playground/worker/vite.config-es.js | 2 -- playground/worker/vite.config-inline-url.js | 15 ----------- playground/worker/worker/main-module.js | 2 +- 17 files changed, 39 insertions(+), 93 deletions(-) delete mode 100644 playground/worker/__tests__/inline-url/inline-url-worker.spec.ts delete mode 100644 playground/worker/__tests__/inline-url/vite.config.js create mode 100644 playground/worker/my-inline-shared-worker.ts delete mode 100644 playground/worker/vite.config-inline-url.js diff --git a/docs/config/worker-options.md b/docs/config/worker-options.md index 04c3d60c5ce746..99500ae259ccfe 100644 --- a/docs/config/worker-options.md +++ b/docs/config/worker-options.md @@ -9,13 +9,6 @@ Options related to Web Workers. Output format for worker bundle. -## worker.inlineUrl - -- **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. When not set, `inlineUrl` will be `'blob'` for Worker and `'base64'` for SharedWorker as blob URL not works with a shared worker. - ## worker.plugins - **Type:** [`(Plugin | Plugin[])[]`](./shared-options#plugins) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 8b551ef4668e0e..fb56c92bca3ccf 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -250,11 +250,6 @@ export interface UserConfig { * @default 'iife' */ format?: 'es' | 'iife' - /** - * URL type for inline worker - * @default 'blob' - */ - inlineUrl?: 'blob' | 'base64' /** * Vite plugins that apply to worker bundle */ @@ -322,7 +317,6 @@ export interface ResolveWorkerOptions extends PluginHookUtils { format: 'es' | 'iife' plugins: Plugin[] rollupOptions: RollupOptions - inlineUrl?: 'blob' | 'base64' } export interface InlineConfig extends UserConfig { @@ -639,7 +633,6 @@ export async function resolveConfig( workerConfig = await runConfigHook(workerConfig, workerUserPlugins, configEnv) const resolvedWorkerOptions: ResolveWorkerOptions = { format: workerConfig.worker?.format || 'iife', - 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 6a0684844823de..b5ee7e2a661e95 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -1,5 +1,4 @@ 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' @@ -279,7 +278,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { // stringified url or `new URL(...)` let url: string - const { format, inlineUrl } = config.worker + const { format } = config.worker const workerConstructor = query.sharedworker != null ? 'SharedWorker' : 'Worker' const workerType = isBuild @@ -288,6 +287,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { : 'classic' : 'module' const workerOptions = workerType === 'classic' ? '' : ',{type: "module"}' + if (isBuild) { getDepsOptimizer(config, ssr)?.registerWorkersSource(id) if (query.inline != null) { @@ -313,26 +313,9 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { } ` - // 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: resolvedInlineUrl === 'blob' ? blobCode : base64Code, + // SharedWorker does not support blob URL + code: workerConstructor === 'Worker' ? 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 3f150578097c75..58add957e522eb 100644 --- a/playground/worker/__tests__/es/es-worker.spec.ts +++ b/playground/worker/__tests__/es/es-worker.spec.ts @@ -34,6 +34,10 @@ test('shared worker', async () => { await untilUpdated(() => page.textContent('.tick-count'), 'pong', true) }) +test('inline shared worker', async () => { + await untilUpdated(() => page.textContent('.pong-shared-inline'), 'pong') +}) + test('worker emitted and import.meta.url in nested worker (serve)', async () => { await untilUpdated( () => page.textContent('.nested-worker'), diff --git a/playground/worker/__tests__/iife/iife-worker.spec.ts b/playground/worker/__tests__/iife/iife-worker.spec.ts index 5e81c84a777985..f37c70643f5fc4 100644 --- a/playground/worker/__tests__/iife/iife-worker.spec.ts +++ b/playground/worker/__tests__/iife/iife-worker.spec.ts @@ -29,6 +29,10 @@ test('shared worker', async () => { await untilUpdated(() => page.textContent('.tick-count'), 'pong') }) +test('inline shared worker', async () => { + await untilUpdated(() => page.textContent('.pong-shared-inline'), 'pong') +}) + test('worker emitted and import.meta.url in nested worker (serve)', async () => { await untilUpdated(() => page.textContent('.nested-worker'), '/worker-nested') await untilUpdated( diff --git a/playground/worker/__tests__/inline-url/inline-url-worker.spec.ts b/playground/worker/__tests__/inline-url/inline-url-worker.spec.ts deleted file mode 100644 index 9236c8c136bdf7..00000000000000 --- a/playground/worker/__tests__/inline-url/inline-url-worker.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import fs from 'node:fs' -import path from 'node:path' -import { describe, expect, test } from 'vitest' -import { isBuild, testDir } from '~utils' - -describe.runIf(isBuild)('build', () => { - // assert correct files - test('inlined code generation', async () => { - const assetsDir = path.resolve(testDir, 'dist/inline-url/assets') - const files = fs.readdirSync(assetsDir) - const index = files.find((f) => f.includes('main-module')) - const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') - - // inline worker in base64 - expect(content).toMatch( - `return new Worker("data:application/javascript;base64,"+`, - ) - - // inline sharedworker in base64 - expect(content).toMatch( - `return new SharedWorker("data:application/javascript;base64,"+`, - ) - - // no blob URL inline worker - expect(content).not.toMatch(`window.Blob`) - }) -}) diff --git a/playground/worker/__tests__/inline-url/vite.config.js b/playground/worker/__tests__/inline-url/vite.config.js deleted file mode 100644 index c6c9d32e918015..00000000000000 --- a/playground/worker/__tests__/inline-url/vite.config.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '../../vite.config-inline-url' diff --git a/playground/worker/__tests__/relative-base/relative-base-worker.spec.ts b/playground/worker/__tests__/relative-base/relative-base-worker.spec.ts index db8284a46b2863..87bb5e4ab338d9 100644 --- a/playground/worker/__tests__/relative-base/relative-base-worker.spec.ts +++ b/playground/worker/__tests__/relative-base/relative-base-worker.spec.ts @@ -35,6 +35,10 @@ test('shared worker', async () => { await untilUpdated(() => page.textContent('.tick-count'), 'pong', true) }) +test('inline shared worker', async () => { + await untilUpdated(() => page.textContent('.pong-shared-inline'), 'pong') +}) + test('worker emitted and import.meta.url in nested worker (serve)', async () => { await untilUpdated( () => page.textContent('.nested-worker'), 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 afcb51af117729..2565e941962f63 100644 --- a/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts +++ b/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts @@ -10,7 +10,7 @@ describe.runIf(isBuild)('build', () => { const files = fs.readdirSync(assetsDir) // should have 2 worker chunk - expect(files.length).toBe(31) + expect(files.length).toBe(32) 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 04f272b1487a8c..837c118a6ebaf3 100644 --- a/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts +++ b/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts @@ -9,7 +9,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(31) + expect(files.length).toBe(32) 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 ed7079ae8dd38c..ff26fbcbba3206 100644 --- a/playground/worker/index.html +++ b/playground/worker/index.html @@ -26,12 +26,6 @@

format iife:

-

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

- -

import TSOutputWorker from '../possible-ts-output-worker?worker' .pong-ts-output @@ -44,6 +38,12 @@

format iife:

+

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

+ +

new Worker(new URL('./url-worker.js', import.meta.url), { type: 'module' }) .worker-import-meta-url diff --git a/playground/worker/my-inline-shared-worker.ts b/playground/worker/my-inline-shared-worker.ts new file mode 100644 index 00000000000000..580c6cf4fdb9b6 --- /dev/null +++ b/playground/worker/my-inline-shared-worker.ts @@ -0,0 +1,13 @@ +let inlineSharedWorkerCount = 0 + +// @ts-expect-error onconnect exists in worker +self.onconnect = (event) => { + inlineSharedWorkerCount++ + const port = event.ports[0] + if (inlineSharedWorkerCount >= 2) { + port.postMessage('pong') + } +} + +// for sourcemap +console.log('my-inline-shared-worker.js') diff --git a/playground/worker/my-shared-worker.ts b/playground/worker/my-shared-worker.ts index 0062ec2636089f..d9728304191085 100644 --- a/playground/worker/my-shared-worker.ts +++ b/playground/worker/my-shared-worker.ts @@ -4,7 +4,7 @@ let sharedWorkerCount = 0 self.onconnect = (event) => { sharedWorkerCount++ const port = event.ports[0] - if (sharedWorkerCount === 2) { + if (sharedWorkerCount >= 2) { port.postMessage('pong') } } diff --git a/playground/worker/package.json b/playground/worker/package.json index da4bb0c5c1ef70..3299b9a57556a5 100644 --- a/playground/worker/package.json +++ b/playground/worker/package.json @@ -21,9 +21,6 @@ "dev:relative-base": "WORKER_MODE=inline vite --config ./vite.config-relative-base.js dev", "build:relative-base": "WORKER_MODE=inline vite --config ./vite.config-relative-base.js build", "preview:relative-base": "WORKER_MODE=inline vite --config ./vite.config-relative-base.js preview", - "dev:inline-url": "vite --config ./vite.config-inline-url.js dev", - "build:inline-url": "vite --config ./vite.config-inline-url.js build", - "preview:inline-url": "vite --config ./vite.config-inline-url.js preview", "debug": "node --inspect-brk ../../packages/vite/bin/vite" }, "dependencies": { diff --git a/playground/worker/vite.config-es.js b/playground/worker/vite.config-es.js index 3adb2b157f671d..329377ee37732a 100644 --- a/playground/worker/vite.config-es.js +++ b/playground/worker/vite.config-es.js @@ -1,8 +1,6 @@ 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 deleted file mode 100644 index cd1062b6e7b59c..00000000000000 --- a/playground/worker/vite.config-inline-url.js +++ /dev/null @@ -1,15 +0,0 @@ -import { defineConfig } from 'vite' -import config from './vite.config-es' - -export default defineConfig({ - ...config, - base: '/inline-url/', - worker: { - ...config.worker, - inlineUrl: 'base64', - }, - build: { - ...config.build, - outDir: 'dist/inline-url', - }, -}) diff --git a/playground/worker/worker/main-module.js b/playground/worker/worker/main-module.js index effad0c33d7ce3..e19f8f4ec3af3a 100644 --- a/playground/worker/worker/main-module.js +++ b/playground/worker/worker/main-module.js @@ -1,6 +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 InlineSharedWorker from '../my-inline-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' From 099109034abddd28dad3218a564376f2472228e3 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Wed, 15 Mar 2023 00:08:43 +0800 Subject: [PATCH 07/10] fix: try catch createObjectURL fail --- packages/vite/src/node/plugins/worker.ts | 10 +++++++--- playground/worker/__tests__/es/es-worker.spec.ts | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index b5ee7e2a661e95..fd93cd0c520583 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -299,9 +299,13 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { const blobCode = `${encodedJs} const blob = typeof window !== "undefined" && window.Blob && new Blob([atob(encodedJs)], { type: "text/javascript;charset=utf-8" }); export default function WorkerWrapper() { - const objURL = blob && (window.URL || window.webkitURL).createObjectURL(blob); + let objURL; try { - return objURL ? new ${workerConstructor}(objURL) : new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions}); + objURL = blob && (window.URL || window.webkitURL).createObjectURL(blob); + if (objURL) throw '' + return new ${workerConstructor}(objURL) + } catch(e) { + return new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions}); } finally { objURL && (window.URL || window.webkitURL).revokeObjectURL(objURL); } @@ -314,7 +318,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { ` return { - // SharedWorker does not support blob URL + // Using blob URL for SharedWorker results in multiple instances of a same worker code: workerConstructor === 'Worker' ? 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 58add957e522eb..cabe43a6fdc5b0 100644 --- a/playground/worker/__tests__/es/es-worker.spec.ts +++ b/playground/worker/__tests__/es/es-worker.spec.ts @@ -80,7 +80,7 @@ describe.runIf(isBuild)('build', () => { expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`) expect(content).toMatch(`window.Blob`) expect(content).toMatch( - /try\{return .*\?new Worker\(.+\):new Worker\("data:application\/javascript;base64,"\+/, + /try\{if\(\w+=\w+&&\(window\.URL\|\|window\.webkitURL\)\.createObjectURL\(\w+\),\w+\)throw""/, ) // inlined shared worker expect(content).toMatch( From a1fd67650444587e34bc294d9ca2d4d15d790a9b Mon Sep 17 00:00:00 2001 From: fi3ework Date: Fri, 17 Mar 2023 23:50:01 +0800 Subject: [PATCH 08/10] refactor: clean code --- packages/vite/src/node/plugins/worker.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index fd93cd0c520583..3fcfdbc8300225 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -296,7 +296,10 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { chunk.code, ).toString('base64')}";` - const blobCode = `${encodedJs} + const code = + // Using blob URL for SharedWorker results in multiple instances of a same worker + workerConstructor === 'Worker' + ? `${encodedJs} const blob = typeof window !== "undefined" && window.Blob && new Blob([atob(encodedJs)], { type: "text/javascript;charset=utf-8" }); export default function WorkerWrapper() { let objURL; @@ -310,16 +313,14 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { objURL && (window.URL || window.webkitURL).revokeObjectURL(objURL); } }` - - const base64Code = `${encodedJs} + : `${encodedJs} export default function WorkerWrapper() { return new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions}); } ` return { - // Using blob URL for SharedWorker results in multiple instances of a same worker - code: workerConstructor === 'Worker' ? blobCode : base64Code, + code, // Empty sourcemap to suppress Rollup warning map: { mappings: '' }, } From 68a612d9b2f00e7f0659cf74a04cf1d3044203ca Mon Sep 17 00:00:00 2001 From: Wei Date: Wed, 15 Mar 2023 16:34:14 +0800 Subject: [PATCH 09/10] Update packages/vite/src/node/plugins/worker.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 翠 / green --- packages/vite/src/node/plugins/worker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 3fcfdbc8300225..197b3feeabe0bc 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -305,7 +305,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { let objURL; try { objURL = blob && (window.URL || window.webkitURL).createObjectURL(blob); - if (objURL) throw '' + if (!objURL) throw '' return new ${workerConstructor}(objURL) } catch(e) { return new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions}); From ec3dc3276511890bd6507f4d70c82afd632ea15f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Thu, 16 Mar 2023 13:58:08 +0900 Subject: [PATCH 10/10] chore: update test --- playground/worker/__tests__/es/es-worker.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground/worker/__tests__/es/es-worker.spec.ts b/playground/worker/__tests__/es/es-worker.spec.ts index cabe43a6fdc5b0..dfa2462f61a551 100644 --- a/playground/worker/__tests__/es/es-worker.spec.ts +++ b/playground/worker/__tests__/es/es-worker.spec.ts @@ -80,7 +80,7 @@ describe.runIf(isBuild)('build', () => { expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`) expect(content).toMatch(`window.Blob`) expect(content).toMatch( - /try\{if\(\w+=\w+&&\(window\.URL\|\|window\.webkitURL\)\.createObjectURL\(\w+\),\w+\)throw""/, + /try\{if\(\w+=\w+&&\(window\.URL\|\|window\.webkitURL\)\.createObjectURL\(\w+\),!\w+\)throw""/, ) // inlined shared worker expect(content).toMatch(