(text)
| 218 | } |
| 219 | |
| 220 | function renderInlineMarkdown(text) { |
| 221 | const placeholders = []; |
| 222 | const addPlaceholder = (html) => { |
| 223 | const token = `@@MD${placeholders.length}@@`; |
| 224 | placeholders.push({ token, html }); |
| 225 | return token; |
| 226 | }; |
| 227 | |
| 228 | let rendered = text; |
| 229 | |
| 230 | rendered = rendered.replace(/`([^`]+)`/g, (_, code) => addPlaceholder(`<code>${escapeHtml(code)}</code>`)); |
| 231 | rendered = rendered.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (_, label, url) => { |
| 232 | const href = sanitizeUrl(url); |
| 233 | if (!href) { |
| 234 | return `${label} (${url})`; |
| 235 | } |
| 236 | return addPlaceholder( |
| 237 | `<a href="${escapeHtml(href)}" target="_blank" rel="noreferrer noopener">${escapeHtml(label)}</a>` |
| 238 | ); |
| 239 | }); |
| 240 | |
| 241 | rendered = escapeHtml(rendered); |
| 242 | rendered = rendered.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>"); |
| 243 | rendered = rendered.replace(/\*([^*]+)\*/g, "<em>$1</em>"); |
| 244 | rendered = rendered.replace(/_([^_]+)_/g, "<em>$1</em>"); |
| 245 | |
| 246 | for (const placeholder of placeholders) { |
| 247 | rendered = rendered.replaceAll(placeholder.token, placeholder.html); |
| 248 | } |
| 249 | |
| 250 | return rendered; |
| 251 | } |
| 252 | |
| 253 | function renderMarkdown(text) { |
| 254 | const lines = String(text).replace(/\r\n?/g, "\n").split("\n"); |
no test coverage detected