syncova-policies/lib/markdown.ts
Weapie 328f042307 Syncova Policies: Portal für DSGVO-Datenschutzerklärungen
Next.js 15 App Router mit öffentlicher Übersicht und geschütztem
Admin-Bereich zur Pflege von Datenschutzerklärungen nach Art. 12ff. DSGVO.

- Oberfläche auf Basis von shadcn/ui, Inter lokal eingebunden
- Prisma/SQLite, Auth.js mit Credentials-Provider und Rollen
- Massenimport der Merkblätter des Amtes Leezen aus PDF
- Docker-Image (standalone) und Gitea-Workflow zur Veröffentlichung

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 16:23:51 +02:00

81 lines
2.6 KiB
TypeScript

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.)
.use(remarkHtml, { sanitize: false }) // Convert to HTML without sanitization
export async function markdownToHtml(markdown: string): Promise<string> {
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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#x27;')
// Basic markdown formatting
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
.replace(/`(.*?)`/g, '<code>$1</code>')
// Headers
.replace(/^### (.*$)/gm, '<h3>$1</h3>')
.replace(/^## (.*$)/gm, '<h2>$1</h2>')
.replace(/^# (.*$)/gm, '<h1>$1</h1>')
// Lists
.replace(/^\* (.*$)/gm, '• $1')
.replace(/^- (.*$)/gm, '• $1')
.replace(/^(\d+)\. (.*$)/gm, '$1. $2')
// Line breaks
.replace(/\n/g, '<br>')
// Clean up multiple <br> tags
.replace(/<br><br>/g, '<br>')
}