Skip to content

Commit

Permalink
fix: errors in worker handling (#7236)
Browse files Browse the repository at this point in the history
  • Loading branch information
poyoho committed Mar 25, 2022
1 parent d649dab commit 77dc1a1
Show file tree
Hide file tree
Showing 35 changed files with 438 additions and 196 deletions.
102 changes: 102 additions & 0 deletions packages/playground/worker/__tests__/es/es-worker.spec.ts
@@ -0,0 +1,102 @@
import fs from 'fs'
import path from 'path'
import { untilUpdated, isBuild, testDir } from '../../../testUtils'
import type { Page } from 'playwright-chromium'

test('normal', async () => {
await page.click('.ping')
await untilUpdated(() => page.textContent('.pong'), 'pong')
await untilUpdated(
() => page.textContent('.mode'),
isBuild ? 'production' : 'development'
)
await untilUpdated(
() => page.textContent('.bundle-with-plugin'),
'worker bundle with plugin success!'
)
})

test('TS output', async () => {
await page.click('.ping-ts-output')
await untilUpdated(() => page.textContent('.pong-ts-output'), 'pong')
})

test('inlined', async () => {
await page.click('.ping-inline')
await untilUpdated(() => page.textContent('.pong-inline'), 'pong')
})

const waitSharedWorkerTick = (
(resolvedSharedWorkerCount: number) => async (page: Page) => {
await untilUpdated(async () => {
const count = await page.textContent('.tick-count')
// ignore the initial 0
return count === '1' ? 'page loaded' : ''
}, 'page loaded')
// test.concurrent sequential is not guaranteed
// force page to wait to ensure two pages overlap in time
resolvedSharedWorkerCount++
if (resolvedSharedWorkerCount < 2) return

await untilUpdated(() => {
return resolvedSharedWorkerCount === 2 ? 'all pages loaded' : ''
}, 'all pages loaded')
}
)(0)

test.concurrent.each([[true], [false]])('shared worker', async (doTick) => {
if (doTick) {
await page.click('.tick-shared')
}
await waitSharedWorkerTick(page)
})

test('worker emitted', async () => {
await untilUpdated(() => page.textContent('.nested-worker'), 'pong')
})

if (isBuild) {
const assetsDir = path.resolve(testDir, 'dist/es/assets')
// assert correct files
test('inlined code generation', async () => {
const files = fs.readdirSync(assetsDir)
expect(files.length).toBe(20)
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'))
const workerContent = fs.readFileSync(
path.resolve(assetsDir, worker),
'utf-8'
)

// worker should have all imports resolved and no exports
expect(workerContent).not.toMatch(`import`)
expect(workerContent).not.toMatch(`export`)
// chunk
expect(content).toMatch(`new Worker("/es/assets`)
expect(content).toMatch(`new SharedWorker("/es/assets`)
// inlined
expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`)
expect(content).toMatch(`window.Blob`)
})
}

test('module worker', async () => {
expect(await page.textContent('.shared-worker-import-meta-url')).toMatch(
'A string'
)
})

test('classic worker', async () => {
expect(await page.textContent('.classic-worker')).toMatch('A classic')
expect(await page.textContent('.classic-shared-worker')).toMatch('A classic')
})

test('emit chunk', async () => {
expect(await page.textContent('.emti-chunk-worker')).toMatch(
'{"msg1":"module1","msg2":"module2","msg3":"module3"}'
)
expect(await page.textContent('.emti-chunk-dynamic-import-worker')).toMatch(
'"A string/es/"'
)
})
1 change: 1 addition & 0 deletions packages/playground/worker/__tests__/es/vite.config.js
@@ -0,0 +1 @@
module.exports = require('../../vite.config-es')
22 changes: 12 additions & 10 deletions packages/playground/worker/__tests__/worker.spec.ts
Expand Up @@ -53,19 +53,15 @@ test.concurrent.each([[true], [false]])('shared worker', async (doTick) => {

test('worker emitted', async () => {
await untilUpdated(() => page.textContent('.nested-worker'), 'pong')
await untilUpdated(
() => page.textContent('.nested-worker-dynamic-import'),
'"msg":"pong"'
)
})

if (isBuild) {
const assetsDir = path.resolve(testDir, 'dist/assets')
const assetsDir = path.resolve(testDir, 'dist/iife/assets')
// assert correct files
test('inlined code generation', async () => {
const files = fs.readdirSync(assetsDir)
expect(files.length).toBe(11)
const index = files.find((f) => f.includes('index'))
expect(files.length).toBe(13)
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'))
const workerContent = fs.readFileSync(
Expand All @@ -77,15 +73,21 @@ if (isBuild) {
expect(workerContent).not.toMatch(`import`)
expect(workerContent).not.toMatch(`export`)
// chunk
expect(content).toMatch(`new Worker("/assets`)
expect(content).toMatch(`new SharedWorker("/assets`)
expect(content).toMatch(`new Worker("/iife/assets`)
expect(content).toMatch(`new SharedWorker("/iife/assets`)
// inlined
expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`)
expect(content).toMatch(`window.Blob`)
})
}

test('classic worker is run', async () => {
test('module worker', async () => {
expect(await page.textContent('.shared-worker-import-meta-url')).toMatch(
'A string'
)
})

test('classic worker', async () => {
expect(await page.textContent('.classic-worker')).toMatch('A classic')
expect(await page.textContent('.classic-shared-worker')).toMatch('A classic')
})
@@ -1,4 +1,4 @@
importScripts('/classic.js')
importScripts(`/${self.location.pathname.split('/')[1]}/classic.js`)

self.onconnect = (event) => {
const port = event.ports[0]
Expand Down
30 changes: 3 additions & 27 deletions packages/playground/worker/classic-worker.js
@@ -1,29 +1,5 @@
// prettier-ignore
function text(el, text) {
document.querySelector(el).textContent = text
}
importScripts(`/${self.location.pathname.split("/")[1]}/classic.js`)

const classicWorker = new Worker(
new URL('./newUrl/classic-worker.js', import.meta.url) /* , */ ,
// test comment

)

classicWorker.addEventListener('message', ({ data }) => {
text('.classic-worker', data)
})
classicWorker.postMessage('ping')

const classicSharedWorker = new SharedWorker(
new URL('./newUrl/classic-shared-worker.js', import.meta.url),
{
type: 'classic'
}
)
classicSharedWorker.port.addEventListener('message', (ev) => {
text(
'.classic-shared-worker',
ev.data
)
self.addEventListener('message', () => {
self.postMessage(self.constant)
})
classicSharedWorker.port.start()
@@ -0,0 +1,3 @@
import('./modules/module').then((module) => {
self.postMessage(module.default + import.meta.env.BASE_URL)
})
7 changes: 7 additions & 0 deletions packages/playground/worker/emit-chunk-nested-worker.js
@@ -0,0 +1,7 @@
import SubWorker from './emit-chunk-sub-worker?worker'

const subWorker = new SubWorker()

subWorker.onmessage = (event) => {
self.postMessage(event.data)
}
6 changes: 6 additions & 0 deletions packages/playground/worker/emit-chunk-sub-worker.js
@@ -0,0 +1,6 @@
Promise.all([import('./modules/module2'), import('./modules/module3')]).then(
(data) => {
const _data = { ...data[0], ...data[1] }
self.postMessage(_data)
}
)
165 changes: 56 additions & 109 deletions packages/playground/worker/index.html
@@ -1,3 +1,4 @@
<h2 class="format-iife">format iife:</h2>
<div>Expected values: <span class="mode-true"></span></div>
<button class="ping">Ping</button>
<div>
Expand All @@ -20,113 +21,59 @@
<span class="tick-count">0</span>
</div>

<p>new Worker(new Url('path', import.meta.url), { type: 'module' })</p>
<div class="worker-import-meta-url"></div>

<p>new SharedWorker(new Url('path', import.meta.url), { type: 'module' })</p>
<div class="shared-worker-import-meta-url"></div>

<p>nested worker</p>
<div class="nested-worker"></div>
<div class="nested-worker-dynamic-import"></div>
<p>new Worker(new Url('path', import.meta.url))</p>
<div class="classic-worker"></div>

<p>new Worker(new Url('path', import.meta.url), { type: 'classic' })</p>
<div class="classic-shared-worker"></div>

<script type="module">
import myWorker from './my-worker?worker'
import InlineWorker from './my-worker?worker&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'
import { mode } from './workerImport'
import './classic-worker'

document.querySelector('.mode-true').textContent = mode

const worker = new myWorker()
worker.addEventListener('message', (e) => {
text('.pong', e.data.msg)
text('.mode', e.data.mode)
text('.bundle-with-plugin', e.data.bundleWithPlugin)
})

document.querySelector('.ping').addEventListener('click', () => {
worker.postMessage('ping')
})

const inlineWorker = new InlineWorker()
inlineWorker.addEventListener('message', (e) => {
text('.pong-inline', e.data.msg)
})

document.querySelector('.ping-inline').addEventListener('click', () => {
inlineWorker.postMessage('ping')
})

const sharedWorker = new mySharedWorker()
document.querySelector('.tick-shared').addEventListener('click', () => {
sharedWorker.port.postMessage('tick')
})

sharedWorker.port.addEventListener('message', (event) => {
text('.tick-count', event.data)
})

sharedWorker.port.start()

const tsOutputWorker = new TSOutputWorker()
tsOutputWorker.addEventListener('message', (e) => {
text('.pong-ts-output', e.data.msg)
})

document.querySelector('.ping-ts-output').addEventListener('click', () => {
tsOutputWorker.postMessage('ping')
})

function text(el, text) {
document.querySelector(el).textContent = text
<p>
new Worker(new Url('./url-worker.js', import.meta.url), { type: 'module' })
<span class="classname">.worker-import-meta-url</span>
</p>
<code class="worker-import-meta-url"></code>

<p>
new SharedWorker(new Url('./url-shared-worker.js', import.meta.url), { type:
'module' })
<span class="classname">.shared-worker-import-meta-url</span>
</p>
<code class="shared-worker-import-meta-url"></code>

<p>
import NestedWorker from './worker-nested-worker?worker' - nested worker
<span class="classname">.nested-worker</span>
</p>
<code class="nested-worker"></code>

<p>
new Worker(new Url('./classic-worker.js', import.meta.url))
<span class="classname">.classic-worker</span>
</p>
<code class="classic-worker"></code>

<p>
new SharedWorker(new Url('./classic-shared-worker.js', import.meta.url), {
type: 'classic' })
<span class="classname">.classic-shared-worker</span>
</p>
<code class="classic-shared-worker"></code>

<hr />
<h2 class="format-es"></h2>

<p>
worker emit chunk
<span class="classname">.emti-chunk-worker</span>
</p>
<code class="emti-chunk-worker"></code>

<p>
worker dynamic import to emit chunk
<span class="classname">.emti-chunk-dynamic-import-worker</span>
</p>
<code class="emti-chunk-dynamic-import-worker"></code>

<style>
p {
background: rgba(0, 0, 0, 0.1);
}
const workerOptions = { type: 'module' }
// url import worker
const w = new Worker(
new URL('./newUrl/url-worker.js', import.meta.url),
/* @vite-ignore */ workerOptions
)
w.addEventListener('message', (ev) =>
text(
'.worker-import-meta-url',
'worker import.meta.url' + JSON.stringify(ev.data)
)
)

const genWorkerName = () => 'module'
const w2 = new SharedWorker(
new URL('./newUrl/url-shared-worker.js', import.meta.url),
{
/* @vite-ignore */
name: genWorkerName(),
type: 'module'
}
)
w2.port.addEventListener('message', (ev) => {
text(
'.shared-worker-import-meta-url',
'shared-worker import.meta.url' + JSON.stringify(ev.data)
)
})
w2.port.start()

const nestedWorker = new NestedWorker()
nestedWorker.addEventListener('message', (ev) => {
console.log(ev)
if (typeof ev.data === 'string') {
text('.nested-worker', ev.data)
} else {
text('.nested-worker-dynamic-import', JSON.stringify(ev.data))
}
})
nestedWorker.postMessage('ping')
</script>
.classname {
color: green;
}
</style>
<script type="module" src="./worker/main.js"></script>
File renamed without changes.
1 change: 1 addition & 0 deletions packages/playground/worker/modules/module1.js
@@ -0,0 +1 @@
export const msg1 = 'module1'
3 changes: 3 additions & 0 deletions packages/playground/worker/modules/module2.js
@@ -0,0 +1,3 @@
export * from './module'
export * from './module1'
export const msg2 = 'module2'
2 changes: 2 additions & 0 deletions packages/playground/worker/modules/module3.js
@@ -0,0 +1,2 @@
export * from './module'
export const msg3 = 'module3'
File renamed without changes.
File renamed without changes.

0 comments on commit 77dc1a1

Please sign in to comment.