a programming education platform
www.hypercommit.com
education
1import * as React from "react"
2
3function nodeToText(node: React.ReactNode): string {
4 if (node == null || typeof node === "boolean") return ""
5 if (typeof node === "string" || typeof node === "number") return String(node)
6 if (Array.isArray(node)) return node.map(nodeToText).join("")
7 if (React.isValidElement(node)) {
8 const props = node.props as { children?: React.ReactNode }
9 return nodeToText(props.children)
10 }
11 return ""
12}
13
14export function slugifyHeading(node: React.ReactNode): string {
15 return nodeToText(node)
16 .toLowerCase()
17 .replace(/[`'"]/g, "")
18 .replace(/[^a-z0-9]+/g, "-")
19 .replace(/^-+|-+$/g, "")
20}
21
22export function extractMdxHeadings(
23 source: string,
24 level: 2 | 3 = 2
25): { text: string; slug: string }[] {
26 const prefix = "#".repeat(level) + " "
27 const headings: { text: string; slug: string }[] = []
28 let inFence = false
29 for (const raw of source.split("\n")) {
30 const line = raw.trimEnd()
31 if (line.startsWith("```")) {
32 inFence = !inFence
33 continue
34 }
35 if (inFence) continue
36 if (line.startsWith(prefix)) {
37 const text = line.slice(prefix.length).trim()
38 headings.push({ text, slug: slugifyHeading(text) })
39 }
40 }
41 return headings
42}