Newer
Older
bugtrail / packages / extension / scripts / build.mjs
import { spawn } from "node:child_process";
import { createRequire } from "node:module";
import { dirname } from "node:path";
import { writeManifest } from "./build-manifest.mjs";

const require = createRequire(import.meta.url);
// vite's ./bin subpath isn't exported; resolve via the package directory
const viteBin = new URL("file://" + dirname(require.resolve("vite/package.json")) + "/bin/vite.js").pathname;

/**
 * Vite can't emit IIFE for a multi-entry (code-splitting) build, so each
 * extension entry (background, content, options) is built as its own
 * single-chunk bundle into the same dist/<target> directory.
 * Usage: node scripts/build.mjs <chrome|firefox> [--watch]
 */
const target = process.argv[2];
const watch = process.argv.includes("--watch");
if (target !== "chrome" && target !== "firefox") {
  console.error("Usage: node scripts/build.mjs <chrome|firefox> [--watch]");
  process.exit(1);
}

// order matters: every pass writes assets/style.css (cssCodeSplit:false), so
// the content pass — whose stylesheet is the union of all styles — runs last
const entries = ["background", "options", "popup", "relay", "content"];

function run(entry) {
  return new Promise((resolve, reject) => {
    const child = spawn(
      process.execPath,
      [viteBin, "build", "--mode", target, ...(watch ? ["--watch"] : [])],
      {
        cwd: new URL("..", import.meta.url).pathname,
        env: { ...process.env, LTT_ENTRY: entry },
        stdio: "inherit",
      }
    );
    child.on("exit", (code) => (code === 0 ? resolve() : reject(new Error(`${target}/${entry} exited with ${code}`))));
  });
}

try {
  for (const entry of entries) {
    if (watch) {
      // keep all three watchers alive
      run(entry).catch((error) => {
        console.error(error.message);
        process.exit(1);
      });
    } else {
      await run(entry);
    }
  }
  if (!watch) writeManifest(target);
} catch (error) {
  console.error(error.message);
  process.exit(1);
}