/** * Lightweight markdown to HTML converter for uni-app rich-text component. * Supports: headings, bold, italic, inline code, code blocks, * unordered lists, ordered lists, blockquotes, horizontal rules, line breaks. */ function escapeHtml(str) { return str .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') } /** * Convert markdown string to HTML string. * @param {string} text * @returns {string} */ export function renderMarkdown(text) { if (!text) return '' // 1. Protect code blocks first (```) const codeBlocks = [] let html = text.replace(/```([\w]*)\n?([\s\S]*?)```/g, (_, lang, code) => { const idx = codeBlocks.length const escaped = escapeHtml(code.replace(/^\n/, '').replace(/\n$/, '')) const langAttr = lang ? ` class="language-${lang}"` : '' codeBlocks.push(`
${escaped}
`) return `\x00CODE${idx}\x00` }) // 2. Escape remaining HTML to prevent injection html = html.replace(/&/g, '&').replace(//g, '>') // 3. Protect inline code (`) const inlineCodes = [] html = html.replace(/`([^`\n]+)`/g, (_, code) => { const idx = inlineCodes.length inlineCodes.push(`${escapeHtml(code)}`) return `\x00INLINE${idx}\x00` }) // 4. Headings (must be at line start) html = html.replace(/^#{4,6} (.+)$/gm, '
$1
') html = html.replace(/^### (.+)$/gm, '

$1

') html = html.replace(/^## (.+)$/gm, '

$1

') html = html.replace(/^# (.+)$/gm, '

$1

') // 5. Horizontal rules html = html.replace(/^[ \t]*(?:---+|===+|\*\*\*+)[ \t]*$/gm, '
') // 6. Blockquotes html = html.replace(/^> ?(.*)$/gm, '
$1
') // 7. Bold + Italic html = html.replace(/\*\*\*(.+?)\*\*\*/g, '$1') html = html.replace(/___(.+?)___/g, '$1') // 8. Bold html = html.replace(/\*\*(.+?)\*\*/g, '$1') html = html.replace(/__(.+?)__/g, '$1') // 9. Italic html = html.replace(/\*(.+?)\*/g, '$1') html = html.replace(/_(.+?)_/g, '$1') // 10. Lists — collect consecutive list lines into '); inUl = false } if (inOl) { result.push(''); inOl = false } result.push(line) } } if (inUl) result.push('') if (inOl) result.push('') html = result.join('\n') // 11. Paragraphs — split by blank lines, wrap plain text blocks in

const blocks = html.split(/\n{2,}/) html = blocks.map(block => { block = block.trim() if (!block) return '' // Already wrapped in a block-level tag — leave as-is if (/^<(h[1-6]|ul|ol|li|pre|blockquote|hr)[\s/>]/.test(block)) return block // Has multiple lines → join with
block = block.replace(/\n/g, '
') return `

${block}

` }).join('\n') // 12. Restore inline codes and code blocks inlineCodes.forEach((code, idx) => { html = html.replace(`\x00INLINE${idx}\x00`, code) }) codeBlocks.forEach((code, idx) => { html = html.replace(`\x00CODE${idx}\x00`, code) }) return html }