import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { spawnSync } from "node:child_process"; const root = path.resolve(import.meta.dirname, "../.."); const arg = (name, fallback = null) => { const index = process.argv.indexOf(name); return index >= 0 ? process.argv[index + 1] : fallback; }; const wasmSplit = path.join(root, ".emsdk/upstream/bin/wasm-split"); const input = arg("--input", "web/app/public/vendor/blender/web_engine.wasm"); const outputDir = arg("--output-dir", "web/app/public/vendor/blender/modules"); const manifestPath = arg("--manifest", "web/app/public/wasm-module-manifest.json"); const splitFunctions = arg("--split-funcs", "web_engine_get_live_handles"); if (!fs.existsSync(path.join(root, input))) throw new Error(`WASM input is missing: ${input}`); if (!fs.existsSync(wasmSplit)) throw new Error(`wasm-split is missing: ${wasmSplit}`); const destination = path.join(root, outputDir); fs.mkdirSync(destination, { recursive: true }); const stem = path.basename(input, ".wasm"); const primary = path.join(destination, `${stem}.primary.wasm`); const secondary = path.join(destination, `${stem}.secondary.wasm`); const result = spawnSync(wasmSplit, [ "--split", "--all-features", "--split-funcs", splitFunctions, "--primary-output", primary, "--secondary-output", secondary, path.join(root, input), ], { cwd: root, encoding: "utf8" }); if (result.status !== 0 || !fs.existsSync(primary) || !fs.existsSync(secondary)) { throw new Error(`wasm-split failed (${result.status}):\n${result.stdout ?? ""}${result.stderr ?? ""}`); } const digest = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); const variant = input.includes("/pthread/") ? "pthread" : "single"; if (fs.existsSync(path.join(root, manifestPath))) { const manifestFile = path.join(root, manifestPath); const manifest = JSON.parse(fs.readFileSync(manifestFile, "utf8")); const debug = manifest.modules?.find((entry) => entry.id === "debug-incremental"); const secondaryEntry = manifest.modules?.find((entry) => entry.id === "debug-incremental-secondary"); if (debug?.resources?.[variant]) { debug.resources[variant].sha256 = digest(path.join(root, input)); debug.resources[variant].url = `/${input.replace(/^web\/app\/public\//u, "")}`; } if (secondaryEntry?.resources?.[variant]) { secondaryEntry.resources[variant].fileName = path.basename(secondary); secondaryEntry.resources[variant].url = `/${path.relative(path.join(root, "web/app/public"), secondary).replaceAll(path.sep, "/")}`; secondaryEntry.resources[variant].sha256 = digest(secondary); } fs.writeFileSync(manifestFile, `${JSON.stringify(manifest, null, 2)}\n`); } console.log(JSON.stringify({ status: "PASS", input, splitFunctions: splitFunctions.split(",").filter(Boolean), primary: { path: path.relative(root, primary), bytes: fs.statSync(primary).size, sha256: digest(primary) }, secondary: { path: path.relative(root, secondary), bytes: fs.statSync(secondary).size, sha256: digest(secondary) }, }, null, 2));