31 lines
881 B
TypeScript
31 lines
881 B
TypeScript
export type Heading = { id: string; text: string; level: number; line: number };
|
|
export function headingsFor(content: string): Heading[] {
|
|
let fenced = false;
|
|
const counts = new Map<string, number>();
|
|
return content.split("\n").flatMap((line, index) => {
|
|
if (/^\s*(```|~~~)/.test(line)) {
|
|
fenced = !fenced;
|
|
return [];
|
|
}
|
|
const match = !fenced && line.match(/^(#{2,3})\s+(.+?)\s*#*$/);
|
|
if (!match) return [];
|
|
const text = match[2].replace(/[`*_]/g, "");
|
|
const base =
|
|
text
|
|
.toLowerCase()
|
|
.replace(/[^\p{L}\p{N}\s-]/gu, "")
|
|
.trim()
|
|
.replace(/\s+/g, "-") || "section";
|
|
const count = counts.get(base) ?? 0;
|
|
counts.set(base, count + 1);
|
|
return [
|
|
{
|
|
id: count ? `${base}-${count}` : base,
|
|
text,
|
|
level: match[1].length,
|
|
line: index + 1,
|
|
},
|
|
];
|
|
});
|
|
}
|