// Adapted from https://github.com/mitschabaude/esbuild-plugin-inline-worker import * as esbuild from 'esbuild'; import findCacheDir from 'find-cache-dir'; import fs from 'node:fs'; import path from 'node:path'; export function inlineWorkerPlugin(extraConfig: esbuild.BuildOptions): esbuild.Plugin { return { name: 'esbuild-plugin-inline-worker', setup(build) { build.onLoad( { filter: /\.worker\.(js|jsx|ts|tsx)$/ }, async ({ path: workerPath }) => { const workerCode = await buildWorker(workerPath, extraConfig); return { contents: `import inlineWorker from '__inline-worker' export default function Worker() { return inlineWorker(${JSON.stringify(workerCode)}); } `, loader: 'js', }; }, ); const inlineWorkerFunctionCode = ` export default async function inlineWorker(scriptText) { if (typeof Worker !== 'undefined' && typeof Bun === 'undefined') { // Browser, Deno const blob = new Blob([scriptText], { type: "text/javascript" }); const url = URL.createObjectURL(blob); const worker = new Worker(url, { type: typeof Deno !== 'undefined' ? 'module' : undefined }); // module for Deno URL.revokeObjectURL(url); return worker; } else { // Node, Bun (Bun's Worker is flaky, worker_threads works much better) let Worker; try { Worker = (await import('worker_threads')).Worker; } catch { const workerModule = 'worker_threads'; Worker = require(workerModule).Worker; } const worker = new Worker(scriptText, { eval: true }); return worker; } } `; build.onResolve({ filter: /^__inline-worker$/ }, ({ path }) => { return { path, namespace: 'inline-worker' }; }); build.onLoad({ filter: /.*/, namespace: 'inline-worker' }, () => { return { contents: inlineWorkerFunctionCode, loader: 'js' }; }); }, }; } const cacheDir = findCacheDir({ name: 'esbuild-plugin-inline-worker', create: true, }); if (cacheDir === undefined) { throw new Error('Cache directory not found.'); } async function buildWorker(workerPath: string, extraConfig: esbuild.BuildOptions) { const scriptNameParts = path.basename(workerPath).split('.'); scriptNameParts.pop(); scriptNameParts.push('js'); const scriptName = scriptNameParts.join('.'); const bundlePath = path.resolve(cacheDir!, scriptName); if (extraConfig) { delete extraConfig.entryPoints; delete extraConfig.outfile; delete extraConfig.outdir; } await esbuild.build({ entryPoints: [workerPath], bundle: true, minify: true, outfile: bundlePath, target: 'es2017', format: 'esm', ...extraConfig, }); return fs.promises.readFile(bundlePath, { encoding: 'utf-8' }); }