UNPKG

markdown-it

Version:

Markdown-it - modern pluggable markdown parser.

1 lines 256 kB
{"version":3,"file":"markdown-it.mjs","names":[],"sources":["../src/common/utils.ts","../src/helpers/parse_link_label.ts","../src/helpers/parse_link_destination.ts","../src/helpers/parse_link_title.ts","../src/helpers/index.ts","../src/token.ts","../src/ruler.ts","../src/renderer.ts","../src/rules_core/state_core.ts","../src/rules_core/normalize.ts","../src/rules_core/block.ts","../src/rules_core/strip_references.ts","../src/rules_core/inline.ts","../src/rules_core/linkify.ts","../src/rules_core/replacements.ts","../src/rules_core/smartquotes.ts","../src/rules_core/text_join.ts","../src/parser_core.ts","../src/rules_block/state_block.ts","../src/rules_block/table.ts","../src/rules_block/code.ts","../src/rules_block/fence.ts","../src/rules_block/blockquote.ts","../src/rules_block/hr.ts","../src/rules_block/list.ts","../src/rules_block/reference.ts","../src/common/html_blocks.ts","../src/common/html_re.ts","../src/rules_block/html_block.ts","../src/rules_block/heading.ts","../src/rules_block/lheading.ts","../src/rules_block/paragraph.ts","../src/parser_block.ts","../src/rules_inline/state_inline.ts","../src/rules_inline/text.ts","../src/rules_inline/linkify.ts","../src/rules_inline/newline.ts","../src/rules_inline/escape.ts","../src/rules_inline/backticks.ts","../src/rules_inline/strikethrough.ts","../src/rules_inline/emphasis.ts","../src/rules_inline/link.ts","../src/rules_inline/image.ts","../src/rules_inline/autolink.ts","../src/rules_inline/html_inline.ts","../src/rules_inline/entity.ts","../src/rules_inline/balance_pairs.ts","../src/rules_inline/fragments_join.ts","../src/parser_inline.ts","../src/presets/default.ts","../src/presets/zero.ts","../src/presets/commonmark.ts","../src/markdownit.ts","../src/index.ts"],"sourcesContent":["/**\n * Common utility functions exposed through `md.utils` for use by plugins.\n *\n * @module md.utils\n */\n\nimport * as mdurl from 'mdurl'\nimport * as ucmicro from 'uc.micro'\nimport { decodeHTMLStrict } from 'entities'\n\n/** @hidden */\ntype ClassToWrap = new (...args: any[]) => object\n\n/** Wraps a class so it can be called with or without `new`. */\nfunction callable<T extends ClassToWrap> (\n cls: T\n): T & ((...args: ConstructorParameters<T>) => InstanceType<T>)\nfunction callable<T extends ClassToWrap> (cls: T) {\n const wrapper = function (...args: ConstructorParameters<T>) {\n const newTarget =\n new.target && new.target !== wrapper\n ? new.target\n : cls\n\n return Reflect.construct(cls, args, newTarget)\n }\n\n Object.defineProperty(wrapper, 'name', { value: cls.name })\n Object.setPrototypeOf(wrapper, cls)\n wrapper.prototype = cls.prototype\n\n return wrapper\n}\n\n/**\n * Returns a copy of a token array with the token at `pos` replaced by\n * `newElements`. Used to transform token streams without modifying the\n * original array.\n */\nfunction arrayReplaceAt<T> (src: T[], pos: number, newElements: T[]): T[] {\n return ([] as T[]).concat(src.slice(0, pos), newElements, src.slice(pos + 1))\n}\n\n/** Checks whether a code point can be decoded from a numeric HTML entity. */\nfunction isValidEntityCode (c: number) {\n // broken sequence\n if (c >= 0xD800 && c <= 0xDFFF) { return false }\n // never used\n if (c >= 0xFDD0 && c <= 0xFDEF) { return false }\n if ((c & 0xFFFF) === 0xFFFF || (c & 0xFFFF) === 0xFFFE) { return false }\n // control codes\n if (c >= 0x00 && c <= 0x08) { return false }\n if (c === 0x0B) { return false }\n if (c >= 0x0E && c <= 0x1F) { return false }\n if (c >= 0x7F && c <= 0x9F) { return false }\n // out of range\n if (c > 0x10FFFF) { return false }\n return true\n}\n\n/**\n * Converts a Unicode code point to a string, like `String.fromCodePoint()`,\n * but does not throw for invalid input.\n */\nfunction fromCodePoint (c: number) {\n /* eslint no-bitwise:0 */\n if (c > 0xffff) {\n c -= 0x10000\n const surrogate1 = 0xd800 + (c >> 10)\n const surrogate2 = 0xdc00 + (c & 0x3ff)\n\n return String.fromCharCode(surrogate1, surrogate2)\n }\n return String.fromCharCode(c)\n}\n\nconst UNESCAPE_MD_RE = /\\\\([!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{|}~])/g\nconst ENTITY_RE = /&([a-z#][a-z0-9]{1,31});/gi\nconst UNESCAPE_ALL_RE = new RegExp(`${UNESCAPE_MD_RE.source}|${ENTITY_RE.source}`, 'gi')\n\nconst DIGITAL_ENTITY_TEST_RE = /^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))$/i\n\nfunction replaceEntityPattern (match: string, name: string) {\n if (name.charCodeAt(0) === 0x23/* # */ && DIGITAL_ENTITY_TEST_RE.test(name)) {\n const code = name[1].toLowerCase() === 'x'\n ? parseInt(name.slice(2), 16)\n : parseInt(name.slice(1), 10)\n\n if (isValidEntityCode(code)) {\n return fromCodePoint(code)\n }\n\n return match\n }\n\n const decoded = decodeHTMLStrict(match)\n if (decoded !== match) {\n return decoded\n }\n\n return match\n}\n\n/** Decodes Markdown backslash escapes. */\nfunction unescapeMd (str: string) {\n if (str.indexOf('\\\\') < 0) { return str }\n return str.replace(UNESCAPE_MD_RE, '$1')\n}\n\n/**\n * Decodes Markdown backslash escapes and HTML character references in link\n * destinations, link titles, and fenced code info strings.\n */\nfunction unescapeAll (str: string) {\n if (str.indexOf('\\\\') < 0 && str.indexOf('&') < 0) { return str }\n\n return str.replace(UNESCAPE_ALL_RE, function (match, escaped, entity) {\n if (escaped) { return escaped }\n return replaceEntityPattern(match, entity)\n })\n}\n\nconst HTML_ESCAPE_TEST_RE = /[&<>\"]/\nconst HTML_ESCAPE_REPLACE_RE = /[&<>\"]/g\nconst HTML_REPLACEMENTS = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;'\n}\n\nfunction replaceUnsafeChar (ch: string): string {\n return HTML_REPLACEMENTS[ch as keyof typeof HTML_REPLACEMENTS]\n}\n\n/** Escapes HTML special characters in a string. */\nfunction escapeHtml (str: string) {\n if (HTML_ESCAPE_TEST_RE.test(str)) {\n return str.replace(HTML_ESCAPE_REPLACE_RE, replaceUnsafeChar)\n }\n return str\n}\n\nconst REGEXP_ESCAPE_RE = /[.?*+^$[\\]\\\\(){}|-]/g\n\n/** Escapes regular expression metacharacters in a string. */\nfunction escapeRE (str: string) {\n return str.replace(REGEXP_ESCAPE_RE, '\\\\$&')\n}\n\n/** Checks whether a character code is an ASCII space or tab. */\nfunction isSpace (code: number) {\n switch (code) {\n case 0x09:\n case 0x20:\n return true\n }\n return false\n}\n\n/**\n * Checks whether a character code is whitespace recognized by Markdown.\n *\n * Matches the Unicode `Zs` category or `\\t`, `\\f`, `\\v`, `\\r`, `\\n`.\n */\nfunction isWhiteSpace (code: number) {\n if (code >= 0x2000 && code <= 0x200A) { return true }\n switch (code) {\n case 0x09: // \\t\n case 0x0A: // \\n\n case 0x0B: // \\v\n case 0x0C: // \\f\n case 0x0D: // \\r\n case 0x20:\n case 0xA0:\n case 0x1680:\n case 0x202F:\n case 0x205F:\n case 0x3000:\n return true\n }\n return false\n}\n\n/**\n * Checks whether a character is Unicode punctuation or a symbol.\n *\n * Does not support astral characters.\n */\nfunction isPunctChar (ch: string) {\n return ucmicro.P.test(ch) || ucmicro.S.test(ch)\n}\n\n/** Checks whether a Unicode code point is punctuation or a symbol. */\nfunction isPunctCharCode (code: number) {\n return isPunctChar(fromCodePoint(code))\n}\n\n/**\n * Markdown ASCII punctuation characters.\n *\n * !, \", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @,\n * [, \\, ], ^, _, `, {, |, }, or ~\n *\n * http://spec.commonmark.org/0.15/#ascii-punctuation-character\n *\n * Don't confuse with Unicode punctuation. It lacks some characters in the\n * ASCII range.\n */\nfunction isMdAsciiPunct (ch: number) {\n switch (ch) {\n case 0x21/* ! */:\n case 0x22/* \" */:\n case 0x23/* # */:\n case 0x24/* $ */:\n case 0x25/* % */:\n case 0x26/* & */:\n case 0x27/* ' */:\n case 0x28/* ( */:\n case 0x29/* ) */:\n case 0x2A/* * */:\n case 0x2B/* + */:\n case 0x2C/* , */:\n case 0x2D/* - */:\n case 0x2E/* . */:\n case 0x2F/* / */:\n case 0x3A/* : */:\n case 0x3B/* ; */:\n case 0x3C/* < */:\n case 0x3D/* = */:\n case 0x3E/* > */:\n case 0x3F/* ? */:\n case 0x40/* @ */:\n case 0x5B/* [ */:\n case 0x5C/* \\ */:\n case 0x5D/* ] */:\n case 0x5E/* ^ */:\n case 0x5F/* _ */:\n case 0x60/* ` */:\n case 0x7B/* { */:\n case 0x7C/* | */:\n case 0x7D/* } */:\n case 0x7E/* ~ */:\n return true\n default:\n return false\n }\n}\n\n/** Normalizes `[reference labels]` for case-insensitive lookup. */\nfunction normalizeReference (str: string) {\n // Trim and collapse whitespace\n //\n str = str.trim().replace(/\\s+/g, ' ')\n\n // .toLowerCase().toUpperCase() should get rid of all differences\n // between letter variants.\n //\n // Simple .toLowerCase() doesn't normalize 125 code points correctly,\n // and .toUpperCase doesn't normalize 6 of them (list of exceptions:\n // İ, ϴ, ẞ, Ω, K, Å - those are already uppercased, but have differently\n // uppercased versions).\n //\n // Here's an example showing how it happens. Lets take greek letter omega:\n // uppercase U+0398 (Θ), U+03f4 (ϴ) and lowercase U+03b8 (θ), U+03d1 (ϑ)\n //\n // Unicode entries:\n // 0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8;\n // 03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398\n // 03D1;GREEK THETA SYMBOL;Ll;0;L;<compat> 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398\n // 03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L;<compat> 0398;;;;N;;;;03B8;\n //\n // Case-insensitive comparison should treat all of them as equivalent.\n //\n // But .toLowerCase() doesn't change ϑ (it's already lowercase),\n // and .toUpperCase() doesn't change ϴ (already uppercase).\n //\n // Applying first lower then upper case normalizes any character:\n // '\\u0398\\u03f4\\u03b8\\u03d1'.toLowerCase().toUpperCase() === '\\u0398\\u0398\\u0398\\u0398'\n //\n // Note: this is equivalent to unicode case folding; unicode normalization\n // is a different step that is not required here.\n //\n // Final result should be uppercased, because it's later stored in an object\n // (this avoid a conflict with Object.prototype members,\n // most notably, `__proto__`)\n //\n return str.toLowerCase().toUpperCase()\n}\n\nfunction isAsciiTrimmable (c: number) {\n return c === 0x20 || c === 0x09 || c === 0x0a || c === 0x0d\n}\n\n/**\n * \"Light\" `.trim()` for blocks (headings, paragraphs), where Unicode spaces\n * should be preserved.\n */\nfunction asciiTrim (str: string) {\n let start = 0\n for (; start < str.length; start++) {\n if (!isAsciiTrimmable(str.charCodeAt(start))) {\n break\n }\n }\n let end = str.length - 1\n for (; end >= start; end--) {\n if (!isAsciiTrimmable(str.charCodeAt(end))) {\n break\n }\n }\n return str.slice(start, end + 1)\n}\n\n/**\n * Libraries commonly used by markdown-it and its plugins, re-exported to\n * reduce duplicate dependencies in browser bundles.\n */\nconst lib = { mdurl, ucmicro }\n\nexport {\n lib,\n callable,\n unescapeMd,\n unescapeAll,\n isValidEntityCode,\n fromCodePoint,\n escapeHtml,\n arrayReplaceAt,\n isSpace,\n isWhiteSpace,\n isMdAsciiPunct,\n isPunctChar,\n isPunctCharCode,\n escapeRE,\n normalizeReference,\n asciiTrim\n}\n","import type StateInline from '../rules_inline/state_inline.ts'\n\n/** Finds the end of a link or image label (`[label]`). */\nexport default function parseLinkLabel (state: StateInline, start: number, disableNested?: boolean): number {\n let level, found, marker, prevPos\n\n const max = state.posMax\n const oldPos = state.pos\n\n state.pos = start + 1\n level = 1\n\n while (state.pos < max) {\n marker = state.src.charCodeAt(state.pos)\n if (marker === 0x5D /* ] */) {\n level--\n if (level === 0) {\n found = true\n break\n }\n }\n\n prevPos = state.pos\n state.md.inline.skipToken(state)\n if (marker === 0x5B /* [ */) {\n if (prevPos === state.pos - 1) {\n // increase level if we find text `[`, which is not a part of any token\n level++\n } else if (disableNested) {\n state.pos = oldPos\n return -1\n }\n }\n }\n\n let labelEnd = -1\n\n if (found) {\n labelEnd = state.pos\n }\n\n // restore old state\n state.pos = oldPos\n\n return labelEnd\n}\n","import { unescapeAll } from '../common/utils.ts'\n\n/** Parses the destination in `[label](destination \"title\")`. */\nexport default function parseLinkDestination (str: string, start: number, max: number) {\n let code\n let pos = start\n\n const result = {\n ok: false,\n pos: 0,\n str: ''\n }\n\n if (str.charCodeAt(pos) === 0x3C /* < */) {\n pos++\n while (pos < max) {\n code = str.charCodeAt(pos)\n if (code === 0x0A /* \\n */) { return result }\n if (code === 0x3C /* < */) { return result }\n if (code === 0x3E /* > */) {\n result.pos = pos + 1\n result.str = unescapeAll(str.slice(start + 1, pos))\n result.ok = true\n return result\n }\n if (code === 0x5C /* \\ */ && pos + 1 < max) {\n pos += 2\n continue\n }\n\n pos++\n }\n\n // no closing '>'\n return result\n }\n\n // this should be ... } else { ... branch\n\n let level = 0\n while (pos < max) {\n code = str.charCodeAt(pos)\n\n if (code === 0x20) { break }\n\n // ascii control characters\n if (code < 0x20 || code === 0x7F) { break }\n\n if (code === 0x5C /* \\ */ && pos + 1 < max) {\n if (str.charCodeAt(pos + 1) === 0x20) { pos++; continue }\n pos += 2\n continue\n }\n\n if (code === 0x28 /* ( */) {\n level++\n if (level > 32) { return result }\n }\n\n if (code === 0x29 /* ) */) {\n if (level === 0) { break }\n level--\n }\n\n pos++\n }\n\n if (start === pos) { return result }\n if (level !== 0) { return result }\n\n result.str = unescapeAll(str.slice(start, pos))\n result.pos = pos\n result.ok = true\n return result\n}\n","import { unescapeAll } from '../common/utils.ts'\n\n/** @inline */\ninterface ParseLinkTitleResult {\n ok: boolean\n can_continue: boolean\n pos: number\n str: string\n marker: number\n}\n\n/**\n * Parses the optional title in `[label](destination \"title\")` or\n * `[label]: destination \"title\"`.\n *\n * `prev_state` continues a reference title on the next source line.\n */\nexport default function parseLinkTitle (\n str: string,\n start: number,\n max: number,\n prev_state?: ParseLinkTitleResult\n): ParseLinkTitleResult {\n let code\n let pos = start\n\n const state = {\n // if `true`, this is a valid link title\n ok: false,\n // if `true`, this link can be continued on the next line\n can_continue: false,\n // if `ok`, it's the position of the first character after the closing marker\n pos: 0,\n // if `ok`, it's the unescaped title\n str: '',\n // expected closing marker character code\n marker: 0\n }\n\n if (prev_state) {\n // this is a continuation of a previous parseLinkTitle call on the next line,\n // used in reference links only\n state.str = prev_state.str\n state.marker = prev_state.marker\n } else {\n if (pos >= max) { return state }\n\n let marker = str.charCodeAt(pos)\n if (marker !== 0x22 /* \" */ && marker !== 0x27 /* ' */ && marker !== 0x28 /* ( */) { return state }\n\n start++\n pos++\n\n // if opening marker is \"(\", switch it to closing marker \")\"\n if (marker === 0x28) { marker = 0x29 }\n\n state.marker = marker\n }\n\n while (pos < max) {\n code = str.charCodeAt(pos)\n if (code === state.marker) {\n state.pos = pos + 1\n state.str += unescapeAll(str.slice(start, pos))\n state.ok = true\n return state\n } else if (code === 0x28 /* ( */ && state.marker === 0x29 /* ) */) {\n return state\n } else if (code === 0x5C /* \\ */ && pos + 1 < max) {\n pos++\n }\n\n pos++\n }\n\n // no closing marker found, but this link title may continue on the next line (for references)\n state.can_continue = true\n state.str += unescapeAll(str.slice(start, pos))\n return state\n}\n","/**\n * Functions used to parse links and images, split out of parser rules because\n * of their size.\n *\n * @module md.helpers\n */\n\n// Just a shortcut for bulk export\n\nimport parseLinkLabel from './parse_link_label.ts'\nimport parseLinkDestination from './parse_link_destination.ts'\nimport parseLinkTitle from './parse_link_title.ts'\n\nexport {\n parseLinkLabel,\n parseLinkDestination,\n parseLinkTitle\n}\n","// Token class\n\n/** @inline */\ntype TokenNesting = -1 | 0 | 1\n\n/** @inline */\ntype TokenAttribute = [name: string, value: string | number]\n\n/**\n * Represents one item in the parsed token stream, storing parsed data and\n * providing helpers for managing HTML attributes.\n */\nclass Token {\n /**\n * Type of the token (string, e.g. \"paragraph_open\")\n */\n declare type: string\n\n /**\n * html tag name, e.g. \"p\"\n */\n declare tag: string\n\n /** Html attributes. Format: `[ [ name1, value1 ], [ name2, value2 ] ]` */\n declare attrs: TokenAttribute[] | null\n\n /**\n * Source map info. Format: `[ line_begin, line_end ]`\n */\n map: [number, number] | null = null\n\n /**\n * Level change (number in {-1, 0, 1} set), where:\n *\n * - `1` means the tag is opening\n * - `0` means the tag is self-closing\n * - `-1` means the tag is closing\n */\n declare nesting: TokenNesting\n\n /**\n * nesting level, the same as `state.level`\n */\n level = 0\n\n /**\n * An array of child nodes (inline and img tokens)\n */\n children: Token[] | null = null\n\n /**\n * In a case of self-closing tag (code, html, fence, etc.),\n * it has contents of this tag.\n */\n content = ''\n\n /**\n * '*' or '_' for emphasis, fence string for fence, etc.\n */\n markup = ''\n\n /**\n * Additional information:\n *\n * - Info string for \"fence\" tokens\n * - The value \"auto\" for autolink \"link_open\" and \"link_close\" tokens\n * - The string value of the item marker for ordered-list \"list_item_open\" tokens\n */\n info = ''\n\n /** A place for plugins to store an arbitrary data */\n declare meta: Record<string, unknown> | null\n\n /**\n * True for block-level tokens, false for inline tokens.\n * Used in renderer to calculate line breaks\n */\n block = false\n\n /**\n * If it's true, ignore this element when rendering. Used for tight lists\n * to hide paragraphs.\n */\n hidden = false\n\n constructor (type: string, tag: string, nesting: TokenNesting) {\n this.type = type\n this.tag = tag\n\n this.attrs = null\n\n this.nesting = nesting\n\n this.meta = null\n }\n\n /**\n * Search attribute index by name.\n */\n attrIndex (name: string): number {\n if (!this.attrs) { return -1 }\n\n const attrs = this.attrs\n\n for (let i = 0, len = attrs.length; i < len; i++) {\n if (attrs[i][0] === name) { return i }\n }\n return -1\n }\n\n /**\n * Add `[ name, value ]` attribute to list. Init attrs if necessary\n */\n attrPush (attrData: TokenAttribute): void {\n if (this.attrs) {\n this.attrs.push(attrData)\n } else {\n this.attrs = [attrData]\n }\n }\n\n /**\n * Set `name` attribute to `value`. Override old value if exists.\n */\n attrSet (name: string, value: string | number): void {\n const idx = this.attrIndex(name)\n const attrData: TokenAttribute = [name, value]\n\n if (idx < 0) {\n this.attrPush(attrData)\n } else {\n this.attrs![idx] = attrData\n }\n }\n\n /**\n * Get the value of attribute `name`, or null if it does not exist.\n */\n attrGet (name: string): string | number | null {\n const idx = this.attrIndex(name)\n let value = null\n if (idx >= 0) {\n value = this.attrs![idx][1]\n }\n return value\n }\n\n /**\n * Join value to existing attribute via space. Or create new attribute if not\n * exists. Useful to operate with token classes.\n */\n attrJoin (name: string, value: string | number): void {\n const idx = this.attrIndex(name)\n\n if (idx < 0) {\n this.attrPush([name, value])\n } else {\n this.attrs![idx][1] = `${this.attrs![idx][1]} ${value}`\n }\n }\n}\n\nexport default Token\n","/** @inline */\ntype RuleOptions = { alt?: string[] }\n\n/**\n * Helper class, used by {@link MarkdownIt.core}, {@link MarkdownIt.block} and\n * {@link MarkdownIt.inline} to manage sequences of functions (rules):\n *\n * - keep rules in defined order\n * - assign the name to each rule\n * - enable/disable rules\n * - add/replace rules\n * - allow assign rules to additional named chains (in the same)\n * - cacheing lists of active rules\n *\n * You will not need use this class directly until write plugins. For simple\n * rules control use {@link MarkdownIt.disable}, {@link MarkdownIt.enable} and\n * {@link MarkdownIt.use}.\n */\nclass Ruler<Args extends unknown[], Result> {\n // List of added rules. Each element is:\n //\n // {\n // name: XXX,\n // enabled: Boolean,\n // fn: Function(),\n // alt: [ name2, name3 ]\n // }\n //\n __rules__: Array<{\n name: string\n enabled: boolean\n fn: (...args: Args) => Result\n alt: string[]\n }> = []\n\n // Cached rule chains.\n //\n // First level - chain name, '' for default.\n // Second level - diginal anchor for fast filtering by charcodes.\n //\n __cache__: Record<string, Array<(...args: Args) => Result>> | null = null\n\n // Helper methods, should not be used directly\n\n // Find rule index by name\n //\n __find__ (name: string): number {\n for (let i = 0; i < this.__rules__.length; i++) {\n if (this.__rules__[i].name === name) {\n return i\n }\n }\n return -1\n }\n\n // Build rules lookup cache\n //\n __compile__ (): void {\n const chains = new Set<string>()\n\n // collect unique names\n this.__rules__.forEach(rule => {\n if (!rule.enabled) return\n rule.alt.forEach(altName => {\n if (altName) chains.add(altName)\n })\n })\n\n this.__cache__ = Object.create(null)\n\n // Collect default chain\n this.__cache__![''] = []\n this.__rules__.forEach(rule => {\n if (rule.enabled) this.__cache__![''].push(rule.fn)\n })\n\n // Collect alt chains\n chains.forEach(chain => {\n this.__cache__![chain] = []\n\n this.__rules__.forEach(rule => {\n if (rule.enabled && rule.alt.indexOf(chain) >= 0) {\n this.__cache__![chain].push(rule.fn)\n }\n })\n })\n }\n\n /**\n * Replace rule by name with new function & options. Throws error if name not\n * found.\n *\n * @param name Rule name to replace.\n * @param fn New rule function.\n * @param options Rule options. `alt` is an array with names of \"alternate\"\n * chains.\n *\n * @example Replace existing typographer replacement rule with new one\n * ```javascript\n * import MarkdownIt from 'markdown-it'\n * const md = new MarkdownIt()\n *\n * md.core.ruler.at('replacements', function replace(state) {\n * //...\n * });\n * ```\n */\n at (name: string, fn: (...args: Args) => Result, options: RuleOptions = {}): void {\n const index = this.__find__(name)\n\n if (index === -1) { throw new Error(`Parser rule not found: ${name}`) }\n\n this.__rules__[index].fn = fn\n this.__rules__[index].alt = options.alt || []\n this.__cache__ = null\n }\n\n /**\n * Add new rule to chain before one with given name. See also\n * {@link Ruler.after}, {@link Ruler.push}.\n *\n * @param beforeName New rule will be added before this one.\n * @param ruleName Name of added rule.\n * @param fn Rule function.\n * @param options Rule options. `alt` is an array with names of \"alternate\"\n * chains.\n *\n * @example\n * ```javascript\n * import MarkdownIt from 'markdown-it'\n * const md = new MarkdownIt()\n *\n * md.block.ruler.before('paragraph', 'my_rule', function replace(state) {\n * //...\n * });\n * ```\n */\n before (beforeName: string, ruleName: string, fn: (...args: Args) => Result, options: RuleOptions = {}): void {\n const index = this.__find__(beforeName)\n\n if (index === -1) { throw new Error(`Parser rule not found: ${beforeName}`) }\n\n this.__rules__.splice(index, 0, {\n name: ruleName,\n enabled: true,\n fn,\n alt: options.alt || []\n })\n\n this.__cache__ = null\n }\n\n /**\n * Add new rule to chain after one with given name. See also\n * {@link Ruler.before}, {@link Ruler.push}.\n *\n * @param afterName New rule will be added after this one.\n * @param ruleName Name of added rule.\n * @param fn Rule function.\n * @param options Rule options. `alt` is an array with names of \"alternate\"\n * chains.\n *\n * @example\n * ```javascript\n * import MarkdownIt from 'markdown-it'\n * const md = new MarkdownIt()\n *\n * md.inline.ruler.after('text', 'my_rule', function replace(state) {\n * //...\n * });\n * ```\n */\n after (afterName: string, ruleName: string, fn: (...args: Args) => Result, options: RuleOptions = {}): void {\n const index = this.__find__(afterName)\n\n if (index === -1) { throw new Error(`Parser rule not found: ${afterName}`) }\n\n this.__rules__.splice(index + 1, 0, {\n name: ruleName,\n enabled: true,\n fn,\n alt: options.alt || []\n })\n\n this.__cache__ = null\n }\n\n /**\n * Push new rule to the end of chain. See also\n * {@link Ruler.before}, {@link Ruler.after}.\n *\n * @param ruleName Name of added rule.\n * @param fn Rule function.\n * @param options Rule options. `alt` is an array with names of \"alternate\"\n * chains.\n *\n * @example\n * ```javascript\n * import MarkdownIt from 'markdown-it'\n * const md = new MarkdownIt()\n *\n * md.core.ruler.push('my_rule', function replace(state) {\n * //...\n * });\n * ```\n */\n push (ruleName: string, fn: (...args: Args) => Result, options: RuleOptions = {}): void {\n this.__rules__.push({\n name: ruleName,\n enabled: true,\n fn,\n alt: options.alt || []\n })\n\n this.__cache__ = null\n }\n\n /**\n * Enable rules with given names. If any rule name not found - throw Error.\n * Errors can be disabled by second param.\n *\n * See also {@link Ruler.disable}, {@link Ruler.enableOnly}.\n *\n * @param list List of rule names to enable.\n * @param ignoreInvalid Set `true` to ignore errors when rule not found.\n * @returns List of found rule names (if no exception happened).\n */\n enable (list: string | string[], ignoreInvalid = false): string[] {\n if (!Array.isArray(list)) { list = [list] }\n\n const result: string[] = []\n\n // Search by name and enable\n list.forEach(name => {\n const idx = this.__find__(name)\n\n if (idx < 0) {\n if (ignoreInvalid) { return }\n throw new Error(`Rules manager: invalid rule name ${name}`)\n }\n this.__rules__[idx].enabled = true\n result.push(name)\n })\n\n this.__cache__ = null\n return result\n }\n\n /**\n * Enable rules with given names, and disable everything else. If any rule name\n * not found - throw Error. Errors can be disabled by second param.\n *\n * See also {@link Ruler.disable}, {@link Ruler.enable}.\n *\n * @param list List of rule names to enable (whitelist).\n * @param ignoreInvalid Set `true` to ignore errors when rule not found.\n */\n enableOnly (list: string | string[], ignoreInvalid = false): void {\n if (!Array.isArray(list)) { list = [list] }\n\n this.__rules__.forEach(rule => { rule.enabled = false })\n\n this.enable(list, ignoreInvalid)\n }\n\n /**\n * Disable rules with given names. If any rule name not found - throw Error.\n * Errors can be disabled by second param.\n *\n * See also {@link Ruler.enable}, {@link Ruler.enableOnly}.\n *\n * @param list List of rule names to disable.\n * @param ignoreInvalid Set `true` to ignore errors when rule not found.\n * @returns List of found rule names (if no exception happened).\n */\n disable (list: string | string[], ignoreInvalid = false): string[] {\n if (!Array.isArray(list)) { list = [list] }\n\n const result: string[] = []\n\n // Search by name and disable\n list.forEach(name => {\n const idx = this.__find__(name)\n\n if (idx < 0) {\n if (ignoreInvalid) { return }\n throw new Error(`Rules manager: invalid rule name ${name}`)\n }\n this.__rules__[idx].enabled = false\n result.push(name)\n })\n\n this.__cache__ = null\n return result\n }\n\n /**\n * Return array of active functions (rules) for given chain name. It analyzes\n * rules configuration, compiles caches if not exists and returns result.\n *\n * Default chain name is `''` (empty string). It can't be skipped. That's\n * done intentionally, to keep signature monomorphic for high speed.\n */\n getRules (chainName: string): Array<(...args: Args) => Result> {\n if (!this.__cache__) this.__compile__()\n\n // Chain can be empty, if rules disabled. But we still have to return Array.\n return this.__cache__![chainName] || []\n }\n}\n\nexport default Ruler\n","import { unescapeAll, escapeHtml } from './common/utils.ts'\nimport type Token from './token.ts'\nimport type { Env, MarkdownItOptions } from './types.ts'\n\n/** Function that renders a token at a given position in a token stream. */\nexport type RendererRule = (\n tokens: Token[],\n idx: number,\n options: Required<MarkdownItOptions>,\n env: Env | undefined,\n renderer: Renderer\n) => string\n\nconst default_rules: Record<string, RendererRule> = {}\n\ndefault_rules.code_inline = function (\n tokens: Token[],\n idx: number,\n options: Required<MarkdownItOptions>,\n env: Env | undefined,\n slf: Renderer\n): string {\n const token = tokens[idx]\n\n return `<code${slf.renderAttrs(token)}>${escapeHtml(token.content)}</code>`\n}\n\ndefault_rules.code_block = function (\n tokens: Token[],\n idx: number,\n options: Required<MarkdownItOptions>,\n env: Env | undefined,\n slf: Renderer\n): string {\n const token = tokens[idx]\n\n return `<pre${slf.renderAttrs(token)}><code>${escapeHtml(tokens[idx].content)}</code></pre>\\n`\n}\n\ndefault_rules.fence = function (\n tokens: Token[],\n idx: number,\n options: Required<MarkdownItOptions>,\n env: Env | undefined,\n slf: Renderer\n): string {\n const token = tokens[idx]\n const info = token.info ? unescapeAll(token.info).trim() : ''\n let langName = ''\n let langAttrs = ''\n\n if (info) {\n const arr = info.split(/(\\s+)/g)\n langName = arr[0]\n langAttrs = arr.slice(2).join('')\n }\n\n let highlighted\n if (options.highlight) {\n highlighted = options.highlight(token.content, langName, langAttrs) || escapeHtml(token.content)\n } else {\n highlighted = escapeHtml(token.content)\n }\n\n if (highlighted.indexOf('<pre') === 0) {\n return highlighted + '\\n'\n }\n\n // If language exists, inject class gently, without modifying original token.\n // May be, one day we will add .deepClone() for token and simplify this part, but\n // now we prefer to keep things local.\n if (info) {\n const i = token.attrIndex('class')\n const tmpAttrs = token.attrs ? token.attrs.slice() : []\n\n if (i < 0) {\n tmpAttrs.push(['class', `${options.langPrefix}${langName}`])\n } else {\n tmpAttrs[i] = [tmpAttrs[i][0], tmpAttrs[i][1]] // shallow clone\n tmpAttrs[i][1] += ` ${options.langPrefix}${langName}`\n }\n\n // Fake token just to render attributes\n const tmpToken = {\n attrs: tmpAttrs\n }\n\n return `<pre><code${slf.renderAttrs(tmpToken)}>${highlighted}</code></pre>\\n`\n }\n\n return `<pre><code${slf.renderAttrs(token)}>${highlighted}</code></pre>\\n`\n}\n\ndefault_rules.image = function (\n tokens: Token[],\n idx: number,\n options: Required<MarkdownItOptions>,\n env: Env | undefined,\n slf: Renderer\n): string {\n const token = tokens[idx]\n\n // \"alt\" attr MUST be set, even if empty. Because it's mandatory and\n // should be placed on proper position for tests.\n //\n // Replace content with actual value\n\n token.attrs![token.attrIndex('alt')][1] =\n slf.renderInlineAsText(token.children!, options, env)\n\n return slf.renderToken(tokens, idx, options)\n}\n\ndefault_rules.hardbreak = function (\n tokens: Token[],\n idx: number,\n options: Required<MarkdownItOptions>\n): string {\n return options.xhtmlOut ? '<br />\\n' : '<br>\\n'\n}\ndefault_rules.softbreak = function (\n tokens: Token[],\n idx: number,\n options: Required<MarkdownItOptions>\n): string {\n return options.breaks ? (options.xhtmlOut ? '<br />\\n' : '<br>\\n') : '\\n'\n}\n\ndefault_rules.text = function (tokens: Token[], idx: number): string {\n return escapeHtml(tokens[idx].content)\n}\n\ndefault_rules.html_block = function (tokens: Token[], idx: number): string {\n return tokens[idx].content\n}\ndefault_rules.html_inline = function (tokens: Token[], idx: number): string {\n return tokens[idx].content\n}\n\n/**\n * Generates HTML from parsed token stream. Each instance has independent\n * copy of rules. Those can be rewritten with ease. Also, you can add new\n * rules if you create plugin and adds new token types.\n *\n * Creates new renderer instance and fills {@link Renderer.rules} with defaults.\n */\nclass Renderer {\n /**\n * Contains render rules for tokens. Can be updated and extended.\n *\n * See [source code](https://github.com/markdown-it/markdown-it/blob/master/src/renderer.ts)\n * for more details and examples.\n *\n * @example Custom render rules\n * ```javascript\n * import MarkdownIt from 'markdown-it'\n * const md = new MarkdownIt()\n *\n * md.renderer.rules.strong_open = function () { return '<b>'; };\n * md.renderer.rules.strong_close = function () { return '</b>'; };\n *\n * const result = md.renderInline(...);\n * ```\n *\n * @example Each rule is called as independent static function with fixed signature\n * ```javascript\n * function my_token_render(tokens, idx, options, env, renderer) {\n * // ...\n * return renderedHTML;\n * }\n * ```\n */\n rules: Record<string, RendererRule> = Object.assign({}, default_rules)\n\n /**\n * Render token attributes to string.\n */\n renderAttrs (token: Pick<Token, 'attrs'>): string {\n let i, l, result\n\n if (!token.attrs) { return '' }\n\n result = ''\n\n for (i = 0, l = token.attrs.length; i < l; i++) {\n result += ` ${escapeHtml(token.attrs[i][0])}=\"${escapeHtml(String(token.attrs[i][1]))}\"`\n }\n\n return result\n }\n\n /**\n * Default token renderer. Can be overriden by custom function\n * in {@link Renderer.rules}.\n *\n * @param tokens List of tokens.\n * @param idx Token index to render.\n * @param options Params of parser instance.\n */\n renderToken (tokens: Token[], idx: number, options: Required<MarkdownItOptions>): string {\n const token = tokens[idx]\n let result = ''\n\n // Tight list paragraphs\n if (token.hidden) {\n return ''\n }\n\n // Insert a newline between hidden paragraph and subsequent opening\n // block-level tag.\n //\n // For example, here we should insert a newline before blockquote:\n // - a\n // >\n //\n // Only closing hidden tokens count, to not break on other hidden ones.\n //\n // Hidden tokens without nesting (`reference_definition`) are skipped here\n // and below, or they would break line feeds around neighbour blocks.\n //\n let prev = idx - 1\n while (prev >= 0 && tokens[prev].hidden && tokens[prev].nesting === 0) { prev-- }\n\n if (token.block && token.nesting !== -1 && prev >= 0 &&\n tokens[prev].hidden && tokens[prev].nesting === -1) {\n result += '\\n'\n }\n\n // Add token name, e.g. `<img`\n result += (token.nesting === -1 ? '</' : '<') + token.tag\n\n // Encode attributes, e.g. `<img src=\"foo\"`\n result += this.renderAttrs(token)\n\n // Add a slash for self-closing tags, e.g. `<img src=\"foo\" /`\n if (token.nesting === 0 && options.xhtmlOut) {\n result += ' /'\n }\n\n // Check if we need to add a newline after this tag\n let needLf = false\n if (token.block) {\n needLf = true\n\n if (token.nesting === 1) {\n let next = idx + 1\n while (next < tokens.length && tokens[next].hidden && tokens[next].nesting === 0) { next++ }\n\n if (next < tokens.length) {\n const nextToken = tokens[next]\n\n if (nextToken.type === 'inline' || nextToken.hidden) {\n // Block-level tag containing an inline tag.\n //\n needLf = false\n } else if (nextToken.nesting === -1 && nextToken.tag === token.tag) {\n // Opening tag + closing tag of the same type. E.g. `<li></li>`.\n //\n needLf = false\n }\n }\n }\n }\n\n result += needLf ? '>\\n' : '>'\n\n return result\n }\n\n /**\n * The same as {@link Renderer.render}, but for single token of `inline` type.\n *\n * @param tokens List on block tokens to render.\n * @param options Params of parser instance.\n * @param env Additional data from parsed input (references, for example).\n */\n renderInline (tokens: Token[], options: Required<MarkdownItOptions>, env: Env | undefined): string {\n let result = ''\n const rules = this.rules\n\n for (let i = 0, len = tokens.length; i < len; i++) {\n const type = tokens[i].type\n\n if (typeof rules[type] !== 'undefined') {\n result += rules[type](tokens, i, options, env, this)\n } else {\n result += this.renderToken(tokens, i, options)\n }\n }\n\n return result\n }\n\n /**\n * Special kludge for image `alt` attributes to conform CommonMark spec.\n * Don't try to use it! Spec requires to show `alt` content with stripped markup,\n * instead of simple escaping.\n *\n * @param tokens List on block tokens to render.\n * @param options Params of parser instance.\n * @param env Additional data from parsed input (references, for example).\n */\n renderInlineAsText (tokens: Token[], options: Required<MarkdownItOptions>, env: Env | undefined): string {\n let result = ''\n\n for (let i = 0, len = tokens.length; i < len; i++) {\n switch (tokens[i].type) {\n case 'text':\n case 'code_inline':\n // code content is added as plain text, without backticks\n result += tokens[i].content\n break\n case 'image':\n result += this.renderInlineAsText(tokens[i].children!, options, env)\n break\n case 'html_inline':\n case 'html_block':\n result += tokens[i].content\n break\n case 'softbreak':\n case 'hardbreak':\n result += '\\n'\n break\n default:\n // all other tokens are skipped\n }\n }\n\n return result\n }\n\n /**\n * Takes token stream and generates HTML. Probably, you will never need to call\n * this method directly.\n *\n * @param tokens List on block tokens to render.\n * @param options Params of parser instance.\n * @param env Additional data from parsed input (references, for example).\n */\n render (tokens: Token[], options: Required<MarkdownItOptions>, env?: Env): string {\n let result = ''\n const rules = this.rules\n\n for (let i = 0, len = tokens.length; i < len; i++) {\n const type = tokens[i].type\n\n if (type === 'inline') {\n result += this.renderInline(tokens[i].children!, options, env)\n } else if (typeof rules[type] !== 'undefined') {\n result += rules[type](tokens, i, options, env, this)\n } else {\n result += this.renderToken(tokens, i, options)\n }\n }\n\n return result\n }\n}\n\nexport default Renderer\n","import Token from '../token.ts'\nimport type MarkdownIt from '../markdownit.ts'\nimport type { Env } from '../types.ts'\n\n/** Mutable state passed through the core rules chain. */\nclass StateCore {\n declare src: string\n declare env: Env\n tokens: Token[] = []\n inlineMode = false\n declare md: MarkdownIt\n\n // re-export Token class to use in core rules\n Token = Token\n\n constructor (src: string, md: MarkdownIt, env: Env) {\n this.src = src\n this.env = env\n this.md = md // link to parser instance\n }\n}\n\nexport default StateCore\n","// Normalize input string\n\nimport type StateCore from './state_core.ts'\n\n// https://spec.commonmark.org/0.29/#line-ending\nconst NEWLINES_RE = /\\r\\n?|\\n/g\nconst NULL_RE = /\\0/g\n\nexport default function normalize (state: StateCore): void {\n let str\n\n // Normalize newlines\n str = state.src.replace(NEWLINES_RE, '\\n')\n\n // Replace NULL characters\n str = str.replace(NULL_RE, '\\uFFFD')\n\n state.src = str\n}\n","import type StateCore from './state_core.ts'\n\nexport default function block (state: StateCore): void {\n let token\n\n if (state.inlineMode) {\n token = new state.Token('inline', '', 0)\n token.content = state.src\n token.map = [0, 1]\n token.children = []\n state.tokens.push(token)\n } else {\n state.md.block.parse(state.src, state.md, state.env, state.tokens)\n }\n}\n","// Drop `reference_definition` tokens to keep the stream backward compatible\n//\n// Those tokens mark places link definitions took in the source. They are new,\n// and plugins walking block tokens may not expect them, so by default the\n// stream stays as it always was. Disable this rule to opt in.\n//\n\nimport type StateCore from './state_core.ts'\n\nexport default function strip_references (state: StateCore): void {\n const tokens = state.tokens\n let last = 0\n\n for (let curr = 0; curr < tokens.length; curr++) {\n if (tokens[curr].type === 'reference_definition') continue\n\n if (curr !== last) { tokens[last] = tokens[curr] }\n\n last++\n }\n\n if (tokens.length !== last) { tokens.length = last }\n}\n","import type StateCore from './state_core.ts'\n\nexport default function inline (state: StateCore): void {\n const tokens = state.tokens\n\n // Parse inlines\n for (let i = 0, l = tokens.length; i < l; i++) {\n const tok = tokens[i]\n if (tok.type === 'inline') {\n state.md.inline.parse(tok.content, state.md, state.env, tok.children!)\n }\n }\n}\n","// Replace link-like texts with link nodes.\n//\n// Currently restricted by `md.validateLink()` to http/https/ftp\n//\n\nimport { arrayReplaceAt } from '../common/utils.ts'\nimport type StateCore from './state_core.ts'\n\nfunction isLinkOpen (str: string) {\n return /^<a[>\\s]/i.test(str)\n}\nfunction isLinkClose (str: string) {\n return /^<\\/a\\s*>/i.test(str)\n}\n\nexport default function linkify (state: StateCore): void {\n const blockTokens = state.tokens\n\n if (!state.md.options.linkify) { return }\n\n for (let j = 0, l = blockTokens.length; j < l; j++) {\n if (blockTokens[j].type !== 'inline' ||\n !state.md.linkify.test(blockTokens[j].content)) {\n continue\n }\n\n let tokens = blockTokens[j].children!\n\n let htmlLinkLevel = 0\n\n // We scan from the end, to keep position when new tags added.\n // Use reversed logic in links start/end match\n for (let i = tokens.length - 1; i >= 0; i--) {\n const currentToken = tokens[i]\n\n // Skip content of markdown links\n if (currentToken.type === 'link_close') {\n i--\n while (tokens[i].level !== currentToken.level && tokens[i].type !== 'link_open') {\n i--\n }\n continue\n }\n\n // Skip content of html tag links\n if (currentToken.type === 'html_inline') {\n if (isLinkOpen(currentToken.content) && htmlLinkLevel > 0) {\n htmlLinkLevel--\n }\n if (isLinkClose(currentToken.content)) {\n htmlLinkLevel++\n }\n }\n if (htmlLinkLevel > 0) { continue }\n\n if (currentToken.type === 'text' && state.md.linkify.test(currentToken.content)) {\n const text = currentToken.content\n let links = state.md.linkify.match(text)!\n\n // Now split string to nodes\n const nodes = []\n let level = currentToken.level\n let lastPos = 0\n\n // forbid escape sequence at the start of the string,\n // this avoids http\\://example.com/ from being linkified as\n // http:<a href=\"//example.com/\">//example.com/</a>\n if (links.length > 0 &&\n links[0].index === 0 &&\n i > 0 &&\n tokens[i - 1].type === 'text_special') {\n links = links.slice(1)\n }\n\n for (let ln = 0; ln < links.length; ln++) {\n const url = links[ln].url\n const fullUrl = state.md.normalizeLink(url)\n if (!state.md.validateLink(fullUrl)) { continue }\n\n let urlText = links[ln].text\n\n // Linkifier might send raw hostnames like \"example.com\", where url\n // starts with domain name. So we prepend http:// in those cases,\n // and remove it afterwards.\n //\n if (!links[ln].schema) {\n urlText = state.md.normalizeLinkText(`http://${urlText}`).replace(/^http:\\/\\//, '')\n } else if (links[ln].schema === 'mailto:' && !/^mailto:/i.test(urlText)) {\n urlText = state.md.normalizeLinkText(`mailto:${urlText}`).replace(/^mailto:/, '')\n } else {\n urlText = state.md.normalizeLinkText(urlText)\n }\n\n const pos = links[ln].index\n\n if (pos > lastPos) {\n const token = new state.Token('text', '', 0)\n token.content = text.slice(lastPos, pos)\n token.level = level\n nodes.push(token)\n }\n\n const token_o = new state.Token('link_open', 'a', 1)\n token_o.attrs = [['href', fullUrl]]\n token_o.level = level++\n token_o.markup = 'linkify'\n token_o.info = 'auto'\n nodes.push(token_o)\n\n const token_t = new state.Token('text', '', 0)\n token_t.content = urlText\n token_t.level = level\n nodes.push(token_t)\n\n const token_c = new state.Token('link_close', 'a', -1)\n token_c.level = --level\n token_c.markup = 'linkify'\n token_c.info = 'auto'\n nodes.push(token_c)\n\n lastPos = links[ln].lastIndex\n }\n if (lastPos < text.length) {\n const token = new state.Token('text', '', 0)\n token.content = text.slice(lastPos)\n token.level = level\n nodes.push(token)\n }\n\n // replace current node\n blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, nodes)\n }\n }\n }\n}\n","// Simple typographic replacements\n//\n// (c) (C) → ©\n// (tm) (TM) → ™\n// (r) (R) → ®\n// +- → ±\n// ... → … (also ?.... → ?.., !.... → !..)\n// ???????? → ???, !!!!! → !!!, `,,` → `,`\n// -- → &ndash;, --- → &mdash;\n//\n\n// TODO:\n// - fractionals 1/2, 1/4, 3/4 -> ½, ¼, ¾\n// - multiplications 2 x 4 -> 2 × 4\n\nimport type StateCore from './state_core.ts'\nimport type Token from '../token.ts'\n\nconst RARE_RE = /\\+-|\\.\\.|\\?\\?\\?\\?|!!!!|,,|--/\n\n// Workaround for phantomjs - need regex without /g flag,\n// or root check will fail every second time\nconst SCOPED_ABBR_TEST_RE = /\\((c|tm|r)\\)/i\n\nconst SCOPED_ABBR_RE = /\\((c|tm|r)\\)/ig\nconst SCOPED_ABBR: Record<string, string> = {\n c: '©',\n r: '®',\n tm: '™'\n}\n\nfunction replaceFn (match: string, name: string) {\n return SCOPED_ABBR[name.toLowerCase()]\n}\n\nfunction replace_scoped (inlineTokens: Token[]) {\n let inside_autolink = 0\n\n for (let i = inlineTokens.length - 1; i >= 0; i--) {\n const token = inlineTokens[i]\n\n if (token.type === 'text' && !inside_autolink) {\n token.content = token.content.replace(SCOPED_ABBR_RE, replaceFn)\n }\n\n if (token.type === 'link_open' && token.info === 'auto') {\n inside_autolink--\n }\n\n if (token.type === 'link_close' && token.info === 'auto') {\n inside_autolink++\n }\n }\n}\n\nfunction replace_rare (inlineTokens: Token[]) {\n let inside_autolink = 0\n\n for (let i = inlineTokens.length - 1; i >= 0; i--) {\n const token = inlineTokens[i]\n\n if (token.type === 'text' && !inside_autolink) {\n if (RARE_RE.test(token.content)) {\n token.content = token.content\n .replace(/\\+-/g, '±')\n // .., ..., ....... -> …\n // but ?..... & !..... -> ?.. & !..\n .replace(/\\.{2,}/g, '…').replace(/([?!])…/g, '$1..')\n .replace(/([?!]){4,}/g, '$1$1$1').replace(/,{2,}/g, ',')\n // em-dash\n .replace(/(^|[^-])---(?=[^-]|$)/mg, '$1\\u2014')\n // en-dash\n .replace(/(^|\\s)--(?=\\s|$)/mg, '$1\\u2013')\n .replace(/(^|[^-\\s])--(?=[^-\\s]|$)/mg, '$1\\u2013')\n }\n }\n\n if (token.type === 'link_open' && token.info === 'auto') {\n inside_autolink--\n }\n\n if (token.type === 'link_close' && token.info === 'auto') {\n inside_autolink++\n }\n }\n}\n\nexport default function replace (state: StateCore): void {\n let blkIdx\n\n if (!state.md.options.typographer) { return }\n\n for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) {\n if (state.tokens[blkIdx].type !== 'inline') { continue }\n\n if (SCOPED_ABBR_TEST_RE.test(state.tokens[blkIdx].content)) {\n replace_scoped(state.tokens[blkIdx].children!)\n }\n\n if (RARE_RE.test(stat