import type { MarkedExtension, Tokens } from 'marked' /** * A marked extension to support footnotes syntax. * Syntax: * This is a footnote reference[^1][^2]. * * [^1]: ..... * [^2]: ..... */ interface MapContent { index: number text: string } const fnMap = new Map() export function markedFootnotes(): MarkedExtension { return { extensions: [ { name: `footnoteDef`, level: `block`, start(src: string) { fnMap.clear() return src.match(/^\[\^/)?.index }, tokenizer(src: string) { const match = src.match(/^\[\^(.*)\]:(.*)/) if (match) { const [raw, fnId, text] = match const index = fnMap.size + 1 fnMap.set(fnId, { index, text }) return { type: `footnoteDef`, raw, fnId, index, text, } } return undefined }, renderer(token: Tokens.Generic) { const { index, text, fnId } = token const fnInner = ` ${index}. ${text} \u21A9\uFE0E
` if (index === 1) { return `

${fnInner}` } if (index === fnMap.size) { return `${fnInner}

` } return fnInner }, }, { name: `footnoteRef`, level: `inline`, start(src: string) { return src.match(/\[\^/)?.index }, tokenizer(src: string) { const match = src.match(/^\[\^(.*?)\]/) if (match) { const [raw, fnId] = match if (fnMap.has(fnId)) { return { type: `footnoteRef`, raw, fnId, } } } }, renderer(token: Tokens.Generic) { const { fnId } = token const { index } = fnMap.get(fnId) as MapContent return ` \[${index}\] ` }, }, ], } }