Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(worker): bundle worker emit asset file #6599

Merged
4 changes: 4 additions & 0 deletions packages/playground/wasm/__tests__/wasm.spec.ts
Expand Up @@ -9,3 +9,7 @@ test('should work when output', async () => {
await page.click('.output-wasm .run')
await untilUpdated(() => page.textContent('.output-wasm .result'), '24')
})

test('should work when wasm in worker', async () => {
await untilUpdated(() => page.textContent('.worker-wasm .result'), '3')
})
Binary file added packages/playground/wasm/add.wasm
Binary file not shown.
15 changes: 15 additions & 0 deletions packages/playground/wasm/index.html
Expand Up @@ -12,9 +12,20 @@ <h3>When wasm is output, result should be 24</h3>
<span class="result"></span>
</div>

<div class="worker-wasm">
<h3>worker wasm</h3>
<span class="result"></span>
</div>

<script type="module">
import light from './light.wasm'
import heavy from './heavy.wasm'
import myWorker from './worker?worker'

const w = new myWorker()
w.addEventListener('message', (ev) => {
text('.worker-wasm .result', ev.data.result)
})

async function testWasm(init, resultElement) {
const { exported_func } = await init({
Expand All @@ -25,6 +36,10 @@ <h3>When wasm is output, result should be 24</h3>
exported_func()
}

function text(el, text) {
document.querySelector(el).textContent = text
}

document
.querySelector('.inline-wasm .run')
.addEventListener('click', async () =>
Expand Down
8 changes: 8 additions & 0 deletions packages/playground/wasm/vite.config.ts
@@ -0,0 +1,8 @@
import { defineConfig } from 'vite'
export default defineConfig({
build: {
// make can no emit light.wasm
// and emit add.wasm
assetsInlineLimit: 80
}
})
5 changes: 5 additions & 0 deletions packages/playground/wasm/worker.js
@@ -0,0 +1,5 @@
import init from './add.wasm'
init().then((exports) => {
// eslint-disable-next-line no-undef
self.postMessage({ result: exports.add(1, 2) })
})
22 changes: 9 additions & 13 deletions packages/playground/worker/__tests__/worker.spec.ts
Expand Up @@ -51,12 +51,20 @@ test.concurrent.each([[true], [false]])('shared worker', async (doTick) => {
await waitSharedWorkerTick(page)
})

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')
// assert correct files
test('inlined code generation', async () => {
const files = fs.readdirSync(assetsDir)
expect(files.length).toBe(8)
expect(files.length).toBe(11)
const index = files.find((f) => f.includes('index'))
const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8')
const worker = files.find((f) => f.includes('my-worker'))
Expand All @@ -75,18 +83,6 @@ if (isBuild) {
expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`)
expect(content).toMatch(`window.Blob`)
})

test('worker need bundle', () => {
fs.readdirSync(assetsDir)
.filter(
(file) =>
file.includes('url-worker') || file.includes('url-shared-worker')
)
.forEach((file) => {
const content = fs.readFileSync(path.resolve(assetsDir, file), 'utf-8')
expect(content.startsWith('(function(){')).toBe(true)
})
})
}

test('classic worker is run', async () => {
Expand Down
15 changes: 15 additions & 0 deletions packages/playground/worker/index.html
Expand Up @@ -26,6 +26,9 @@
<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>

Expand All @@ -37,6 +40,7 @@
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'

Expand Down Expand Up @@ -114,4 +118,15 @@
)
})
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>
13 changes: 13 additions & 0 deletions packages/playground/worker/sub-worker.js
@@ -0,0 +1,13 @@
self.onmessage = (event) => {
if (event.data === 'ping') {
self.postMessage('pong')
}
}
const data = import('./workerImport')
data.then((data) => {
const { mode, msg } = data
self.postMessage({
mode,
msg
})
})
4 changes: 1 addition & 3 deletions packages/playground/worker/vite.config.ts
Expand Up @@ -2,10 +2,8 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
import { defineConfig } from 'vite'

export default defineConfig({
build: {
target: process.env.NODE_ENV === 'production' ? 'chrome60' : 'esnext'
},
worker: {
format: 'es',
plugins: [vueJsx()]
}
})
13 changes: 13 additions & 0 deletions packages/playground/worker/worker-nested-worker.js
@@ -0,0 +1,13 @@
import SubWorker from './sub-worker?worker'

const subWorker = new SubWorker()

self.onmessage = (event) => {
if (event.data === 'ping') {
subWorker.postMessage('ping')
}
}

subWorker.onmessage = (event) => {
self.postMessage(event.data)
}
21 changes: 18 additions & 3 deletions packages/vite/src/node/plugins/worker.ts
Expand Up @@ -10,6 +10,7 @@ import { onRollupWarning } from '../build'
const WorkerFileId = 'worker_file'

export async function bundleWorkerEntry(
ctx: Rollup.TransformPluginContext,
config: ResolvedConfig,
id: string
): Promise<Buffer> {
Expand All @@ -27,11 +28,25 @@ export async function bundleWorkerEntry(
})
let code: string
try {
const { output } = await bundle.generate({
const {
output: [outputCode, ...outputChunks]
} = await bundle.generate({
format,
sourcemap: config.build.sourcemap
})
code = output[0].code
code = outputCode.code
outputChunks.forEach((outputChunk) => {
if (outputChunk.type === 'asset') {
ctx.emitFile(outputChunk)
}
poyoho marked this conversation as resolved.
Show resolved Hide resolved
if (outputChunk.type === 'chunk') {
ctx.emitFile({
fileName: `${config.build.assetsDir}/${outputChunk.fileName}`,
source: outputChunk.code,
type: 'asset'
})
}
})
} finally {
await bundle.close()
}
Expand Down Expand Up @@ -72,7 +87,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {

let url: string
if (isBuild) {
const code = await bundleWorkerEntry(config, id)
const code = await bundleWorkerEntry(this, config, id)
if (query.inline != null) {
const { format } = config.worker
const workerOptions = format === 'es' ? '{type: "module"}' : '{}'
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/node/plugins/workerImportMetaUrl.ts
Expand Up @@ -138,7 +138,7 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
const file = path.resolve(path.dirname(id), rawUrl.slice(1, -1))
let url: string
if (isBuild) {
const content = await bundleWorkerEntry(config, file)
const content = await bundleWorkerEntry(this, config, file)
const basename = path.parse(cleanUrl(file)).name
const contentHash = getAssetHash(content)
const fileName = path.posix.join(
Expand Down