#!/usr/bin/env node // Regenerates the devlog data module from the last few commits. The client has // no build step, so the list the main menu shows has to live in the tree; this // script keeps it current and is run by the pre-commit hook (and by hand with // `node scripts/generate-devlog.js`). // // The file is written only when it changes, so an unchanged history does not // touch the working tree. import { execFileSync } from "node:child_process"; import { readFileSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; const ROOT = dirname(dirname(fileURLToPath(import.meta.url))); const OUT = join(ROOT, "client", "js", "devlog_data.js"); // How many commits the Devlog shows. const COUNT = 10; // Field separator inside a commit record and the record separator between // commits. Neither byte is legal in a git commit message. const FIELD = "\x1f"; const RECORD = "\x1e"; function lastCommits() { const pretty = `--pretty=format:%h${FIELD}%cs${FIELD}%s${FIELD}%b${RECORD}`; const out = execFileSync("git", ["log", `-n${COUNT}`, pretty], { cwd: ROOT, encoding: "utf8", }); return out .split(RECORD) // `git log` puts a newline between records; drop the leading ones. .map((record) => record.replace(/^\n+/, "")) .filter((record) => record.trim().length > 0) .map((record) => { const [hash, date, subject, body = ""] = record.split(FIELD); return { hash: hash.trim(), date: date.trim(), subject: subject.trim(), body: body.trim(), }; }); } function source(commits) { return ( "// Generated by scripts/generate-devlog.js from `git log`; do not edit.\n" + "// The pre-commit hook refreshes it so the main menu shows the latest commits.\n" + `export const DEVLOG = ${JSON.stringify(commits, null, 2)};\n` ); } let commits; try { commits = lastCommits(); } catch (error) { console.warn(`generate-devlog: could not read the git history (${error.message})`); process.exit(0); } const next = source(commits); let previous = ""; try { previous = readFileSync(OUT, "utf8"); } catch { // First run: the file does not exist yet. } if (next === previous) { console.log(`generate-devlog: ${OUT} is already up to date (${commits.length} commits)`); } else { writeFileSync(OUT, next); console.log(`generate-devlog: wrote ${commits.length} commits to ${OUT}`); }