57 lines
1.7 KiB
TypeScript
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`
|
|
},
|
|
}],
|
|
}
|
|
}
|