#!/usr/bin/env node
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

/**
 * Post-`tsc` step that produces the published `dist/streaming/inline-worker.js`
 * with the decode worker bundled as an IIFE string. See
 * `src/streaming/worker-client.ts` for how the bundle is consumed.
 *
 * What this gives consumers:
 *
 *  - **Zero Vite config.** Spawning the worker via `Blob` URL bypasses
 *    Vite's default `worker.format: 'iife'` setting that previously errored
 *    on the ES module worker (#666). No `optimizeDeps.exclude` either,
 *    because the worker code lives inside the imported JS chunk, not in a
 *    sibling file Vite has to discover.
 *  - **Tree-shakable.** `inline-worker.js` is reached only via a dynamic
 *    `import()` inside `defaultSpawn`. Consumers who use the package's
 *    non-worker entry points (e.g. `decodeIfcxPointAttribute`) don't pay
 *    the bundle's bytes.
 *
 * What this does NOT include:
 *
 *  - **laz-perf wasm.** The npm `laz-perf` package fetches its `.wasm`
 *    binary at runtime via `new URL(..., import.meta.url)` — a pattern
 *    that doesn't work inside a Blob URL worker (no resolvable origin).
 *    LAZ decoding from the inline bundle therefore needs the consumer to
 *    pass a custom `spawn` callback that yields a worker capable of
 *    fetching the wasm. LAS / PLY / PCD / E57 / ASCII work fine.
 *
 * Determinism: this script writes the same output bytes given the same
 * input. esbuild is invoked with explicit `target: 'es2020'`,
 * `minify: false`, `sourcemap: false` so the result is human-inspectable
 * and stable across CI runs.
 */

import { build } from 'esbuild';
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const pkgRoot = resolve(__dirname, '..');
const workerEntry = resolve(pkgRoot, 'dist/streaming/decode-worker.js');
const inlineWorkerOut = resolve(pkgRoot, 'dist/streaming/inline-worker.js');

if (!existsSync(workerEntry)) {
  console.error(`[build-worker-bundle] expected ${workerEntry} to exist — run \`pnpm exec tsc\` first.`);
  process.exit(1);
}

/** @type {import('esbuild').BuildOptions} */
const esbuildOptions = {
  entryPoints: [workerEntry],
  bundle: true,
  format: 'iife',
  platform: 'browser',
  target: 'es2020',
  minify: false,
  sourcemap: false,
  write: false,
  // Inline every static + dynamic import the worker reaches EXCEPT the
  // laz-perf wasm URL hint. That hint is a Vite-only `?url` suffix; esbuild
  // can't resolve it and we can't sensibly inline the wasm asset anyway
  // (see the file header for why). Mark it as external so the bundle
  // succeeds; LAZ users who hit the inline path will see a clear runtime
  // error from `await import('laz-perf/lib/web/laz-perf.wasm?url')`.
  external: ['laz-perf/lib/web/laz-perf.wasm?url'],
  // The dynamic imports of format-specific sources get pulled in by
  // esbuild's bundler regardless of `splitting`; we want them inlined so
  // the IIFE is self-contained.
  splitting: false,
  // The worker uses `self.onmessage = ...` — keep that intact rather than
  // letting tree-shaking trip on the side-effect-only assignment.
  treeShaking: false,
};

console.log('[build-worker-bundle] bundling decode-worker.js as IIFE…');
const result = await build(esbuildOptions);
const out = result.outputFiles?.[0];
if (!out) {
  console.error('[build-worker-bundle] esbuild produced no output');
  process.exit(1);
}

const workerCode = out.text;
const sizeKb = (workerCode.length / 1024).toFixed(1);
console.log(`[build-worker-bundle] bundled worker is ${sizeKb} KB`);

// Emit `inline-worker.js` exporting the bundled IIFE as a single string
// constant. The constant is large but only loaded when `defaultSpawn`
// dynamically imports this module.
//
// `JSON.stringify` handles the escape work (newlines, backslashes, quotes,
// unicode escapes) — much safer than a hand-rolled escape pass for a
// ~50-100 KB blob.
const inlineWorkerSource = `// AUTO-GENERATED by scripts/build-worker-bundle.mjs — do not edit by hand.
// The matching source file at src/streaming/inline-worker.ts exports
// \`null\` so workspace dev falls back to the new-URL spawn path; this
// dist override is what published consumers see.
export const INLINE_WORKER_CODE = ${JSON.stringify(workerCode)};
`;

writeFileSync(inlineWorkerOut, inlineWorkerSource);
console.log(`[build-worker-bundle] wrote ${inlineWorkerOut} (${(inlineWorkerSource.length / 1024).toFixed(1)} KB total)`);
