feat(cli): add guide icon validation and warnings in sync script

This commit is contained in:
Prad Nukala committed 2026-09-30 15:17:39 -04:00
1 parent 790e256127
commit e931391200
1 file changed
+12 -4
+12 -4
View File
@@ -27,7 +27,9 @@
* problem page links to it. A guide may be filed into a category `(group)`
* folder (e.g. `guides/(2-algorithms)/`) — no route segment, so
* `/guides/<slug>` holds — and is found there by slug; a new placeholder
* lands at the `guides/` root for a human to file (and give an `icon:`).
* lands at the `guides/` root for a human to file (and give an `icon:`,
* which every badge linking to the guide shows; sync warns while one is
* missing or shared).
*
* The sidebar group is the campaign TOPIC from the README table (`tree-dfs/`,
* ordered by topic number via a script-owned `meta.ts`). A problem outside the
@@ -777,15 +779,21 @@ for (const tag of guideTags) {
// tag has no landing (`tagTarget()`). A guide whose tag a landing has since
// claimed has no backlinks left. Counts move with every solved problem, so the
// badge is rewritten whenever it differs; the rest of the guide is untouched.
// The same pass warns when a guide's hand-picked `icon:` is one a topic group
// owns (`TOPIC_ICONS`), so a guide row never reads as a topic row.
// The same pass checks each guide's hand-picked `icon:`, which is also the icon
// on every problem-page badge linking to the guide: it must exist, must not be
// a topic group's (`TOPIC_ICONS`), so a guide badge never reads as a topic's,
// and must not be another guide's.
const topicIcons = new Set(Object.values(TOPIC_ICONS));
const guideIcons = new Map<string, string>(); // icon -> the guide that took it first
for await (const rel of new Bun.Glob("guides/**/*.mdx").scan(DOCS)) {
const slug = basename(rel, ".mdx");
const count = TOPIC_LANDINGS[slug] ? 0 : (tagged.get(slug) ?? 0);
const page = await Bun.file(join(DOCS, rel)).text();
const icon = page.match(/^---\n[\s\S]*?\n---/)?.[0].match(/^icon: (\S+)$/m)?.[1];
if (icon && topicIcons.has(icon)) console.warn(`${rel}: icon ${JSON.stringify(icon)} is a topic's (TOPIC_ICONS); pick another`);
if (!icon) console.warn(`${rel}: no icon; its badges render without one — file it and add \`icon:\``);
else if (topicIcons.has(icon)) console.warn(`${rel}: icon ${JSON.stringify(icon)} is a topic's (TOPIC_ICONS); pick another`);
else if (guideIcons.has(icon)) console.warn(`${rel}: icon ${JSON.stringify(icon)} is ${guideIcons.get(icon)}'s too; pick another`);
else guideIcons.set(icon, rel);
const next = withBacklinkBadge(page, count);
if (next === page) continue;
await Bun.write(join(DOCS, rel), next);