Replace the bash-based bundling script with a TypeScript implementation to improve cross-platform compatibility and maintainability. The new script includes logic for change detection via file hashing to skip unnecessary rebuilds.
120 lines
3.4 KiB
TypeScript
120 lines
3.4 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import { promises as fs } from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { execSync } from "node:child_process";
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const ROOT_DIR = path.resolve(__dirname, "..");
|
|
|
|
const HASH_FILE = path.join(ROOT_DIR, "src/canvas-host/a2ui/.bundle.hash");
|
|
const OUTPUT_FILE = path.join(ROOT_DIR, "src/canvas-host/a2ui/a2ui.bundle.js");
|
|
const A2UI_RENDERER_DIR = path.join(ROOT_DIR, "vendor/a2ui/renderers/lit");
|
|
const A2UI_APP_DIR = path.join(ROOT_DIR, "apps/shared/MoltbotKit/Tools/CanvasA2UI");
|
|
|
|
async function exists(p: string) {
|
|
try {
|
|
await fs.access(p);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
if (!(await exists(A2UI_RENDERER_DIR)) || !(await exists(A2UI_APP_DIR))) {
|
|
console.log("A2UI sources missing; keeping prebuilt bundle.");
|
|
process.exit(0);
|
|
}
|
|
|
|
const INPUT_PATHS = [
|
|
path.join(ROOT_DIR, "package.json"),
|
|
path.join(ROOT_DIR, "pnpm-lock.yaml"),
|
|
A2UI_RENDERER_DIR,
|
|
A2UI_APP_DIR,
|
|
];
|
|
|
|
const currentHash = await computeHash(INPUT_PATHS);
|
|
|
|
let skip = false;
|
|
if (await exists(HASH_FILE)) {
|
|
const previousHash = (await fs.readFile(HASH_FILE, "utf8")).trim();
|
|
if (previousHash === currentHash && (await exists(OUTPUT_FILE))) {
|
|
console.log("A2UI bundle up to date; skipping.");
|
|
skip = true;
|
|
}
|
|
}
|
|
|
|
if (!skip) {
|
|
try {
|
|
// We use `npx` or local `node_modules/.bin` executables.
|
|
// Since we are running with pnpm, `tsc` and `rolldown` should be in path if run via pnpm exec or similar.
|
|
// The bash script used `pnpm -s exec tsc`.
|
|
|
|
console.log("Running tsc...");
|
|
execSync(`pnpm -s exec tsc -p "${path.join(A2UI_RENDERER_DIR, "tsconfig.json")}"`, {
|
|
stdio: "inherit",
|
|
cwd: ROOT_DIR
|
|
});
|
|
|
|
console.log("Running rolldown...");
|
|
// rolldown might need to be run via pnpm exec or npx if not in path
|
|
execSync(`pnpm exec rolldown -c "${path.join(A2UI_APP_DIR, "rolldown.config.mjs")}"`, {
|
|
stdio: "inherit",
|
|
cwd: ROOT_DIR
|
|
});
|
|
|
|
await fs.writeFile(HASH_FILE, currentHash);
|
|
} catch (e) {
|
|
console.error("A2UI bundling failed. Re-run with: pnpm canvas:a2ui:bundle");
|
|
console.error("If this persists, verify pnpm deps and try again.");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function computeHash(inputs: string[]) {
|
|
const files: string[] = [];
|
|
|
|
async function walk(entryPath: string) {
|
|
const st = await fs.stat(entryPath);
|
|
if (st.isDirectory()) {
|
|
const entries = await fs.readdir(entryPath);
|
|
for (const entry of entries) {
|
|
await walk(path.join(entryPath, entry));
|
|
}
|
|
} else {
|
|
files.push(entryPath);
|
|
}
|
|
}
|
|
|
|
for (const input of inputs) {
|
|
await walk(input);
|
|
}
|
|
|
|
// Normalize paths for consistent sorting
|
|
files.sort((a, b) => normalize(a).localeCompare(normalize(b)));
|
|
|
|
const hash = createHash("sha256");
|
|
for (const filePath of files) {
|
|
const rel = normalize(path.relative(ROOT_DIR, filePath));
|
|
hash.update(rel);
|
|
hash.update("\0");
|
|
const content = await fs.readFile(filePath);
|
|
hash.update(content);
|
|
hash.update("\0");
|
|
}
|
|
|
|
return hash.digest("hex");
|
|
}
|
|
|
|
function normalize(p: string) {
|
|
return p.split(path.sep).join("/");
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
});
|