From 79a500726f61b305fcc4b6a436f37c6dc803580c Mon Sep 17 00:00:00 2001
From: Wei
Date: Sat, 18 Mar 2023 15:29:54 +0800
Subject: [PATCH] fix(worker): using data URLs for inline shared worker
(#12014)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: 翠 / green
---
docs/config/worker-options.md | 2 +-
packages/vite/src/node/plugins/worker.ts | 43 +++++++++++++------
.../worker/__tests__/es/es-worker.spec.ts | 13 +++++-
.../worker/__tests__/iife/iife-worker.spec.ts | 4 ++
.../relative-base-worker.spec.ts | 4 ++
.../sourcemap-hidden-worker.spec.ts | 2 +-
.../sourcemap/sourcemap-worker.spec.ts | 2 +-
playground/worker/index.html | 6 +++
playground/worker/my-inline-shared-worker.ts | 13 ++++++
playground/worker/my-shared-worker.ts | 11 ++---
playground/worker/worker/main-module.js | 26 ++++++++---
11 files changed, 96 insertions(+), 30 deletions(-)
create mode 100644 playground/worker/my-inline-shared-worker.ts
diff --git a/docs/config/worker-options.md b/docs/config/worker-options.md
index 840caa60915ba2..99500ae259ccfe 100644
--- a/docs/config/worker-options.md
+++ b/docs/config/worker-options.md
@@ -5,7 +5,7 @@ Options related to Web Workers.
## worker.format
- **Type:** `'es' | 'iife'`
-- **Default:** `iife`
+- **Default:** `'iife'`
Output format for worker bundle.
diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts
index aae1a4a1423cda..197b3feeabe0bc 100644
--- a/packages/vite/src/node/plugins/worker.ts
+++ b/packages/vite/src/node/plugins/worker.ts
@@ -287,25 +287,40 @@ 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) {
const chunk = await bundleWorkerEntry(config, id, query)
- // 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 ? new ${workerConstructor}(objURL) : new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions});
- } finally {
- objURL && (window.URL || window.webkitURL).revokeObjectURL(objURL);
- }
- }`,
+ const encodedJs = `const encodedJs = "${Buffer.from(
+ chunk.code,
+ ).toString('base64')}";`
+
+ 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;
+ try {
+ 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);
+ }
+ }`
+ : `${encodedJs}
+ export default function WorkerWrapper() {
+ return new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions});
+ }
+ `
+ return {
+ code,
// 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 817198c23a07a4..dfa2462f61a551 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'),
@@ -72,9 +76,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\{if\(\w+=\w+&&\(window\.URL\|\|window\.webkitURL\)\.createObjectURL\(\w+\),!\w+\)throw""/,
+ )
+ // 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 () => {
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__/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 5c9c85b4799fd5..ff26fbcbba3206 100644
--- a/playground/worker/index.html
+++ b/playground/worker/index.html
@@ -38,6 +38,12 @@
+
+ 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 552c3e20f57853..d9728304191085 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/worker/main-module.js b/playground/worker/worker/main-module.js
index 1e5242935cdbb8..e19f8f4ec3af3a 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-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'
@@ -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')