mackenzii/projects/frontend/src/utilities/marked.ts

57 lines
1.7 KiB
TypeScript

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 `<section id=${slugger.slug(token.heading)}>\n${this.parser.parse(token.tokens!)}</section>\n`
},
}],
}
}