Skip to content

Commit

Permalink
fix(worker): using data URLs for inline shared worker (#12014)
Browse files Browse the repository at this point in the history
Co-authored-by: 翠 / green <green@sapphi.red>
  • Loading branch information
fi3ework and sapphi-red authored Mar 18, 2023
1 parent 1a8af8d commit 79a5007
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 30 deletions.
2 changes: 1 addition & 1 deletion docs/config/worker-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Options related to Web Workers.
## worker.format

- **Type:** `'es' | 'iife'`
- **Default:** `iife`
- **Default:** `'iife'`

Output format for worker bundle.

Expand Down
43 changes: 29 additions & 14 deletions packages/vite/src/node/plugins/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '' },
}
Expand Down
13 changes: 12 additions & 1 deletion playground/worker/__tests__/es/es-worker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down Expand Up @@ -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 () => {
Expand Down
4 changes: 4 additions & 0 deletions playground/worker/__tests__/iife/iife-worker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions playground/worker/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ <h2 class="format-iife">format iife:</h2>
</p>
<code class="tick-count"></code>

<p>
import InlineSharedWorker from '../my-shared-worker?sharedworker&inline'
<span class="classname">.pong-shared-inline</span>
</p>
<code class="pong-shared-inline"></code>

<p>
new Worker(new URL('./url-worker.js', import.meta.url), { type: 'module' })
<span class="classname">.worker-import-meta-url</span>
Expand Down
13 changes: 13 additions & 0 deletions playground/worker/my-inline-shared-worker.ts
Original file line number Diff line number Diff line change
@@ -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')
11 changes: 4 additions & 7 deletions playground/worker/my-shared-worker.ts
Original file line number Diff line number Diff line change
@@ -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')
}
}

Expand Down
26 changes: 21 additions & 5 deletions playground/worker/worker/main-module.js
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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')
Expand Down

0 comments on commit 79a5007

Please sign in to comment.