Files
HealthLog/scripts/stage-tesseract-assets.mjs

69 lines
2.7 KiB
JavaScript

#!/usr/bin/env node
/**
* Stage the in-browser OCR engine under `public/tesseract/`.
*
* `src/lib/labs/local-ocr.ts` reads a lab-report photo on the device with
* tesseract.js. Left at its defaults, tesseract.js fetches its worker, its
* WebAssembly core and the language data from cdn.jsdelivr.net: the browser
* of every user who scans a report contacts a third-party CDN, and the
* production CSP (`worker-src 'self'`, `connect-src 'self'`) refuses those
* requests, so the feature could not load at all. Serving the same files
* from the app's own origin removes both problems.
*
* What is copied, all from pinned packages in node_modules:
* - tesseract.js `dist/worker.min.js`
* - the three LSTM-only core builds of tesseract.js-core (plain, SIMD,
* relaxed SIMD); the worker picks one by feature detection. LSTM-only
* because `createWorker` defaults to OEM 1.
* - `deu` and `eng` `4.0.0_best_int` language data, gzipped, which is what
* OEM 1 loads.
*
* Runs as a `prebuild`/`predev` step; the output is gitignored.
*/
import { copyFileSync, mkdirSync, rmSync } from "node:fs";
import { createRequire } from "node:module";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const require = createRequire(import.meta.url);
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
const outDir = join(repoRoot, "public", "tesseract");
const pkgDir = (name) => dirname(require.resolve(`${name}/package.json`));
// tesseract.js-core is a dependency of tesseract.js, not of this project;
// resolve it from tesseract.js's own location so pnpm's strict layout finds it.
const tesseractDir = pkgDir("tesseract.js");
const coreDir = dirname(
createRequire(join(tesseractDir, "package.json")).resolve(
"tesseract.js-core/package.json",
),
);
const files = [
[join(tesseractDir, "dist", "worker.min.js"), "worker.min.js"],
...[
"tesseract-core-lstm.wasm.js",
"tesseract-core-simd-lstm.wasm.js",
"tesseract-core-relaxedsimd-lstm.wasm.js",
].map((file) => [join(coreDir, file), join("core", file)]),
// Literal specifiers, so the dependency is visible to static analysis.
...[
["deu", require.resolve("@tesseract.js-data/deu/package.json")],
["eng", require.resolve("@tesseract.js-data/eng/package.json")],
].map(([lang, manifest]) => [
join(dirname(manifest), "4.0.0_best_int", `${lang}.traineddata.gz`),
join("lang", `${lang}.traineddata.gz`),
]),
];
rmSync(outDir, { recursive: true, force: true });
for (const [from, to] of files) {
const target = join(outDir, to);
mkdirSync(dirname(target), { recursive: true });
copyFileSync(from, target);
}
console.log(
`stage-tesseract-assets: ${files.length} files -> public/tesseract`,
);