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 • 27.4 kB
Source Map (JSON)
{"version":3,"file":"lib.umd.js","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":["_ref","content","options","className","style","_ref$asArticle","asArticle","id","aria","parsedContent","useMemo","markdown","_options$langPrefix","langPrefix","_options$customClasse","customClasses","_options$customStyles","customStyles","_options$linkTarget","linkTarget","_options$maxNestingLe","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","map","_ref2","value","join","reduce","processed","_ref3","forEach","line","index","push","React","createElement","key","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","apply","cell","endsWith","headerColumns","col","tableProps","textAlign","row","rowIndex","cellIndex","_match$","_match$2","src","_match$3","alt","isOrdered","processedText","dangerouslySetInnerHTML","__html","parse","error","console","containerProps","label","describedBy"],"mappings":"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"}