import { MarkedExtension } from 'marked' import GithubSlugger from 'github-slugger' let slugger: GithubSlugger let sectionLevel = 1 const sectionRegexps = new Array(7).fill(undefined).map((_, i) => new RegExp(`^ {0,3}(#{${i + 1}} )[^]*?(?:\\n(?=\\1)|$)`)) const headingRegex = new RegExp(/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/) /** * Extension which creates sections for each heading found in a markdown document, * using the {@link GithubSlugger} to additionally create id's for each section. * @returns marked extension factory */ export const headingSectionsExtension = (): MarkedExtension => { return { hooks: { preprocess(src) { slugger = new GithubSlugger() return src }, postprocess(html) { return html } }, extensions: [{ name: 'headingSections', level: 'block', start(src: string) { // Match when # is at the beginning of a line. return src.match(/^ {0,3}#/m)?.index }, tokenizer(src: string) { const match = sectionRegexps[sectionLevel].exec(src) if (!match) { return } sectionLevel++ // Tokenize text inside the section. // Only add sectionBlock token for headers one level up from current level. const tokens = this.lexer.blockTokens(match[0]) sectionLevel-- return { type: 'headingSections', heading: headingRegex.exec(match[0])?.[2].trim(), raw: match[0], level: sectionLevel + 1, tokens, } }, renderer(token) { return `
\n${this.parser.parse(token.tokens!)}
\n` }, }], } }