// Bundles the extension's TypeScript entry points into dist/ for Firefox to load. // // Runs on `npm run build` and, via the `prepare` script, on every `npm ci` — so CI's // `web-ext lint` (which needs the referenced bundles to exist) works without a separate // build step. Firefox-only: no polyfill, native ESM, `browser.*` is a global. import { build } from 'esbuild'; import { rm, mkdir, copyFile } from 'node:fs/promises'; import { fileURLToPath } from 'node:url'; import { dirname, resolve } from 'node:path'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); const outdir = resolve(root, 'dist'); // The background page and the popup/options documents load as native ESM (manifest.json // declares "type": "module" for the background script; the popup/options HTML load // their script with type="module"). Content scripts registered via manifest.json's // content_scripts have no such declaration and run as classic scripts, so content.js is // built as an IIFE instead — an `export` left in by the esm build would be a syntax // error there. const esmEntryPoints = { background: resolve(root, 'src/background/index.ts'), popup: resolve(root, 'src/popup/popup.ts'), options: resolve(root, 'src/options/options.ts'), }; const iifeEntryPoints = { content: resolve(root, 'src/content/index.ts'), }; // Static HTML/CSS esbuild doesn't touch — copied straight to dist/ alongside their JS. const staticFiles = [ ['src/popup/popup.html', 'popup.html'], ['src/popup/popup.css', 'popup.css'], ['src/options/options.html', 'options.html'], ['src/options/options.css', 'options.css'], ]; await rm(outdir, { recursive: true, force: true }); await mkdir(outdir, { recursive: true }); await Promise.all(staticFiles.map(([src, dest]) => copyFile(resolve(root, src), resolve(outdir, dest)))); const watch = process.argv.includes('--watch'); const dev = watch || process.argv.includes('--dev'); const shared = { outdir, bundle: true, target: ['firefox128'], platform: 'browser', sourcemap: dev ? 'inline' : 'linked', minify: !dev, logLevel: 'info', // A bare `import ... from 'ws'` etc. must never reach a bundle — fail loud if one does. external: [], }; const buildConfigs = [ { ...shared, entryPoints: esmEntryPoints, format: 'esm' }, { ...shared, entryPoints: iifeEntryPoints, format: 'iife' }, ]; if (watch) { const { context } = await import('esbuild'); const contexts = await Promise.all(buildConfigs.map((cfg) => context(cfg))); await Promise.all(contexts.map((ctx) => ctx.watch())); console.log('esbuild: watching'); } else { await Promise.all(buildConfigs.map((cfg) => build(cfg))); }