62 lines
2.2 KiB
JavaScript
62 lines
2.2 KiB
JavaScript
// Appends a batch of new cards to a category file.
|
|
// node tools/add-cards.mjs <category> <batch.json>
|
|
// The batch is a JSON array of cards. Ids must be new across ALL card files, and every card's
|
|
// `category` must match. Existing cards are kept as they are. The file is rewritten in a compact
|
|
// layout (small objects on one line). Run `go test ./server` afterwards for the full validation.
|
|
|
|
import fs from "node:fs";
|
|
import { fileURLToPath } from "node:url";
|
|
import { format } from "./cardfmt.mjs";
|
|
|
|
const dir = fileURLToPath(new URL("../web/data/cards/", import.meta.url));
|
|
const [category, batchPath] = process.argv.slice(2);
|
|
if (!category || !batchPath) {
|
|
console.error("usage: node tools/add-cards.mjs <category> <batch.json>");
|
|
process.exit(2);
|
|
}
|
|
|
|
const manifest = JSON.parse(fs.readFileSync(dir + "index.json", "utf8")).files;
|
|
const file = `${category}.json`;
|
|
if (!manifest.includes(file)) {
|
|
console.error(`unknown category "${category}" (known: ${manifest.map((f) => f.replace(".json", "")).join(", ")})`);
|
|
process.exit(2);
|
|
}
|
|
|
|
const existing = {};
|
|
const used = new Set();
|
|
for (const f of manifest) {
|
|
existing[f] = JSON.parse(fs.readFileSync(dir + f, "utf8"));
|
|
for (const c of existing[f]) used.add(c.id);
|
|
}
|
|
|
|
let batch;
|
|
try {
|
|
batch = JSON.parse(fs.readFileSync(batchPath, "utf8"));
|
|
} catch (e) {
|
|
console.error(`cannot read the batch: ${e.message}`);
|
|
process.exit(1);
|
|
}
|
|
if (!Array.isArray(batch)) {
|
|
console.error("the batch must be a JSON array of cards");
|
|
process.exit(1);
|
|
}
|
|
|
|
const problems = [];
|
|
const seen = new Set();
|
|
for (const [i, c] of batch.entries()) {
|
|
const who = c?.id ?? `#${i}`;
|
|
if (!c?.id) problems.push(`${who}: missing id`);
|
|
else if (used.has(c.id) || seen.has(c.id)) problems.push(`${who}: id already exists`);
|
|
seen.add(c?.id);
|
|
if (c?.category !== category) problems.push(`${who}: category must be "${category}"`);
|
|
if (!c?.title || !c?.body || !c?.yes || !c?.no) problems.push(`${who}: needs title, body, yes and no`);
|
|
}
|
|
if (problems.length) {
|
|
console.error("batch rejected, nothing written:\n " + problems.join("\n "));
|
|
process.exit(1);
|
|
}
|
|
|
|
const all = [...existing[file], ...batch];
|
|
fs.writeFileSync(dir + file, `${format(all)}\n`);
|
|
console.log(`${file}: +${batch.length} cards, ${all.length} in total`);
|