/** * 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') // 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
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 }