{"version":3,"file":"markdown-parse.cjs","names":[],"sources":["../../../src/components/Markdown/markdown-parse.ts"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — a Markdown block parser is a state\n * machine over line kinds: paragraph, heading, fence, list, quote, table and\n * thematic break each need to see the lines around them to know where they end.\n * parseBlocks is that loop, and cutting it into per-kind functions would hand each\n * one the same lookahead cursor.\n */\n// A small Markdown parser for the subset an app actually renders from untrusted\n// text: comments, ticket bodies, release notes, a product description.\n//\n// It produces a node tree, never an HTML string, and the renderer turns that into\n// React elements. No `dangerouslySetInnerHTML` exists anywhere in this component —\n// which is the property that makes rendering somebody else's Markdown safe, and it\n// is structural rather than a promise about escaping.\n//\n// Raw HTML in the input is therefore not \"sanitized\", it is **text**: `<script>` in\n// a comment renders as the four characters somebody typed. That is the whole point,\n// and it is also the documented limit — this is not a CommonMark implementation and\n// does not try to be.\n\nimport { safeImageUrl, safeLinkUrl } from \"./markdown-url\";\n\n/** Inline content. */\nexport type MarkdownInline =\n    | { type: \"text\"; value: string }\n    | { type: \"strong\"; children: MarkdownInline[] }\n    | { type: \"em\"; children: MarkdownInline[] }\n    | { type: \"del\"; children: MarkdownInline[] }\n    | { type: \"code\"; value: string }\n    | { type: \"link\"; href: string; children: MarkdownInline[] }\n    | { type: \"image\"; src: string; alt: string }\n    | { type: \"break\" };\n\n/** Column alignment of a table, from the delimiter row. */\nexport type MarkdownAlign = \"left\" | \"center\" | \"right\" | null;\n\n/** Block content. */\nexport type MarkdownBlock =\n    | { type: \"heading\"; level: number; children: MarkdownInline[] }\n    | { type: \"paragraph\"; children: MarkdownInline[] }\n    | { type: \"code\"; language: string | null; value: string }\n    | { type: \"quote\"; children: MarkdownBlock[] }\n    | { type: \"list\"; ordered: boolean; start: number; items: MarkdownBlock[][] }\n    | {\n          type: \"table\";\n          align: MarkdownAlign[];\n          head: MarkdownInline[][];\n          rows: MarkdownInline[][][];\n      }\n    | { type: \"rule\" };\n\nconst FENCE = /^\\s{0,3}(`{3,}|~{3,})\\s*([\\w+-]*)\\s*$/;\nconst HEADING = /^\\s{0,3}(#{1,6})\\s+(.*?)\\s*#*\\s*$/;\nconst RULE = /^\\s{0,3}([-*_])\\s*(?:\\1\\s*){2,}$/;\nconst QUOTE = /^\\s{0,3}>\\s?(.*)$/;\nconst BULLET = /^(\\s*)([-*+])\\s+(.*)$/;\nconst ORDERED = /^(\\s*)(\\d{1,9})[.)]\\s+(.*)$/;\nconst HARD_BREAK = / {2,}$/;\n/**\n * A table's delimiter row.\n *\n * The `|` is required somewhere in the line, and that is what tells a one-column\n * table (`| --- |`) apart from a thematic break (`---`) — a single column is a\n * legitimate table, and the first version of this regex demanded two.\n */\nconst TABLE_DELIMITER = /^\\s{0,3}\\|[\\s|:-]*$|^\\s{0,3}:?-+:?(\\s*\\|\\s*:?-+:?)+\\s*\\|?\\s*$/;\n\n/**\n * Parse a Markdown document into blocks.\n *\n * @param source - Raw Markdown.\n * @returns Block nodes in document order.\n */\nexport function parseMarkdown(source: string): MarkdownBlock[] {\n    const lines = source.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\");\n    return parseBlocks(lines);\n}\n\n/** Parse a run of lines into blocks. */\nfunction parseBlocks(lines: string[]): MarkdownBlock[] {\n    const blocks: MarkdownBlock[] = [];\n    let i = 0;\n\n    while (i < lines.length) {\n        const line = lines[i];\n\n        if (line.trim() === \"\") {\n            i += 1;\n            continue;\n        }\n\n        const fence = FENCE.exec(line);\n        if (fence) {\n            const marker = fence[1][0];\n            const body: string[] = [];\n            i += 1;\n            while (i < lines.length && !new RegExp(`^\\\\s{0,3}${marker}{3,}\\\\s*$`).test(lines[i])) {\n                body.push(lines[i]);\n                i += 1;\n            }\n            // An unterminated fence still yields a code block: the alternative is to\n            // render the rest of the document as prose full of backticks.\n            i += 1;\n            blocks.push({ type: \"code\", language: fence[2] || null, value: body.join(\"\\n\") });\n            continue;\n        }\n\n        const heading = HEADING.exec(line);\n        if (heading) {\n            blocks.push({\n                type: \"heading\",\n                level: heading[1].length,\n                children: parseInline(heading[2]),\n            });\n            i += 1;\n            continue;\n        }\n\n        if (RULE.test(line)) {\n            blocks.push({ type: \"rule\" });\n            i += 1;\n            continue;\n        }\n\n        if (QUOTE.test(line)) {\n            const inner: string[] = [];\n            while (i < lines.length) {\n                const match = QUOTE.exec(lines[i]);\n                if (match) {\n                    inner.push(match[1]);\n                    i += 1;\n                    continue;\n                }\n                // A blank line ends the quote; a plain line continues it (lazy\n                // continuation), which is how people actually write quotes.\n                if (lines[i].trim() === \"\") break;\n                inner.push(lines[i]);\n                i += 1;\n            }\n            blocks.push({ type: \"quote\", children: parseBlocks(inner) });\n            continue;\n        }\n\n        if (BULLET.test(line) || ORDERED.test(line)) {\n            const [list, next] = parseList(lines, i);\n            blocks.push(list);\n            i = next;\n            continue;\n        }\n\n        if (i + 1 < lines.length && line.includes(\"|\") && TABLE_DELIMITER.test(lines[i + 1])) {\n            const [table, next] = parseTable(lines, i);\n            blocks.push(table);\n            i = next;\n            continue;\n        }\n\n        const paragraph: string[] = [];\n        const hardBreaks: boolean[] = [];\n        while (i < lines.length && lines[i].trim() !== \"\") {\n            const current = lines[i];\n            if (\n                HEADING.test(current) ||\n                RULE.test(current) ||\n                FENCE.test(current) ||\n                QUOTE.test(current) ||\n                BULLET.test(current) ||\n                ORDERED.test(current)\n            ) {\n                break;\n            }\n            paragraph.push(current.trim());\n            hardBreaks.push(HARD_BREAK.test(current));\n            i += 1;\n        }\n        if (paragraph.length > 0) {\n            blocks.push({\n                type: \"paragraph\",\n                children: parseInline(joinParagraph(paragraph, hardBreaks)),\n            });\n        }\n    }\n\n    return blocks;\n}\n\n/**\n * Re-join the lines of a paragraph, keeping the two-space hard break.\n *\n * Every line is trimmed before it is parsed, because leading indentation is not\n * content and a trailing space is invisible noise in almost every line. The one\n * exception is the CommonMark hard break: two or more trailing spaces mean\n * \"break the line here\", so trimming first would delete the only mark that says\n * so and the `  \\n` inline rule could never match. The flag is recorded before\n * the trim and the marker put back here, on every line but the last — a break\n * after the final line of a paragraph would break into nothing.\n *\n * @param lines - The trimmed lines, in order.\n * @param hardBreaks - Whether each line ended in two or more spaces.\n * @returns The paragraph source the inline parser reads.\n */\nfunction joinParagraph(lines: readonly string[], hardBreaks: readonly boolean[]): string {\n    return lines\n        .map((line, index) => (hardBreaks[index] && index < lines.length - 1 ? `${line}  ` : line))\n        .join(\"\\n\");\n}\n\n/**\n * Parse a list, including nested lists and multi-line items.\n *\n * Nesting is decided by indentation relative to the **first** item's marker, so a\n * list indented inside a quote or another list still reads correctly.\n *\n * @returns The list node and the index of the first line after it.\n */\nfunction parseList(lines: string[], start: number): [MarkdownBlock, number] {\n    const bulletStart = BULLET.exec(lines[start]);\n    const orderedStart = ORDERED.exec(lines[start]);\n    // The two markers are mutually exclusive — `-*+` versus digits — so whichever\n    // matched decides the list kind.\n    const ordered = orderedStart !== null;\n    const first = orderedStart ?? bulletStart;\n    const baseIndent = (first?.[1] ?? \"\").length;\n    const startNumber = ordered ? Number(orderedStart?.[2] ?? 1) : 1;\n\n    const items: MarkdownBlock[][] = [];\n    let buffer: string[] = [];\n    let i = start;\n\n    const flush = (): void => {\n        if (buffer.length === 0) return;\n        items.push(parseBlocks(buffer));\n        buffer = [];\n    };\n\n    while (i < lines.length) {\n        const line = lines[i];\n        if (line.trim() === \"\") {\n            // A single blank line inside a list is a loose item, not the end. Two in\n            // a row end it, which is what a blank-then-paragraph document means.\n            if (i + 1 < lines.length && lines[i + 1].trim() !== \"\") {\n                buffer.push(\"\");\n                i += 1;\n                continue;\n            }\n            break;\n        }\n        const bullet = BULLET.exec(line);\n        const numbered = ORDERED.exec(line);\n        const match = bullet ?? numbered;\n\n        if (match && match[1].length <= baseIndent) {\n            const sameKind = ordered ? Boolean(numbered) : Boolean(bullet);\n            if (!sameKind) break;\n            flush();\n            buffer.push(match[3]);\n            i += 1;\n            continue;\n        }\n        if (match) {\n            // Deeper marker: keep the indentation so the recursive call sees a list.\n            buffer.push(line.slice(baseIndent));\n            i += 1;\n            continue;\n        }\n        if (line.search(/\\S/) > baseIndent) {\n            buffer.push(line.trim());\n            i += 1;\n            continue;\n        }\n        break;\n    }\n    flush();\n\n    return [{ type: \"list\", ordered, start: startNumber, items }, i];\n}\n\n/** Split a table row on unescaped pipes. */\nfunction splitRow(line: string): string[] {\n    const trimmed = line.trim().replace(/^\\|/, \"\").replace(/\\|$/, \"\");\n    const cells: string[] = [];\n    let current = \"\";\n    for (let i = 0; i < trimmed.length; i += 1) {\n        if (trimmed[i] === \"\\\\\" && trimmed[i + 1] === \"|\") {\n            current += \"|\";\n            i += 1;\n            continue;\n        }\n        if (trimmed[i] === \"|\") {\n            cells.push(current);\n            current = \"\";\n            continue;\n        }\n        current += trimmed[i];\n    }\n    cells.push(current);\n    return cells.map((cell) => cell.trim());\n}\n\n/**\n * Parse a GFM pipe table.\n *\n * @returns The table node and the index of the first line after it.\n */\nfunction parseTable(lines: string[], start: number): [MarkdownBlock, number] {\n    const head = splitRow(lines[start]);\n    const align: MarkdownAlign[] = splitRow(lines[start + 1]).map((cell) => {\n        const left = cell.startsWith(\":\");\n        const right = cell.endsWith(\":\");\n        if (left && right) return \"center\";\n        if (right) return \"right\";\n        if (left) return \"left\";\n        return null;\n    });\n\n    const rows: MarkdownInline[][][] = [];\n    let i = start + 2;\n    while (i < lines.length && lines[i].trim() !== \"\" && lines[i].includes(\"|\")) {\n        rows.push(splitRow(lines[i]).map(parseInline));\n        i += 1;\n    }\n\n    return [{ type: \"table\", align, head: head.map(parseInline), rows }, i];\n}\n\n/** Inline delimiters, longest marker first so `**` wins over `*`. */\nconst INLINE_RULES: Array<{\n    pattern: RegExp;\n    build: (match: RegExpExecArray) => MarkdownInline | null;\n}> = [\n    {\n        pattern: /^!\\[([^\\]]*)\\]\\(((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+\"[^\"]*\")?\\)/,\n        build: (match) => {\n            const src = safeImageUrl(match[2]);\n            return src ? { type: \"image\", src, alt: match[1] } : { type: \"text\", value: match[1] };\n        },\n    },\n    {\n        pattern: /^\\[([^\\]]*)\\]\\(((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+\"[^\"]*\")?\\)/,\n        build: (match) => {\n            const href = safeLinkUrl(match[2]);\n            const children = parseInline(match[1]);\n            // A rejected URL keeps the label as plain text: dropping the text too\n            // would silently delete words somebody wrote.\n            return href ? { type: \"link\", href, children } : { type: \"text\", value: match[1] };\n        },\n    },\n    { pattern: /^`([^`]+)`/, build: (match) => ({ type: \"code\", value: match[1] }) },\n    {\n        pattern: /^\\*\\*([\\s\\S]+?)\\*\\*/,\n        build: (match) => ({ type: \"strong\", children: parseInline(match[1]) }),\n    },\n    {\n        pattern: /^__([\\s\\S]+?)__/,\n        build: (match) => ({ type: \"strong\", children: parseInline(match[1]) }),\n    },\n    {\n        pattern: /^~~([\\s\\S]+?)~~/,\n        build: (match) => ({ type: \"del\", children: parseInline(match[1]) }),\n    },\n    {\n        pattern: /^\\*([^*\\n]+)\\*/,\n        build: (match) => ({ type: \"em\", children: parseInline(match[1]) }),\n    },\n    {\n        pattern: /^_([^_\\n]+)_/,\n        build: (match) => ({ type: \"em\", children: parseInline(match[1]) }),\n    },\n    {\n        pattern: /^<((?:https?:\\/\\/|mailto:)[^>\\s]+)>/,\n        build: (match) => {\n            const href = safeLinkUrl(match[1]);\n            return href\n                ? { type: \"link\", href, children: [{ type: \"text\", value: match[1] }] }\n                : { type: \"text\", value: match[1] };\n        },\n    },\n    { pattern: /^ {2,}\\n/, build: () => ({ type: \"break\" }) },\n    { pattern: /^\\\\\\n/, build: () => ({ type: \"break\" }) },\n];\n\n/**\n * Parse inline Markdown.\n *\n * A backslash escapes the next character, so `\\*not italic\\*` renders with the\n * asterisks. Anything that matches no rule is text — an unclosed `**` is two\n * asterisks, not a bold run to the end of the paragraph.\n *\n * @param source - One block's text.\n * @returns Inline nodes.\n */\nexport function parseInline(source: string): MarkdownInline[] {\n    const nodes: MarkdownInline[] = [];\n    let text = \"\";\n    let i = 0;\n\n    const flush = (): void => {\n        if (text) nodes.push({ type: \"text\", value: text });\n        text = \"\";\n    };\n\n    while (i < source.length) {\n        if (source[i] === \"\\\\\" && i + 1 < source.length && source[i + 1] !== \"\\n\") {\n            text += source[i + 1];\n            i += 2;\n            continue;\n        }\n\n        const rest = source.slice(i);\n        let matched = false;\n        for (const rule of INLINE_RULES) {\n            const match = rule.pattern.exec(rest);\n            if (!match) continue;\n            const node = rule.build(match);\n            if (!node) continue;\n            flush();\n            nodes.push(node);\n            i += match[0].length;\n            matched = true;\n            break;\n        }\n        if (matched) continue;\n\n        text += source[i];\n        i += 1;\n    }\n\n    flush();\n    return nodes;\n}\n"],"mappings":"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"}