Skip to content

Commit eb33620

Browse files
committed
Add markdown syntax for section links
1 parent b97bf36 commit eb33620

File tree

5 files changed

+105
-60
lines changed

5 files changed

+105
-60
lines changed

build/build.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import rehypeSlug from "rehype-slug";
77
import rehypeStringify from "rehype-stringify";
88
import remarkFlexibleContainers from "remark-flexible-containers";
99
import remarkGfm from "remark-gfm";
10+
import remarkHeadingId from "remark-heading-id";
11+
import remarkHeadings from "@vcarl/remark-headings";
1012
import remarkNumberHeadings from "./remark-number-headings.js";
1113
import remarkPresetLintMarkdownStyleGuide from "remark-preset-lint-markdown-style-guide";
1214
import remarkRehype from "remark-rehype";
15+
import remarkSectionLinks from "./remark-section-links.js";
1316
import remarkToc from "remark-toc";
1417
import remarkValidateLinks from "remark-validate-links";
1518
import torchLight from "remark-torchlight";
@@ -24,12 +27,15 @@ dotenv.config();
2427
.use(remarkGfm)
2528
.use(torchLight)
2629
.use(remarkFlexibleContainers)
30+
.use(remarkHeadingId)
2731
.use(remarkNumberHeadings, {
2832
startDepth: 2,
2933
skip: ["Abstract", "Note to Readers", "Table of Contents", "Authors' Addresses", "\\[.*\\]", "draft-.*"],
3034
appendixToken: "[Appendix]",
3135
appendixPrefix: "Appendix"
3236
})
37+
.use(remarkHeadings)
38+
.use(remarkSectionLinks)
3339
.use(remarkToc, {
3440
tight: true,
3541
heading: "Table of Contents",

build/remark-section-links.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { visit } from "unist-util-visit";
2+
3+
4+
const sectionLink = /\{\{(?<id>.*?)\}\}/u;
5+
6+
const remarkSectionLinks = (options) => (tree, vfile) => {
7+
// Heading data comes from @vcarl/remark-headings
8+
const headings = {};
9+
for (const heading of vfile.data.headings) {
10+
if (heading?.data?.id) {
11+
headings[heading.data.id] = heading.value;
12+
}
13+
}
14+
15+
visit(tree, "text", (node, index, parent) => {
16+
const match = node.value.match(sectionLink);
17+
18+
if (match) {
19+
if (!(match.groups.id in headings)) {
20+
throw Error(`SectionLinkError: No header found with id "${match.groups.id}"`)
21+
}
22+
const headerText = headings[match.groups.id];
23+
24+
const beforeNode = { type: "text", value: node.value.slice(0, match.index) };
25+
const afterNode = { type: "text", value: node.value.slice(match.index + match[0].length) };
26+
const linkNode = {
27+
type: "link",
28+
title: headerText,
29+
url: `#${match.groups.id}`,
30+
children: [
31+
{
32+
type: "text",
33+
value: "Section " + headerText.slice(0, headerText.indexOf(". "))
34+
}
35+
]
36+
};
37+
38+
parent.children.splice(index, 1, beforeNode, linkNode, afterNode);
39+
}
40+
});
41+
};
42+
43+
export default remarkSectionLinks;

0 commit comments

Comments
 (0)