Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/danielroe/nuxt-workers. SSR-safe, zero-config Web Workers integration for Nuxt.
Something went wrong. Try again.
3.5 kB · 129 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130import { findExports } from 'mlly'import { parseQuery } from 'ufo'import { createUnplugin } from 'unplugin'import MagicString from 'magic-string'
export const VIRTUAL_ID = 'virtual:nuxt-workers.mjs'
interface WorkerPluginOptions { mode: 'server' | 'client' context: { workerExports: Record<string, string> reverseMap: Record<string, string[]> } sourcemap?: boolean}
export const WorkerTransformPlugin = (opts: WorkerPluginOptions) => createUnplugin(() => { return { name: 'nuxt-workers:transform', enforce: 'pre', transform(code, _id) { if (opts.mode === 'server') { return }
const id = _id.replace(/\?.+$/, '') if (!opts.context.reverseMap[id]) { return }
const s = new MagicString(code)
const vals = findExports(code) for (const val of vals) { s.replace(val.code, val.code.replace(/export /g, '')) }
const exports = opts.context.reverseMap[id] s.append([ '', `const __worker_exports__ = { ${exports.map(e => `${e}: ${e}`).join(', ')} }`, `self.onmessage = async (e) => {`, ` const { name, args, id } = e.data`, ` const fn = __worker_exports__[name]`, ` if (!fn) { throw new Error('Worker function not found: ' + name) }`, ` try {`, ` const result = await fn(...args)`, ` self.postMessage({ result, id })`, ` } catch (e) {`, ` self.postMessage({ error: e.message, id })`, ` }`, `}`, ].join('\n'))
return { code: s.toString(), map: opts?.sourcemap ? s.generateMap({ hires: true }) : null, } }, }})
export const WorkerPlugin = (opts: WorkerPluginOptions) => createUnplugin(() => { return { name: 'nuxt-workers:load', enforce: 'pre', resolveId(id) { if (id.startsWith(VIRTUAL_ID)) { return id } }, loadInclude: id => id.startsWith(VIRTUAL_ID), load(id) { const query = parseQuery(id.split('?')[1])
const file = query.source as string
const exports = opts.context.reverseMap[file] if (!exports || !exports.length) { return 'export {}' }
let source = '' if (opts.mode === 'client') { source += `const map = {}let count = 0let _nuxt_worker
function initWorker () { const worker = new Worker(new URL(${JSON.stringify(file)}, import.meta.url), { type: 'module' }) worker.onmessage = (e) => { const [resolve, reject] = map[e.data.id] if ('error' in e.data) { reject(new Error(e.data.error)) } else { resolve(e.data.result) } } return worker}` }
for (const name of exports) { source += `\nexport async function ${name} (...args) {` if (opts.mode === 'server') { source += `\n const { ${name}: fn } = await import(${JSON.stringify(opts.context.workerExports[name])})` source += `\n return fn(...args)` } else { source += `\n _nuxt_worker ||= initWorker()\n` source += `\n const id = count++` source += `\n return new Promise((resolve, reject) => {` source += `\n map[id] = [resolve, reject]` source += `\n _nuxt_worker.postMessage({ name: ${JSON.stringify(name)}, args, id })` source += `\n })` }
source += `\n}\n` }
return { code: source, map: null, } }, }})