#!/usr/bin/env node // embed.mjs — seal what fiapup.js borrows from oskiewar's pipeline into it. // // fiapup.js is one global script, like oskiewar.js, because that is all the // console host loads. It carries three things it doesn't own, each read from // the file oskiewar uses so the two games share one pipeline: // // xbox/live/object-lisp.mjs the object compiler (KidLisp → baked sketches) // xbox/live/frame-vm.mjs the frame-program interpreter, run in-game on // hosts that have no `frame` (the console until // R6, and this game's own web shell) // xbox/fiapup/objects/*.lisp the pup, the yard and the props // // It writes them between `// ` and `// ` and nothing else. // // node xbox/fiapup/embed.mjs rewrite the block // node xbox/fiapup/embed.mjs --check exit 1 if the block is stale import { readFileSync, writeFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; const here = new URL("./", import.meta.url); const live = new URL("../live/", import.meta.url); const read = (url) => readFileSync(url, "utf8"); export const gamePath = fileURLToPath(new URL("fiapup.js", here)); export const start = "// "; export const end = "// "; // What the game draws, by the name it asks for, and the file each compiles from. export const objects = { puppy: "puppy-flat", camp: "camp-flat", ball: "ball-flat", rope: "rope-flat", hand: "hand-flat", treat: "treat-flat", // the meadow's things, stamped into chunk sketches (flower … birch) or // drawn where they move (butterfly, sheep) flower: "flower-flat", tuft: "tuft-flat", rock: "rock-flat", pine: "pine-flat", birch: "birch-flat", butterfly: "butterfly-flat", sheep: "sheep-flat" }; // A module's source with its `export` keywords taken off, and the names it // exported. `export default x;` lines go; the named export already covers x. function unexport(source, file) { const names = []; const body = source.replace(/^export default .*$/gm, "") .replace(/^export (async function\*?|function\*?|const|let|class) (\w+)/gm, (_, kind, name) => { names.push(name); return `${kind} ${name}`; }); if (/^import /m.test(body)) throw new Error(`${file}: the game can't carry imports`); return { body, names }; } const indent = (text) => text.trimEnd().split("\n").map((line) => (line ? " " + line : line)).join("\n"); export function generate() { const lisp = unexport(read(new URL("object-lisp.mjs", live)), "object-lisp.mjs"); const vm = unexport(read(new URL("frame-vm.mjs", live)), "frame-vm.mjs"); const sources = Object.entries(objects).map(([name, file]) => ` ${name}: ${JSON.stringify(read(new URL(`objects/${file}.lisp`, here)))},`); return [ start, "// Generated by xbox/fiapup/embed.mjs from xbox/live/object-lisp.mjs,", "// xbox/live/frame-vm.mjs and xbox/fiapup/objects/*.lisp. Don't edit here:", "// edit those and rerun the tool.", "const fiapupObjects = (() => {", " const objectLisp = (() => {", indent(lisp.body), ` return { ${lisp.names.join(", ")} };`, " })();", " const sources = {", ...sources, " };", " const compiled = {};", " for (const name in sources) compiled[name] = objectLisp.compile(sources[name], name);", " return compiled;", "})();", "const fiapupFrameVm = (() => {", indent(vm.body), ` return { ${vm.names.join(", ")} };`, "})();", end, ].join("\n"); } export function embedded(source = read(gamePath)) { const a = source.indexOf(start), b = source.indexOf(end); return a < 0 || b < 0 ? null : source.slice(a, b + end.length); } function write() { const source = read(gamePath), block = generate(), current = embedded(source); if (!current) throw new Error("fiapup.js: no // block to fill"); const next = source.replace(current, () => block); if (next !== source) writeFileSync(gamePath, next); return next !== source; } if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { if (process.argv.includes("--check")) { const fresh = embedded() === generate(); console.log(fresh ? "fiapup.js sealed block is current" : "fiapup.js sealed block is stale: run node xbox/fiapup/embed.mjs"); process.exit(fresh ? 0 : 1); } console.log(write() ? "sealed the compiler, the frame vm and the objects into fiapup.js" : "sealed block already current"); }