{"version":3,"file":"index.cjs","sources":["../src/footnote.ts","../src/references.ts","../src/footnotes.ts","../src/index.ts"],"sourcesContent":["import type { TokenizerAndRendererExtension, TokenizerThis } from 'marked'\nimport type { Footnote, Footnotes, LexerTokens } from './types.js'\n\n/**\n * Returns an extension object for parsing footnote definitions.\n */\nexport function createFootnote(lexer: LexerTokens, description: string) {\n  const footnotes: Footnotes = {\n    type: 'footnotes',\n    raw: description,\n    rawItems: [],\n    items: []\n  }\n\n  return {\n    name: 'footnote',\n    level: 'block',\n    childTokens: ['content'],\n    tokenizer(this: TokenizerThis, src: string) {\n      if (!lexer.hasFootnotes) {\n        this.lexer.tokens.push(footnotes)\n\n        lexer.tokens = this.lexer.tokens\n        lexer.hasFootnotes = true\n\n        // always begin with empty items\n        footnotes.rawItems = []\n        footnotes.items = []\n      }\n\n      const match =\n        /^\\[\\^([^\\]\\n]+)\\]:(?:[ \\t]+|[\\n]*?|$)([^\\n]*?(?:\\n|$)(?:\\n*?[ ]{4,}[^\\n]*)*)/.exec(\n          src\n        )\n\n      if (match) {\n        const [raw, label, text = ''] = match\n        let content = text.split('\\n').reduce((acc, curr) => {\n          return acc + '\\n' + curr.replace(/^(?:[ ]{4}|[\\t])/, '')\n        }, '')\n\n        const contentLastLine = content.trimEnd().split('\\n').pop()\n\n        content +=\n          // add lines after list, blockquote, codefence, and table\n          contentLastLine &&\n          /^[ \\t]*?[>\\-*][ ]|[`]{3,}$|^[ \\t]*?[|].+[|]$/.test(contentLastLine)\n            ? '\\n\\n'\n            : ''\n\n        const token: Footnote = {\n          type: 'footnote',\n          raw,\n          label,\n          refs: [],\n          content: this.lexer.blockTokens(content)\n        }\n\n        footnotes.rawItems.push(token)\n\n        return token\n      }\n    },\n    renderer() {\n      // skip it for now!\n      // we will render all `Footnote` through the footnotes renderer\n      return ''\n    }\n  } as TokenizerAndRendererExtension\n}\n","import type { TokenizerAndRendererExtension, TokenizerThis } from 'marked'\nimport type { FootnoteRef, Footnotes } from './types.js'\n\n/**\n * Escapes special HTML characters in a text to be inserted to an element body.\n */\nfunction escapeTextContent(text: string): string {\n  return text\n    .replaceAll('&', '&amp;')\n    .replaceAll('<', '&lt;')\n    .replaceAll('>', '&gt;')\n}\n\n/**\n * Returns an extension object for parsing inline footnote references.\n */\nexport function createFootnoteRef(\n  prefixId: string,\n  refMarkers = false,\n  keepLabels = false\n) {\n  let order = 0\n\n  return {\n    name: 'footnoteRef',\n    level: 'inline',\n    tokenizer(this: TokenizerThis, src: string) {\n      const match = /^\\[\\^([^\\]\\n]+)\\]/.exec(src)\n\n      if (match) {\n        const [raw, label] = match\n        const footnotes = this.lexer.tokens[0] as Footnotes\n        const filteredRawItems = footnotes.rawItems.filter(\n          item => item.label === label\n        )\n\n        if (!filteredRawItems.length) return\n\n        const rawFootnote = filteredRawItems[0]\n        const footnote = footnotes.items.filter(item => item.label === label)[0]\n\n        const ref: FootnoteRef = {\n          type: 'footnoteRef',\n          raw,\n          index: rawFootnote.refs.length,\n          id: '',\n          label\n        }\n\n        if (footnote) {\n          ref.id = footnote.refs[0].id\n          footnote.refs.push(ref)\n        } else {\n          order++\n          ref.id = String(order)\n          rawFootnote.refs.push(ref)\n          footnotes.items.push(rawFootnote)\n        }\n\n        return ref\n      }\n    },\n    renderer({ index, id, label }: FootnoteRef) {\n      order = 0 // reset order\n      const encodedLabel = encodeURIComponent(label)\n      const textLabel = keepLabels ? escapeTextContent(label) : id\n      const idSuffix = index > 0 ? `-${index + 1}` : ''\n\n      return `<sup><a id=\"${prefixId}ref-${encodedLabel}${idSuffix}\" href=\"#${\n        prefixId + encodedLabel\n      }\" data-${prefixId}ref aria-describedby=\"${prefixId}label\">${\n        refMarkers ? `[${textLabel}]` : textLabel\n      }</a></sup>`\n    }\n  } as TokenizerAndRendererExtension\n}\n","import type { RendererExtension, RendererThis } from 'marked'\nimport type { Footnotes } from './types.js'\n\n/**\n * Returns an extension object for rendering the list of footnotes.\n */\nexport function createFootnotes(\n  prefixId: string,\n  prefixData: string,\n  footnoteDivider: boolean,\n  sectionClass: string,\n  headingClass: string,\n  backRefLabel: string\n) {\n  return {\n    name: 'footnotes',\n    renderer(this: RendererThis, { raw, items = [] }: Footnotes) {\n      if (items.length === 0) return ''\n\n      const footnotesItemsHTML = items.reduce(\n        (acc, { label, content, refs }) => {\n          const encodedLabel = encodeURIComponent(label)\n          const parsedContent = this.parser.parse(content).trimEnd()\n          const isEndsWithP = parsedContent.endsWith('</p>')\n\n          let footnoteItem = `<li id=\"${prefixId + encodedLabel}\">\\n`\n          footnoteItem += isEndsWithP\n            ? parsedContent.replace(/<\\/p>$/, '')\n            : parsedContent\n\n          refs.forEach((_, i) => {\n            const ariaLabel = backRefLabel.replace('{0}', label)\n            let textLabel: string\n            let idSuffix: string\n            if (i > 0) {\n              const displayIndex = i + 1\n              textLabel = `↩<sup>${displayIndex}</sup>`\n              idSuffix = `-${displayIndex}`\n            } else {\n              textLabel = '↩'\n              idSuffix = ''\n            }\n            footnoteItem += ` <a href=\"#${prefixId}ref-${encodedLabel}${idSuffix}\" data-${prefixId}backref aria-label=\"${ariaLabel}\">${textLabel}</a>`\n          })\n\n          footnoteItem += isEndsWithP ? '</p>\\n' : '\\n'\n          footnoteItem += '</li>\\n'\n\n          return acc + footnoteItem\n        },\n        ''\n      )\n\n      let footnotesHTML = ''\n      if (footnoteDivider) {\n        footnotesHTML += `<hr data-${prefixData}footnotes>\\n`\n      }\n      let sectionAttrs = ''\n      if (sectionClass) {\n        sectionAttrs = ` class=\"${sectionClass}\"`\n      }\n      let headingAttrs = ''\n      if (headingClass) {\n        headingAttrs = ` class=\"${headingClass}\"`\n      }\n      footnotesHTML += `<section${sectionAttrs} data-${prefixData}footnotes>\\n`\n      footnotesHTML += `<h2 id=\"${prefixId}label\"${headingAttrs}>${raw.trimEnd()}</h2>\\n`\n      footnotesHTML += `<ol>\\n${footnotesItemsHTML}</ol>\\n`\n      footnotesHTML += '</section>\\n'\n\n      return footnotesHTML\n    }\n  } as RendererExtension\n}\n","import type { MarkedExtension } from 'marked'\nimport { createFootnote } from './footnote.js'\nimport { createFootnoteRef } from './references.js'\nimport { createFootnotes } from './footnotes.js'\nimport type { LexerTokens, Options } from './types.js'\n\n/**\n * A [marked](https://marked.js.org/) extension to support [GFM footnotes](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#footnotes).\n */\nexport default function markedFootnote(options: Options = {}): MarkedExtension {\n  const {\n    prefixId = 'footnote-',\n    prefixData = '',\n    description = 'Footnotes',\n    refMarkers = false,\n    footnoteDivider = false,\n    keepLabels = false,\n    sectionClass = 'footnotes',\n    headingClass = 'sr-only',\n    backRefLabel = 'Back to reference {0}'\n  } = options\n  const lexer: LexerTokens = { hasFootnotes: false, tokens: [] }\n\n  return {\n    extensions: [\n      createFootnote(lexer, description),\n      createFootnoteRef(prefixId, refMarkers, keepLabels),\n      createFootnotes(\n        prefixId,\n        prefixData,\n        footnoteDivider,\n        sectionClass,\n        headingClass,\n        backRefLabel\n      )\n    ],\n    walkTokens(token) {\n      if (\n        token.type === 'footnotes' &&\n        lexer.tokens.indexOf(token) === 0 &&\n        token.items.length\n      ) {\n        lexer.tokens[0] = { type: 'space', raw: '' }\n        lexer.tokens.push(token)\n      }\n\n      if (lexer.hasFootnotes) lexer.hasFootnotes = false\n    }\n  }\n}\n\nexport type { Footnote, FootnoteRef, Footnotes, Options } from './types.js'\n"],"names":["createFootnote","lexer","description","footnotes","src","match","raw","label","text","content","acc","curr","contentLastLine","token","escapeTextContent","createFootnoteRef","prefixId","refMarkers","keepLabels","order","filteredRawItems","item","rawFootnote","footnote","ref","index","id","encodedLabel","textLabel","idSuffix","createFootnotes","prefixData","footnoteDivider","sectionClass","headingClass","backRefLabel","items","footnotesItemsHTML","refs","parsedContent","isEndsWithP","footnoteItem","_","i","ariaLabel","displayIndex","footnotesHTML","sectionAttrs","headingAttrs","markedFootnote","options"],"mappings":"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"}