Files
brittle/src-tauri/assets/viewer-src/build.cjs
2026-04-03 18:45:08 +02:00

116 lines
4.3 KiB
JavaScript

/**
* build.cjs — esbuild script for the PDF viewer TypeScript bundle.
*
* Produces two output files in ../assets/viewer/:
* viewer.bundle.js — main viewer (IIFE, no PDF.js included)
* render-worker.bundle.js — render worker (pdf.min.js prepended + IIFE)
*
* Usage:
* node build.cjs — one-shot production build (minified)
* node build.cjs --watch — watch mode for development (unminified)
*/
const esbuild = require("esbuild");
const fs = require("fs");
const path = require("path");
const isWatch = process.argv.includes("--watch");
const minify = !isWatch;
const ROOT = __dirname;
const PDFJS_WORKER_MIN = path.join(ROOT, "../viewer/pdfjs/pdf.worker.min.js");
const PDFJS_MIN = path.join(ROOT, "../viewer/pdfjs/pdf.min.js");
const OUT_DIR = path.join(ROOT, "../viewer");
// Preamble prepended to the render-worker bundle.
//
// Sets `globalThis.window = globalThis` before pdf.worker.min.js runs so that:
// 1. pdf.worker.min.js does NOT auto-call WorkerMessageHandler.initializeFromPort(self)
// (which would hijack our render-worker's own onmessage handler).
// 2. pdf.worker.min.js DOES set globalThis.pdfjsWorker.WorkerMessageHandler as usual.
// 3. pdf.min.js's _mainThreadWorkerMessageHandler getter then finds the handler via
// globalThis.pdfjsWorker and uses it inline — no document.createElement needed.
const RENDER_WORKER_PREAMBLE = Buffer.from("globalThis.window=globalThis;\n");
// ── Render-worker plugin: prepend preamble + pdf.worker.min.js + pdf.min.js ───
//
// Bundle order matters:
// 1. Preamble — sets globalThis.window = globalThis
// 2. pdf.worker.min.js — sets globalThis.pdfjsWorker.WorkerMessageHandler
// (skips auto-setup because window is now defined)
// 3. pdf.min.js — sets globalThis.pdfjsLib; fake-worker path reads pdfjsWorker
// 4. Compiled TS — our render-worker code
function prependPdfjsPlugin() {
const pdfjsWorkerMin = fs.readFileSync(PDFJS_WORKER_MIN);
const pdfjsMin = fs.readFileSync(PDFJS_MIN);
const NL = Buffer.from("\n");
return {
name: "prepend-pdfjs",
setup(build) {
build.onEnd(result => {
if (result.errors.length > 0 || !result.outputFiles) return;
const compiled = Buffer.from(result.outputFiles[0].contents);
const combined = Buffer.concat([
RENDER_WORKER_PREAMBLE,
pdfjsWorkerMin, NL,
pdfjsMin, NL,
compiled,
]);
fs.writeFileSync(path.join(OUT_DIR, "render-worker.bundle.js"), combined);
if (!isWatch) console.log("[viewer-src] render-worker.bundle.js written");
});
},
};
}
async function main() {
const sharedOptions = { bundle: true, format: "iife", target: "es2020", minify };
if (isWatch) {
const viewerCtx = await esbuild.context({
...sharedOptions,
entryPoints: ["src/viewer.ts"],
outfile: path.join(OUT_DIR, "viewer.bundle.js"),
});
const workerCtx = await esbuild.context({
...sharedOptions,
entryPoints: ["src/render-worker.ts"],
write: false,
plugins: [prependPdfjsPlugin()],
});
await viewerCtx.watch();
await workerCtx.watch();
console.log("[viewer-src] watching for changes…");
// Keep the process alive
return;
}
// One-shot build
await esbuild.build({
...sharedOptions,
entryPoints: ["src/viewer.ts"],
outfile: path.join(OUT_DIR, "viewer.bundle.js"),
});
console.log("[viewer-src] viewer.bundle.js written");
// Render worker: build to memory then prepend preamble + worker + pdf.min.js
const workerResult = await esbuild.build({
...sharedOptions,
entryPoints: ["src/render-worker.ts"],
write: false,
});
const pdfjsWorkerMin = fs.readFileSync(PDFJS_WORKER_MIN);
const pdfjsMin = fs.readFileSync(PDFJS_MIN);
const NL = Buffer.from("\n");
const compiled = Buffer.from(workerResult.outputFiles[0].contents);
fs.writeFileSync(
path.join(OUT_DIR, "render-worker.bundle.js"),
Buffer.concat([RENDER_WORKER_PREAMBLE, pdfjsWorkerMin, NL, pdfjsMin, NL, compiled]),
);
console.log("[viewer-src] render-worker.bundle.js written");
}
main().catch(e => { console.error(e); process.exit(1); });