import { remark } from 'remark'
import remarkHtml from 'remark-html'
import remarkGfm from 'remark-gfm'
// Simple markdown processor with clean output
const processor = remark()
.use(remarkGfm) // Support for GitHub Flavored Markdown (tables, strikethrough, etc.)
// Das Ergebnis geht über dangerouslySetInnerHTML in die öffentliche Seite.
// Das Standard-Schema lässt Überschriften, Listen, Tabellen und http(s)- bzw.
// mailto-Links durch und entfernt Skripte, Event-Attribute, eingebettete
// Rahmen und javascript:-Ziele.
.use(remarkHtml, { sanitize: true })
/**
* Maskiert Text für die Ausgabe in HTML. Für Werte, die nicht durch den
* Markdown-Prozessor laufen – etwa Titel in der Druckfassung.
*/
export function escapeHtml(value: string): string {
return String(value)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
}
export async function markdownToHtml(markdown: string): Promise {
if (!markdown || typeof markdown !== 'string') {
return ''
}
try {
// Simple preprocessing for line breaks
const preprocessed = simplePreprocessLineBreaks(markdown)
const result = await processor.process(preprocessed)
return result.toString()
} catch (error) {
console.error('Error processing markdown:', error)
// Fallback: return sanitized markdown with basic formatting
return sanitizeAndFormatMarkdown(markdown)
}
}
// Synchronous version for use in components that can't use async
export function markdownToHtmlSync(markdown: string): string {
if (!markdown || typeof markdown !== 'string') {
return ''
}
try {
// Simple preprocessing for line breaks
const preprocessed = simplePreprocessLineBreaks(markdown)
const result = processor.processSync(preprocessed)
return result.toString()
} catch (error) {
console.error('Error processing markdown synchronously:', error)
// Fallback: return sanitized markdown with basic formatting
return sanitizeAndFormatMarkdown(markdown)
}
}
// Simple preprocessing for consistent line breaks
function simplePreprocessLineBreaks(markdown: string): string {
return markdown
// Normalize line endings
.replace(/\r\n/g, '\n')
.replace(/\r/g, '\n')
// Add two spaces before single line breaks for hard breaks
.replace(/([^\n])\n([^\n])/g, '$1 \n$2')
}
// Fallback function for basic markdown formatting
function sanitizeAndFormatMarkdown(text: string): string {
// Simple, clean markdown processing
return text
// Escape HTML
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
// Basic markdown formatting
.replace(/\*\*(.*?)\*\*/g, '$1')
.replace(/\*(.*?)\*/g, '$1')
.replace(/`(.*?)`/g, '$1')
// Headers
.replace(/^### (.*$)/gm, '$1
')
.replace(/^## (.*$)/gm, '$1
')
.replace(/^# (.*$)/gm, '$1
')
// Lists
.replace(/^\* (.*$)/gm, '• $1')
.replace(/^- (.*$)/gm, '• $1')
.replace(/^(\d+)\. (.*$)/gm, '$1. $2')
// Line breaks
.replace(/\n/g, '
')
// Clean up multiple
tags
.replace(/
/g, '
')
}