fix: render heading IDs so anchor links resolve

marked v12 dropped the headerIds option, so headings rendered with no
id attributes and links like [Releases](#releases-and-contributing)
silently failed to scroll. Add a heading renderer that emits a
GitHub-style slug id, with a numeric suffix for duplicates within a
document.

Fixes #390.
This commit is contained in:
tdurieux
2026-05-03 19:44:18 +02:00
parent e18961208a
commit 9feeab1055
+22
View File
@@ -143,6 +143,19 @@ function parseGithubUrl(url) {
} }
} }
// GitHub-style heading slug: lowercase, drop punctuation other than `-_`,
// collapse runs of whitespace into a single dash. Used so anchor links like
// `[Releases](#releases-and-contributing)` actually jump to the heading
// (marked v12 dropped `headerIds`, so headings now have no id by default).
function slugifyHeading(text) {
return String(text)
.toLowerCase()
.replace(/<[^>]+>/g, "")
.replace(/[^\p{L}\p{N}\s_-]/gu, "")
.trim()
.replace(/\s+/g, "-");
}
function renderMD(md, baseUrlValue) { function renderMD(md, baseUrlValue) {
marked.use( marked.use(
markedEmoji({ markedEmoji({
@@ -162,6 +175,15 @@ function renderMD(md, baseUrlValue) {
return rendererLink.call(this, href, title, text); return rendererLink.call(this, href, title, text);
}; };
const slugCounts = {};
renderer.heading = function (text, level, raw) {
const base = slugifyHeading(raw || text);
const n = slugCounts[base] || 0;
slugCounts[base] = n + 1;
const id = n === 0 ? base : `${base}-${n}`;
return `<h${level} id="${id}">${text}</h${level}>\n`;
};
marked.setOptions({ marked.setOptions({
renderer: renderer, renderer: renderer,
pedantic: false, pedantic: false,