Skip to content

Commit 224b557

Browse files
Create update-link-health.mjs
1 parent 29f3309 commit 224b557

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed

scripts/update-link-health.mjs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// scripts/update-link-health.mjs
2+
import fs from "node:fs";
3+
import path from "node:path";
4+
import url from "node:url";
5+
6+
const repoRoot = process.cwd();
7+
const reportPath = path.join(repoRoot, "lychee", "out.json");
8+
const readmePath = path.join(repoRoot, "README.md");
9+
10+
const START = "<!--LINK_HEALTH_START-->";
11+
const END = "<!--LINK_HEALTH_END-->";
12+
13+
function emoji(status) {
14+
return status === "Ok" ? "✅" : "❌";
15+
}
16+
17+
function escapeMd(s) {
18+
return s.replace(/\|/g, "\\|");
19+
}
20+
21+
function loadReport() {
22+
if (!fs.existsSync(reportPath)) {
23+
return { links: [] };
24+
}
25+
const raw = fs.readFileSync(reportPath, "utf8");
26+
try {
27+
return JSON.parse(raw);
28+
} catch (e) {
29+
console.error("Failed to parse lychee JSON:", e);
30+
return { links: [] };
31+
}
32+
}
33+
34+
function buildTable(links) {
35+
// links: array of { target, status, status_code, error, sources: [{file, line}] }
36+
// Deduplicate by URL, prefer failing status if any source fails.
37+
const byUrl = new Map();
38+
for (const l of links) {
39+
const key = l.target;
40+
const current = byUrl.get(key);
41+
if (!current) {
42+
byUrl.set(key, l);
43+
} else {
44+
// If any occurrence is failing, keep that
45+
const curOk = current.status === "Ok";
46+
const newOk = l.status === "Ok";
47+
if (curOk && !newOk) byUrl.set(key, l);
48+
}
49+
}
50+
51+
const rows = [...byUrl.values()]
52+
.sort((a, b) => a.target.localeCompare(b.target))
53+
.map((l) => {
54+
const status = emoji(l.status);
55+
const code = l.status_code ?? "";
56+
const err = l.error ? " " + escapeMd(String(l.error)).slice(0, 120) : "";
57+
const src = Array.isArray(l.sources) && l.sources.length
58+
? l.sources
59+
.slice(0, 3)
60+
.map((s) => `${path.basename(s.file)}:${s.line ?? ""}`)
61+
.join(", ")
62+
: "";
63+
return `| ${status} | ${escapeMd(l.target)} | ${code} | ${escapeMd(src)} |${err ? " " + err : ""}`;
64+
});
65+
66+
const header =
67+
"| Status | URL | Code | Found in (file:line) |\n|---|---|---:|---|\n";
68+
return header + (rows.length ? rows.join("\n") : "| ✅ | _No links found_ | | |");
69+
}
70+
71+
function updateReadme(table) {
72+
if (!fs.existsSync(readmePath)) {
73+
throw new Error("README.md not found");
74+
}
75+
const md = fs.readFileSync(readmePath, "utf8");
76+
77+
// Ensure markers exist; if not, append at end
78+
let newMd = md;
79+
if (!md.includes(START) || !md.includes(END)) {
80+
newMd += `\n\n## Link Health\n${START}\n_The table below is updated automatically by a scheduled job._\n${END}\n`;
81+
}
82+
83+
const before = newMd.split(START)[0];
84+
const after = newMd.split(END)[1] ?? "";
85+
const middle = `\n${table}\n`;
86+
return before + START + middle + END + after;
87+
}
88+
89+
function main() {
90+
const data = loadReport();
91+
const links = Array.isArray(data?.links) ? data.links : [];
92+
const table = buildTable(links);
93+
const updated = updateReadme(table);
94+
95+
fs.writeFileSync(readmePath, updated, "utf8");
96+
console.log("README Link Health section updated.");
97+
}
98+
99+
main();

0 commit comments

Comments
 (0)