Skip to content

Commit

Permalink
test: test ModuleRunnerTransport invoke API (vitejs#18865)
Browse files Browse the repository at this point in the history
Co-authored-by: sapphi-red <[email protected]>
Co-authored-by: bluwy <[email protected]>
  • Loading branch information
3 people authored Dec 6, 2024
1 parent 7a0758c commit e5f5301
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// @ts-check

import { BroadcastChannel, parentPort } from 'node:worker_threads'
import { fileURLToPath } from 'node:url'
import { ESModulesEvaluator, ModuleRunner } from 'vite/module-runner'
import { createBirpc } from 'birpc'

if (!parentPort) {
throw new Error('File "worker.js" must be run in a worker thread')
}

/** @type {import('worker_threads').MessagePort} */
const pPort = parentPort

/** @type {import('birpc').BirpcReturn<{ invoke: (data: any) => any }>} */
const rpc = createBirpc({}, {
post: (data) => pPort.postMessage(data),
on: (data) => pPort.on('message', data),
})

const runner = new ModuleRunner(
{
root: fileURLToPath(new URL('./', import.meta.url)),
transport: {
invoke(data) { return rpc.invoke(data) }
},
hmr: false,
},
new ESModulesEvaluator(),
)

const channel = new BroadcastChannel('vite-worker:invoke')
channel.onmessage = async (message) => {
try {
const mod = await runner.import(message.data.id)
channel.postMessage({ result: mod.default })
} catch (e) {
channel.postMessage({ error: e.stack })
}
}
parentPort.postMessage('ready')
3 changes: 2 additions & 1 deletion packages/vite/src/node/ssr/runtime/__tests__/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"dependencies": {
"@vitejs/cjs-external": "link:./fixtures/cjs-external",
"@vitejs/esm-external": "link:./fixtures/esm-external",
"tinyspy": "2.2.0"
"tinyspy": "2.2.0",
"birpc": "^0.2.19"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { BroadcastChannel, Worker } from 'node:worker_threads'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import type { BirpcReturn } from 'birpc'
import { createBirpc } from 'birpc'
import { DevEnvironment } from '../../..'
import { type ViteDevServer, createServer } from '../../../server'

describe('running module runner inside a worker and using the ModuleRunnerTransport#invoke API', () => {
let worker: Worker
let server: ViteDevServer
let rpc: BirpcReturn<
unknown,
{ invoke: (data: any) => Promise<{ result: any } | { error: any }> }
>
let handleInvoke: (data: any) => Promise<{ result: any } | { error: any }>

beforeAll(async () => {
worker = new Worker(
new URL('./fixtures/worker.invoke.mjs', import.meta.url),
{
stdout: true,
},
)
await new Promise<void>((resolve, reject) => {
worker.on('message', () => resolve())
worker.on('error', reject)
})
server = await createServer({
root: __dirname,
logLevel: 'error',
server: {
middlewareMode: true,
watch: null,
hmr: {
port: 9610,
},
},
environments: {
worker: {
dev: {
createEnvironment: (name, config) => {
return new DevEnvironment(name, config, {
hot: false,
})
},
},
},
},
})
handleInvoke = (data: any) => server.environments.ssr.hot.handleInvoke(data)
rpc = createBirpc(
{
invoke: (data: any) => handleInvoke(data),
},
{
post: (data) => worker.postMessage(data),
on: (data) => worker.on('message', data),
},
)
})

afterAll(() => {
server.close()
worker.terminate()
rpc.$close()
})

async function run(id: string) {
const channel = new BroadcastChannel('vite-worker:invoke')
return new Promise<any>((resolve, reject) => {
channel.onmessage = (event) => {
try {
resolve((event as MessageEvent).data)
} catch (e) {
reject(e)
}
}
channel.postMessage({ id })
})
}

it('correctly runs ssr code', async () => {
const output = await run('./fixtures/default-string.ts')
expect(output).toStrictEqual({
result: 'hello world',
})
})

it('triggers an error', async () => {
handleInvoke = async () => ({ error: new Error('This is an Invoke Error') })
const output = await run('dummy')
expect(output).not.toHaveProperty('result')
expect(output.error).toContain('Error: This is an Invoke Error')
})

it('triggers an unknown error', async () => {
handleInvoke = async () => ({ error: 'a string instead of an error' })
const output = await run('dummy')
expect(output).not.toHaveProperty('result')
expect(output.error).toContain('Error: Unknown invoke error')
})
})
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e5f5301

Please sign in to comment.