blob: 8199b699c354f46688e7055f7deb1738aa6ffb16 [file] [log] [blame]
Earl Lee2e463fb2025-04-17 11:22:22 -07001import { marked } from "marked";
2
3/**
4 * Renders markdown content as HTML with proper security handling.
5 *
6 * @param markdownContent - The markdown string to render
7 * @returns The rendered HTML content as a string
8 */
9export async function renderMarkdown(markdownContent: string): Promise<string> {
10 try {
11 // Set markdown options for proper code block highlighting and safety
12 const markedOptions = {
13 gfm: true, // GitHub Flavored Markdown
14 breaks: true, // Convert newlines to <br>
15 headerIds: false, // Disable header IDs for safety
16 mangle: false, // Don't mangle email addresses
17 // DOMPurify is recommended for production, but not included in this implementation
18 };
19
20 return await marked.parse(markdownContent, markedOptions);
21 } catch (error) {
22 console.error("Error rendering markdown:", error);
23 // Fallback to plain text if markdown parsing fails
24 return markdownContent;
25 }
26}
27
28/**
29 * Process rendered markdown HTML element, adding security attributes to links.
30 *
31 * @param element - The HTML element containing rendered markdown
32 */
33export function processRenderedMarkdown(element: HTMLElement): void {
34 // Make sure links open in a new tab and have proper security attributes
35 const links = element.querySelectorAll("a");
36 links.forEach((link) => {
37 link.setAttribute("target", "_blank");
38 link.setAttribute("rel", "noopener noreferrer");
39 });
40}