Files
systemic-game-design-course/tools/verify-references.mjs
T
Lucas WintherandClaude Opus 5.5 e04dc1a11f feat: module 05 assemble
Cards become JSON data in course/cards (one file per card, validated
against card.schema.json and the catalog); the M5 references load them
via registerCards and interpret effects, replacing content closures.
Adds assemble(base, donor, half), the assemble/buffHand effects with an
injected chooser, Fleshshape as the data-only Expansion Drop, and stages
course/cards in verify-references and start-module.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 03:17:21 +01:00

63 lines
2.6 KiB
JavaScript

#!/usr/bin/env node
// For each module <= N with a reference/: copy reference + tests of modules 01..module into a temp dir, run tests.
// course/cards (the card data, loaded by the references from Module 5) is staged as <dir>/cards.
import { cpSync, existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
import { spawnSync } from "node:child_process";
import { tmpdir } from "node:os";
import { basename, join } from "node:path";
import { courseRoot, moduleDirs, testFiles, fail } from "./lib.mjs";
// A staged reference has no node_modules, so TS installs the reference's own
// pinned devDependencies first (a bare `npx vitest` would fetch an unrelated version).
const DEFAULTS = {
rust: "cargo test",
ts: "npm install --no-audit --no-fund --loglevel=error && npx vitest run",
};
export function commandFor(lang, env = process.env) {
return env[`VERIFY_CMD_${lang.toUpperCase()}`] || DEFAULTS[lang];
}
export function planVerification(mods, upTo) {
return mods
.filter((m) => Number(m.num) >= 1 && Number(m.num) <= upTo)
.map((m) => ({
...m,
langs: ["rust", "ts"].filter((l) => existsSync(join(m.path, "reference", l))),
testModules: mods.filter((t) => Number(t.num) >= 1 && Number(t.num) <= Number(m.num)),
}))
.filter((p) => p.langs.length > 0);
}
export function stage(plan, lang) {
const dir = mkdtempSync(join(tmpdir(), `verify-${plan.num}-${lang}-`));
cpSync(join(plan.path, "reference", lang), dir, { recursive: true });
mkdirSync(join(dir, "tests"), { recursive: true });
for (const t of plan.testModules) {
for (const f of testFiles(t.path, lang)) cpSync(f, join(dir, "tests", basename(f)));
}
const cards = join(courseRoot(), "cards");
if (existsSync(cards)) cpSync(cards, join(dir, "cards"), { recursive: true });
return dir;
}
if (import.meta.main) {
const all = moduleDirs();
const upTo = process.argv[2] ? Number(process.argv[2]) : Math.max(0, ...all.map((m) => Number(m.num)));
const plans = planVerification(all, upTo);
if (!plans.length) console.log("verify-references: no modules with reference/ up to " + upTo);
let failed = false;
for (const p of plans) {
for (const lang of p.langs) {
const dir = stage(p, lang);
const r = spawnSync(commandFor(lang), { cwd: dir, shell: true, encoding: "utf8" });
const ok = r.status === 0;
failed ||= !ok;
console.log(`module ${p.num}-${p.slug} [${lang}]: ${ok ? "ok" : "FAIL"}`);
if (!ok) console.log((r.stdout || "") + (r.stderr || ""));
rmSync(dir, { recursive: true, force: true });
}
}
if (failed) process.exit(1);
}