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]>
54 lines
2.4 KiB
JavaScript
54 lines
2.4 KiB
JavaScript
#!/usr/bin/env node
|
|
// Copies module NN's tests for the learner's track into <workspace>/tests/.
|
|
import { cpSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
import { basename, join } from "node:path";
|
|
import { moduleDirs, courseRoot, testFiles, fail } from "./lib.mjs";
|
|
|
|
const [num, ws] = process.argv.slice(2);
|
|
if (!/^\d\d$/.test(num || "") || !ws) fail("usage: start-module.mjs <NN> <workspaceDir>");
|
|
if (num === "00") fail("Module 00 is not startable: M0 uses the naive-engine/ project, not a workspace.");
|
|
|
|
const cfg = JSON.parse(readFileSync(join(ws, ".course.json"), "utf8"));
|
|
const track = cfg.track;
|
|
if (track !== "rust" && track !== "ts") fail(`bad track in .course.json: ${track}`);
|
|
|
|
const mods = moduleDirs();
|
|
const mod = mods.find((m) => m.num === num);
|
|
if (!mod) fail(`no module ${num} in ${courseRoot()}`);
|
|
|
|
if (num !== "01") {
|
|
const prevNum = String(Number(num) - 1).padStart(2, "0");
|
|
const prev = mods.find((m) => m.num === prevNum);
|
|
if (prev) {
|
|
const missing = testFiles(prev.path, track).map((f) => basename(f))
|
|
.filter((f) => !existsSync(join(ws, "tests", f)));
|
|
if (missing.length) fail(`Cannot start module ${num}: finish module ${prevNum} first. Missing tests/${missing.join(", tests/")}`);
|
|
}
|
|
}
|
|
|
|
const files = testFiles(mod.path, track);
|
|
if (!files.length) fail(`module ${num} has no ${track} tests`);
|
|
mkdirSync(join(ws, "tests"), { recursive: true });
|
|
for (const f of files) cpSync(f, join(ws, "tests", basename(f)));
|
|
console.log(`Copied ${files.length} test file(s) for module ${num} into ${join(ws, "tests")}`);
|
|
|
|
// The course's card data (read by your content from Module 5). Refreshed on every start;
|
|
// card files you add yourself are kept.
|
|
const cards = join(courseRoot(), "cards");
|
|
if (existsSync(cards)) {
|
|
cpSync(cards, join(ws, "cards"), { recursive: true });
|
|
console.log(`Copied the course card data into ${join(ws, "cards")}`);
|
|
}
|
|
|
|
// Harness additions: module-local note, else the "- M<N>:" line of course/harness.md.
|
|
const note = join(mod.path, "harness-adds.md");
|
|
let adds = existsSync(note) ? readFileSync(note, "utf8").trim() : "";
|
|
if (!adds) {
|
|
const h = join(courseRoot(), "harness.md");
|
|
if (existsSync(h)) {
|
|
const re = new RegExp(`^\\s*[-*]\\s*\\**M${Number(num)}\\b.*$`, "m");
|
|
adds = (re.exec(readFileSync(h, "utf8")) || [""])[0].trim();
|
|
}
|
|
}
|
|
console.log(adds ? `Harness functions added by module ${num}:\n${adds}` : `No harness additions recorded for module ${num} (see course/harness.md).`);
|