From 2bcb2451b4fa915dc1cb7b3362eb12f6c7c526ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lohrer?= Date: Wed, 15 Apr 2026 16:03:04 +0200 Subject: [PATCH] spa: markdown-renderer mit sanitize (tdd) --- app/src/lib/render/markdown.ts | 41 +++++++++++++++++++++++++++ app/tests/unit/markdown.test.ts | 49 +++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 app/src/lib/render/markdown.ts create mode 100644 app/tests/unit/markdown.test.ts diff --git a/app/src/lib/render/markdown.ts b/app/src/lib/render/markdown.ts new file mode 100644 index 0000000..d36bf64 --- /dev/null +++ b/app/src/lib/render/markdown.ts @@ -0,0 +1,41 @@ +import { marked } from 'marked'; +import DOMPurify from 'dompurify'; +import hljs from 'highlight.js/lib/core'; +import javascript from 'highlight.js/lib/languages/javascript'; +import bash from 'highlight.js/lib/languages/bash'; +import typescript from 'highlight.js/lib/languages/typescript'; +import json from 'highlight.js/lib/languages/json'; + +hljs.registerLanguage('javascript', javascript); +hljs.registerLanguage('js', javascript); +hljs.registerLanguage('typescript', typescript); +hljs.registerLanguage('ts', typescript); +hljs.registerLanguage('bash', bash); +hljs.registerLanguage('sh', bash); +hljs.registerLanguage('json', json); + +marked.use({ + breaks: true, + gfm: true, + renderer: { + code({ text, lang }) { + const language = lang && hljs.getLanguage(lang) ? lang : undefined; + const highlighted = language + ? hljs.highlight(text, { language }).value + : hljs.highlightAuto(text).value; + const cls = language ? ` language-${language}` : ''; + return `
${highlighted}
`; + }, + }, +}); + +/** + * Rendert einen Markdown-String zu sanitized HTML. + * Einziger Export des Moduls — so bleibt Austausch der Engine lokal. + */ +export function renderMarkdown(md: string): string { + const raw = marked.parse(md, { async: false }) as string; + return DOMPurify.sanitize(raw, { + ADD_ATTR: ['target', 'rel'], + }); +} diff --git a/app/tests/unit/markdown.test.ts b/app/tests/unit/markdown.test.ts new file mode 100644 index 0000000..3cddab0 --- /dev/null +++ b/app/tests/unit/markdown.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, it } from 'vitest'; +import { renderMarkdown } from '$lib/render/markdown'; + +describe('renderMarkdown', () => { + it('rendert einfachen Markdown-Text zu HTML', () => { + const html = renderMarkdown('**bold** and *italic*'); + expect(html).toContain('bold'); + expect(html).toContain('italic'); + }); + + it('entfernt world'); + expect(html).not.toContain('