markdown-parser-react
Version:
A lightweight and configurable Markdown renderer for React and Next.js with syntax highlighting, definition lists, images, math and table support.
1 lines • 26.6 kB
Source Map (JSON)
{"version":3,"file":"lib.modern.mjs","sources":["../src/Markdown.tsx","../src/parser.tsx"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { parse, ParseOptions } from \"./parser\";\n\nexport interface MarkdownProps {\n /**\n * The markdown content to be rendered.\n * @example\n * ```md\n * # Hello World\n *\n * This is a **markdown** paragraph with [links](https://example.com)\n * ```\n */\n content: string;\n\n /**\n * Configuration options for the markdown parser.\n * @optional\n */\n options?: ParseOptions;\n\n /**\n * Additional CSS class names to be applied to the container.\n * @optional\n */\n className?: string;\n\n /**\n * Custom styles to be applied to the container.\n * @optional\n */\n style?: React.CSSProperties;\n\n /**\n * Whether to wrap the content in an article element instead of a div.\n * Recommended for semantic HTML when rendering full articles or blog posts.\n * @default false\n * @optional\n */\n asArticle?: boolean;\n\n /**\n * Custom ID to be applied to the container.\n * @optional\n */\n id?: string;\n\n /**\n * Additional accessibility attributes for the container.\n * @optional\n */\n aria?: {\n label?: string;\n describedBy?: string;\n };\n}\n\n/**\n * A React component that renders markdown content with customizable options.\n *\n * @example\n * ```tsx\n * import Markdown from \"markdown-parser-react\";\n * // Basic usage\n * <Markdown content=\"# Hello World\" />\n *\n * // With custom options\n * <Markdown\n * content={markdownContent}\n * options={{\n * enabledFeatures: {\n * footnotes: true,\n * taskLists: true\n * },\n * customStyles: {\n * headings: {\n * color: 'blue'\n * }\n * }\n * }}\n * className=\"custom-markdown\"\n * asArticle={true}\n * />\n * ```\n * * @remarks\n * This code is open source under the MIT license. The author can be hired by visiting [Jerry's LinkedIn](https://www.linkedin.com/in/jerrythejsguy/).\n */\nexport function Markdown({\n content,\n options,\n className,\n style,\n asArticle = false,\n id,\n aria,\n}: MarkdownProps) {\n // Memoize parsed content to prevent unnecessary re-renders\n const parsedContent = useMemo(() => {\n try {\n return parse(content, options);\n } catch (error) {\n console.error(\"Error parsing markdown:\", error);\n return [<p key=\"error\">Error parsing markdown content</p>];\n }\n }, [content, options]);\n\n // Common props for container\n const containerProps = {\n className: className\n ? `markdown-container ${className}`\n : \"markdown-container\",\n style,\n id,\n ...(aria?.label && { \"aria-label\": aria.label }),\n ...(aria?.describedBy && { \"aria-describedby\": aria.describedBy }),\n };\n\n // Use article tag for full content, div for partial content\n const Container = asArticle ? \"article\" : \"div\";\n\n return <Container {...containerProps}>{parsedContent}</Container>;\n}\n\nexport const defaultStyles = `\n .markdown-container {\n font-family: system-ui, -apple-system, sans-serif;\n line-height: 1.6;\n color: #333;\n max-width: 100%;\n }\n\n .markdown-container img {\n max-width: 100%;\n height: auto;\n }\n\n .markdown-container pre {\n overflow-x: auto;\n padding: 1rem;\n background-color: #f6f8fa;\n border-radius: 4px;\n }\n\n .markdown-container blockquote {\n border-left: 4px solid #ddd;\n margin: 0;\n padding-left: 1rem;\n color: #666;\n }\n`;\n\nexport default Markdown;\n","/**\n * Parses markdown content into React nodes with support for custom styles, classes, and various markdown features.\n *\n * @param markdown - The markdown string to parse.\n * @param options - Optional settings for parsing the markdown.\n * @returns An array of React nodes representing the parsed markdown content.\n *\n * @example\n * ```ts\n * const markdown = \"# Hello World\\nThis is a paragraph.\";\n * const options = {\n * customClasses: {\n * headings: \"my-heading-class\",\n * paragraphs: \"my-paragraph-class\",\n * },\n * customStyles: {\n * headings: { color: \"red\" },\n * paragraphs: { fontSize: \"16px\" },\n * },\n * };\n * const parsedContent = parse(markdown, options);\n * ```\n *\n * @remarks\n * This code is open source under the MIT license. The author can be hired by visiting [Jerry's LinkedIn](https://www.linkedin.com/in/jerrythejsguy/).\n */\n\nimport React, { CSSProperties } from \"react\";\n\ninterface CustomClasses {\n headings?: string;\n paragraphs?: string;\n lists?: string;\n blockquotes?: string;\n codeBlocks?: string;\n tables?: string;\n links?: string;\n images?: string;\n}\n\ninterface CustomStyles {\n headings?: CSSProperties;\n paragraphs?: CSSProperties;\n lists?: CSSProperties;\n blockquotes?: CSSProperties;\n codeBlocks?: CSSProperties;\n tables?: CSSProperties;\n links?: CSSProperties;\n images?: CSSProperties;\n}\n\n/**\n * Options for parsing markdown content.\n */\nexport interface ParseOptions {\n /**\n * Prefix for language-specific code block classes.\n * @example \"language-\"\n */\n langPrefix?: string;\n\n /**\n * Custom CSS classes for different markdown elements.\n */\n customClasses?: CustomClasses;\n\n /**\n * Custom CSS styles for different markdown elements.\n */\n customStyles?: CustomStyles;\n\n /**\n * Target attribute for links.\n * @default \"_blank\"\n */\n linkTarget?: \"_blank\" | \"_self\" | \"_parent\" | \"_top\";\n\n /**\n * Whether to sanitize HTML content.\n * @default false\n */\n sanitizeHtml?: boolean;\n\n /**\n * Maximum allowed nesting level for lists.\n * @default 6\n */\n maxNestingLevel?: number;\n}\n\ntype ElementType =\n | \"headings\"\n | \"paragraphs\"\n | \"lists\"\n | \"blockquotes\"\n | \"codeBlocks\"\n | \"tables\"\n | \"links\"\n | \"images\";\n\nexport function parse(\n markdown: string,\n options: ParseOptions = {}\n): React.ReactNode[] {\n const {\n langPrefix = \"language-\",\n customClasses = {},\n customStyles = {},\n linkTarget = \"_blank\",\n sanitizeHtml = true,\n maxNestingLevel = 6,\n } = options;\n\n const lines = markdown.trim().split(/\\r\\n|\\n/);\n const html: React.ReactNode[] = [];\n let inCodeBlock = false;\n let codeBlockLang: string | null = null;\n let codeBlock: string[] = [];\n let nestingLevel = 0;\n let skipLines = 0;\n\n // Helper function to safely merge styles and classes\n const getElementProperties = (\n elementType: ElementType,\n additionalProps: Record<string, any> = {}\n ): { style?: CSSProperties; className?: string } => {\n const style: CSSProperties = {\n ...customStyles[elementType],\n ...additionalProps.style,\n };\n\n const className = customClasses[elementType]\n ? `${customClasses[elementType]}${\n additionalProps.className ? ` ${additionalProps.className}` : \"\"\n }`\n : additionalProps.className;\n\n return {\n ...additionalProps,\n style: Object.keys(style).length > 0 ? style : undefined,\n className: className || undefined,\n };\n };\n\n // Enhanced sanitization function\n const sanitize = (text: string): string => {\n if (!sanitizeHtml) return text;\n return text\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n };\n\n // Process all inline formatting\n const processInlineFormatting = (text: string): string => {\n if (!text) return \"\";\n\n const replacements = [\n {\n regex: /\\$(.+?)\\$/g,\n replace: (_: string, math: string) =>\n //TODO Return Math tags properly \n `<span class=\"math-inline\">${math}</span>`,\n },\n { regex: /~~(.+?)~~/g, replace: \"<del>$1</del>\" },\n { regex: /\\^(.+?)\\^/g, replace: \"<sup>$1</sup>\" },\n { regex: /~(.+?)~/g, replace: \"<sub>$1</sub>\" },\n { regex: /==(.+?)==/g, replace: \"<mark>$1</mark>\" },\n { regex: /\\*\\*\\*(.+?)\\*\\*\\*/g, replace: \"<strong><em>$1</em></strong>\" },\n { regex: /___(.+?)___/g, replace: \"<strong><em>$1</em></strong>\" },\n { regex: /\\*\\*(.+?)\\*\\*/g, replace: \"<strong>$1</strong>\" },\n { regex: /__(.+?)__/g, replace: \"<strong>$1</strong>\" },\n { regex: /\\*(.+?)\\*/g, replace: \"<em>$1</em>\" },\n { regex: /_(.+?)_/g, replace: \"<em>$1</em>\" },\n { regex: /`([^`]+)`/g, replace: \"<code>$1</code>\" },\n {\n regex: /\\[([^\\]]+)\\]\\(([^)\"'\\s]+)(?:\\s+\"([^\"]+)\")?\\)/g,\n replace: (\n match: string,\n text: string,\n url: string,\n title: string | undefined\n ): string => {\n const props = getElementProperties(\"links\", {\n href: url,\n ...(title && { title }), // Only add title if it exists\n target: linkTarget,\n rel: linkTarget === \"_blank\" ? \"noopener noreferrer\" : undefined,\n });\n\n const attributes = Object.entries(props)\n .filter(([_, value]) => value !== undefined) // Filter out undefined values\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join(\" \");\n\n return `<a ${attributes}>${text}</a>`;\n },\n },\n ];\n\n return replacements.reduce(\n (processed, { regex, replace }) =>\n processed.replace(regex, replace as any),\n text\n );\n };\n\n // Process each line\n lines.forEach((line, index) => {\n if (skipLines > 0) {\n skipLines--;\n return;\n }\n // Skip processing if max nesting level is reached\n if (nestingLevel > maxNestingLevel) {\n html.push(<p key={`error-${index}`}>Maximum nesting level reached</p>);\n return;\n }\n\n // Inside the parse function, where we handle different line types:\n\n // Handle definition lists - needs multiple line lookahead\n if (index < lines.length - 1) {\n const currentLine = line.trim();\n const nextLine = lines[index + 1].trim();\n\n if (\n currentLine !== \"\" &&\n !currentLine.startsWith(\":\") &&\n nextLine.startsWith(\":\")\n ) {\n const term = currentLine;\n const definitions: string[] = [];\n let i = index + 1;\n\n // Collect all subsequent definitions\n while (i < lines.length && lines[i].trim().startsWith(\":\")) {\n definitions.push(lines[i].trim().slice(1).trim());\n i++;\n }\n\n // Set the number of lines to skip\n skipLines = definitions.length;\n\n // Create the definition list element\n html.push(\n <dl key={`dl-${index}`} {...getElementProperties(\"lists\")}>\n <dt>{processInlineFormatting(term)}</dt>\n {definitions.map((def, defIndex) => (\n <dd key={defIndex}>{processInlineFormatting(def)}</dd>\n ))}\n </dl>\n );\n\n return;\n }\n }\n\n // Task lists with nested support\n if (/^(\\s*)-\\s\\[(x| )\\]/.test(line.trim())) {\n const indent = (line.match(/^\\s*/) || [\"\"])[0].length;\n const checked = line.includes(\"[x]\");\n const text = line.replace(/^(\\s*)-\\s\\[(x| )\\]\\s*/, \"\");\n\n nestingLevel = Math.floor(indent / 2);\n\n html.push(\n <div\n key={`task-${index}`}\n {...getElementProperties(\"lists\")}\n style={{ marginLeft: `${nestingLevel * 20}px` }}\n >\n <input type=\"checkbox\" checked={checked} readOnly />\n <span>{processInlineFormatting(text)}</span>\n </div>\n );\n return;\n }\n\n // Headers with custom IDs\n if (/^#{1,6}\\s/.test(line.trim())) {\n const level = line.match(/^#{1,6}/)?.[0].length || 1;\n const text = line.replace(/^#{1,6}\\s/, \"\");\n const idMatch = text.match(/\\{#([^}]+)\\}/);\n const id = idMatch ? idMatch[1] : undefined;\n const cleanText = text.replace(/\\{#([^}]+)\\}/, \"\").trim();\n\n html.push(\n React.createElement(\n `h${level}`,\n {\n key: `h-${index}`,\n id,\n ...getElementProperties(\"headings\"),\n },\n processInlineFormatting(cleanText)\n )\n );\n return;\n }\n\n // Code blocks\n if (/^```(\\S+)?(\\s+\\{.*\\})?$/.test(line.trim())) {\n inCodeBlock = !inCodeBlock;\n if (inCodeBlock) {\n const match = line.trim().match(/^```(\\S+)?/);\n codeBlockLang = match?.[1] || null;\n codeBlock = [];\n } else {\n const code = codeBlock.join(\"\\n\");\n html.push(\n <pre key={`code-${index}`} {...getElementProperties(\"codeBlocks\")}>\n <code\n className={\n codeBlockLang ? `${langPrefix}${codeBlockLang}` : undefined\n }\n >\n {code}\n </code>\n </pre>\n );\n codeBlock = [];\n codeBlockLang = null;\n }\n return;\n }\n\n if (inCodeBlock) {\n codeBlock.push(line);\n return;\n }\n\n if (/^>\\s/.test(line.trim())) {\n const citationMatch = line.match(/^>\\s?(.+?)(?:\\s+--\\s+(.+))?$/);\n if (citationMatch) {\n const [, text, citation] = citationMatch;\n html.push(\n <blockquote\n key={`quote-${index}`}\n {...getElementProperties(\"blockquotes\")}\n >\n {processInlineFormatting(text)}\n {citation && <footer>— {citation}</footer>}\n </blockquote>\n );\n return;\n } else {\n html.push(\n <blockquote\n key={`quote-${index}`}\n {...getElementProperties(\"blockquotes\")}\n >\n {processInlineFormatting(line.trim().replace(/^>\\s/, \"\"))}\n </blockquote>\n );\n return;\n }\n }\n\n // Tables with alignment\n if (/^\\|(.+\\|)+/.test(line.trim())) {\n // Skip if we're in the middle of processing a table (i.e., this is a data row or alignment row)\n if (index > 0 && /^\\|(.+\\|)+/.test(lines[index - 1].trim())) {\n return;\n }\n // Check if this is not an alignment row - prevent treating alignment rows as new table starts\n if (!/^\\|(\\s*:?-+:?\\s*\\|)+/.test(line.trim())) {\n const tableRows: string[] = [line.trim()]; // Start with the header row\n const alignments: Array<\"left\" | \"center\" | \"right\"> = [];\n\n // Check for alignment row directly after the header row\n if (\n index + 1 < lines.length &&\n /^\\|(\\s*:?-+:?\\s*\\|)+/.test(lines[index + 1].trim())\n ) {\n const alignmentRow = lines[index + 1].trim();\n alignments.push(\n ...alignmentRow\n .split(\"|\")\n .slice(1, -1)\n .map((cell) => {\n if (cell.trim().startsWith(\":\") && cell.trim().endsWith(\":\"))\n return \"center\";\n if (cell.trim().endsWith(\":\")) return \"right\";\n return \"left\";\n })\n );\n index++; // Skip alignment row\n }\n\n // Collect all body rows following the alignment row\n while (\n index + 1 < lines.length &&\n /^\\|(.+\\|)+/.test(lines[index + 1].trim()) &&\n !/^\\|(\\s*:?-+:?\\s*\\|)+/.test(lines[index + 1].trim())\n ) {\n tableRows.push(lines[index + 1].trim());\n index++;\n }\n\n // Only proceed if we have a valid table structure\n if (tableRows.length > 0) {\n // Process the header row to extract columns\n const headerRow = tableRows[0];\n const headerColumns = headerRow\n .split(\"|\")\n .slice(1, -1)\n .map((col) => col.trim());\n\n // Generate the HTML for the table\n const tableProps = getElementProperties(\"tables\");\n\n html.push(\n <table key={`table-${index}`} {...tableProps}>\n <thead>\n <tr>\n {headerColumns.map((col, i) => (\n <th key={i} style={{ textAlign: alignments[i] || \"left\" }}>\n {processInlineFormatting(col)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {tableRows.slice(1).map((row, rowIndex) => (\n <tr key={rowIndex}>\n {row\n .split(\"|\")\n .slice(1, -1)\n .map((cell, cellIndex) => (\n <td\n key={cellIndex}\n style={{ textAlign: alignments[cellIndex] || \"left\" }}\n >\n {processInlineFormatting(cell.trim())}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n );\n }\n }\n return;\n }\n //TODO MOVE REGEX TO A CONFIG\n // Images\n if (/!\\[([^\\]]*)\\]\\(([^)\\s]+)(?:\\s+\"([^\"]+)\")?\\)/.test(line.trim())) {\n const match = line\n .trim()\n .match(/!\\[([^\\]]*)\\]\\(([^)\\s]+)(?:\\s+\"([^\"]+)\")?\\)/);\n\n if (match) {\n const [, alt = \"\", src = \"\", title = \"\"] = match;\n html.push(\n <img\n key={`img-${index}`}\n src={src}\n alt={alt}\n title={title || undefined}\n {...getElementProperties(\"images\")}\n />\n );\n }\n return; // Stop further processing for this line\n }\n\n // Lists (ordered and unordered)\n if (/^(\\s*)([-*+]|\\d+\\.)\\s/.test(line.trim())) {\n const indent = (line.match(/^\\s*/) || [\"\"])[0].length;\n const isOrdered = /^\\s*\\d+\\./.test(line);\n const text = line.replace(/^(\\s*)([-*+]|\\d+\\.)\\s/, \"\");\n\n nestingLevel = Math.floor(indent / 2);\n\n const ListTag = isOrdered ? \"ol\" : \"ul\";\n\n html.push(\n React.createElement(\n ListTag,\n {\n key: `list-${index}`,\n ...getElementProperties(\"lists\"),\n style: { marginLeft: `${nestingLevel * 20}px` },\n },\n <li>{processInlineFormatting(text)}</li>\n )\n );\n return;\n }\n\n // Process regular paragraphs with inline formatting\n const processedText = processInlineFormatting(line.trim());\n if (processedText.trim()) {\n html.push(\n <p key={`p-${index}`} {...getElementProperties(\"paragraphs\")}>\n <span dangerouslySetInnerHTML={{ __html: processedText }} />\n </p>\n );\n }\n });\n\n return html;\n}\n"],"names":["Markdown","content","options","className","style","asArticle","id","aria","parsedContent","useMemo","markdown","langPrefix","customClasses","customStyles","linkTarget","sanitizeHtml","maxNestingLevel","lines","trim","split","html","inCodeBlock","codeBlockLang","codeBlock","nestingLevel","skipLines","getElementProperties","elementType","additionalProps","_extends","Object","keys","length","undefined","processInlineFormatting","text","replacements","regex","replace","_","math","match","url","title","props","href","target","rel","entries","filter","value","map","key","join","reduce","processed","forEach","line","index","push","React","createElement","currentLine","nextLine","startsWith","term","definitions","i","slice","def","defIndex","test","indent","checked","includes","Math","floor","marginLeft","type","readOnly","_line$match","level","idMatch","cleanText","code","citationMatch","citation","tableRows","alignments","alignmentRow","cell","endsWith","headerColumns","col","tableProps","textAlign","row","rowIndex","cellIndex","alt","src","isOrdered","processedText","dangerouslySetInnerHTML","__html","parse","error","console","containerProps","label","describedBy"],"mappings":"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"}