refactor: migrate a2ui bundle script to typescript
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.
This commit is contained in:
parent
72a3046541
commit
624c422422
@ -143,7 +143,7 @@
|
||||
"protocol:gen": "node --import tsx scripts/protocol-gen.ts",
|
||||
"protocol:gen:swift": "node --import tsx scripts/protocol-gen-swift.ts",
|
||||
"protocol:check": "pnpm protocol:gen && pnpm protocol:gen:swift && git diff --exit-code -- dist/protocol.schema.json apps/macos/Sources/MoltbotProtocol/GatewayModels.swift",
|
||||
"canvas:a2ui:bundle": "bash scripts/bundle-a2ui.sh",
|
||||
"canvas:a2ui:bundle": "node --import tsx scripts/bundle-a2ui.ts",
|
||||
"check:loc": "node --import tsx scripts/check-ts-max-loc.ts --max 500"
|
||||
},
|
||||
"keywords": [],
|
||||
|
||||
119
scripts/bundle-a2ui.ts
Normal file
119
scripts/bundle-a2ui.ts
Normal file
@ -0,0 +1,119 @@
|
||||
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);
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user