- Git repository mit .gitignore und .editorconfig - NPM-Projekt mit package.json und Dependencies - Projekt-Struktur (src/, docs/, examples/, test/) - Umfassende README.md mit Features und Roadmap - Architektur-Dokumentation mit Mermaid-Diagrammen - Design-Entscheidungen dokumentiert - .env.example für Forgejo API-Konfiguration - MIT Lizenz und Contributing Guidelines Status: Phase 1 - Core Parser (Setup abgeschlossen)
8.8 KiB
🎯 Design-Entscheidungen
Dieses Dokument dokumentiert wichtige technische Entscheidungen und deren Begründungen.
1. Parser-Technologie: unified/remark
Entscheidung: unified + remark als Core-Parser
Datum: 2025-10-01
Kontext
Es wurden mehrere Markdown-Parser für die Verarbeitung von Bildungsressourcen mit YAML Front Matter evaluiert.
Alternativen
| Parser | Vorteile | Nachteile | Score |
|---|---|---|---|
| marked | Sehr populär (30k+ GitHub Stars) Einfache API Gute Performance |
Fokus auf HTML-Output Kein AST-Zugriff Begrenzte Erweiterbarkeit |
6/10 |
| markdown-it | Erweiterbar durch Plugins Sehr schnell CommonMark-konform |
Komplexe API HTML-fokussiert Keine Browser-Isomorphie |
7/10 |
| unified/remark | AST-basiert (MDAST) Isomorph (Node + Browser) Riesiges Plugin-Ökosystem De-facto Standard |
Steilere Lernkurve Mehr Setup-Code |
9/10 |
| gray-matter + marked | Sehr einfach Schneller Einstieg |
Weniger strukturiert Begrenzte Flexibilität |
5/10 |
Entscheidungskriterien
-
AST-Zugriff (Gewichtung: 30%)
- ✅ unified: Voller MDAST-Zugriff für präzise Manipulation
- ❌ marked: Kein AST, nur Token-Stream
-
Isomorphie (Gewichtung: 25%)
- ✅ unified: Funktioniert identisch in Node.js und Browser
- ⚠️ markdown-it: Primär Node.js-fokussiert
-
Erweiterbarkeit (Gewichtung: 20%)
- ✅ unified: 200+ Plugins im Ökosystem
- ⚠️ marked: Begrenzte Extension-API
-
Standards (Gewichtung: 15%)
- ✅ unified: MDAST ist De-facto-Standard
- ✅ markdown-it: CommonMark-konform
-
Community & Wartung (Gewichtung: 10%)
- ✅ unified: Sehr aktiv, Teil von unifiedjs
- ✅ marked: Aktiv maintained
Konsequenzen
Positiv:
- Maximale Flexibilität für zukünftige Features
- AST-Transformationen für WordPress/Nostr
- Browser-Support ohne Code-Änderungen
- Plugin-System für Custom-Syntax
Negativ:
- Mehr initialer Setup-Code erforderlich
- Höhere Komplexität für einfache Use-Cases
- Größere Learning-Curve für Contributors
Code-Beispiel
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkFrontmatter from 'remark-frontmatter'
import remarkGfm from 'remark-gfm'
const processor = unified()
.use(remarkParse)
.use(remarkFrontmatter, ['yaml'])
.use(remarkGfm)
const ast = processor.parse(markdown)
2. YAML-Parser: yaml library
Entscheidung: yaml (v2.x) statt js-yaml
Datum: 2025-10-01
Kontext
YAML Front Matter muss zuverlässig geparst werden, inkl. komplexer Strukturen (Arrays, nested Objects).
Alternativen
| Library | Bundle Size | Spec-Konformität | Performance | Score |
|---|---|---|---|---|
| yaml | 42 KB | YAML 1.2 | Sehr gut | 9/10 |
| js-yaml | 68 KB | YAML 1.2 | Gut | 7/10 |
| JSON.parse | 0 KB (native) | N/A (nur JSON) | Exzellent | 3/10 |
Entscheidung: yaml
Gründe:
- ✅ Kleinere Bundle-Size (42 KB vs. 68 KB)
- ✅ Moderne API (ESM-first, TypeScript-Typen)
- ✅ Spec-compliant (YAML 1.2)
- ✅ Aktive Entwicklung
- ✅ Bessere Error-Messages
Konsequenzen
- Zuverlässiges Parsing komplexer YAML-Strukturen
- Gute Performance auch bei großen Dateien
- Kleinere Bundle-Size für Browser-Builds
3. HTTP-Client: Native fetch
Entscheidung: Native fetch API statt axios/node-fetch
Datum: 2025-10-01
Kontext
HTTP-Requests für Forgejo API und URL-basierte Markdown-Dateien.
Alternativen
| Option | Vorteile | Nachteile | Score |
|---|---|---|---|
| native fetch | In Node 18+ integriert Identisch im Browser Keine Dependencies |
Erfordert Node.js 18+ | 10/10 |
| axios | Feature-reich Interceptors Auto-Transform |
Zusätzliche Dependency 25 KB Bundle |
6/10 |
| node-fetch | Populär | Zusätzliche Dependency Nicht identisch mit Browser |
5/10 |
Entscheidung: Native fetch
Gründe:
- ✅ Zero Dependencies
- ✅ Isomorphie - Identische API in Node.js 18+ und Browser
- ✅ Standard - Web API Standard
- ✅ Async/await - Moderne Syntax
Requirement: Node.js >= 18.0.0
Konsequenzen
- Keine zusätzlichen Dependencies
- Code funktioniert ohne Änderungen im Browser
- Einfachere Maintenance
4. Module-System: ESM (ES Modules)
Entscheidung: Pure ESM, kein CommonJS
Datum: 2025-10-01
Kontext
Wahl des Module-Systems für das Projekt.
Entscheidung: ESM
{
"type": "module"
}
Gründe:
- ✅ Standard - ESM ist der JavaScript-Standard
- ✅ Browser-kompatibel - Direkt in Browsern lauffähig
- ✅ Tree-Shaking - Bessere Bundle-Optimierung
- ✅ Zukunftssicher - CommonJS ist Legacy
Import-Syntax:
import { parseMarkdownFile } from './parser.js'
// statt:
// const { parseMarkdownFile } = require('./parser.js')
Konsequenzen
Positiv:
- Moderne, zukunftssichere Codebasis
- Bessere Bundle-Optimierung
- Browser-Kompatibilität
Negativ:
- Keine Kompatibilität mit alten CommonJS-only Tools
- Erfordert
.jsExtension bei relativen Imports
5. Datenquelle-Priorität: API-First
Entscheidung: Primärer Fokus auf Forgejo API
Datum: 2025-10-01
Kontext
Verschiedene Datenquellen müssen unterstützt werden.
Priorisierung
-
Forgejo/Gitea API (Priorität 1)
- Primäre Datenquelle
- Direkter API-Zugriff
- No Git-Clone nötig
-
HTTP/HTTPS URLs (Priorität 2)
- Einfacher Fallback
- Für öffentliche Dateien
-
Lokale Dateien (Priorität 3)
- Für Entwicklung und Tests
- Node.js-only
Implementation
// src/index.js
export async function parseMarkdown(source, options = {}) {
if (isForgejoURL(source)) {
return parseFromForgejo(source, options)
} else if (isHTTPURL(source)) {
return parseFromURL(source, options)
} else {
return parseFromFile(source, options)
}
}
6. Error-Handling: Graceful Degradation
Entscheidung: Fehlertolerante Metadaten-Extraktion
Datum: 2025-10-01
Prinzip
Fehlende oder ungültige Metadaten führen nicht zu Fehler, sondern zu Warnings.
{
metadata: {
name: "Unbekannt", // Fallback
_warnings: [
"Fehlendes Feld: commonMetadata.name"
]
}
}
Gründe:
- ✅ Robustheit gegenüber unvollständigen Daten
- ✅ Bessere User Experience
- ✅ Ermöglicht partielle Verarbeitung
7. Browser-Build: Phase 2
Entscheidung: Browser-Build wird in Phase 2 implementiert
Datum: 2025-10-01
Kontext
Browser-Unterstützung ist wichtig, aber nicht im ersten Release.
Phasen-Plan
Phase 1 (jetzt):
- Node.js-kompatibel
- Isomorpher Code (aber kein Build)
- Tests in Node.js
Phase 2:
- ESM Browser-Build
- Bundling mit Rollup/esbuild
- Browser-Tests
Grund:
- Fokus auf Core-Funktionalität
- Vermeidung von Over-Engineering
- Schnelleres Initial-Release
8. Testing: Node.js Native Test Runner
Entscheidung: Node.js --test statt Jest/Mocha
Datum: 2025-10-01
Alternativen
| Test-Framework | Vorteile | Nachteile | Score |
|---|---|---|---|
| Node.js native | Keine Dependencies Schnell ESM-Support |
Weniger Features | 8/10 |
| Jest | Feature-reich Snapshots |
Langsam ESM-Probleme |
6/10 |
| Vitest | Schnell ESM-first |
Weitere Dependency | 7/10 |
Entscheidung: Node.js Native
// test/parser.test.js
import { test } from 'node:test'
import assert from 'node:assert'
test('parses markdown with YAML', async () => {
const result = await parseMarkdownFile('./fixtures/test.md')
assert.ok(result.yaml)
})
Gründe:
- ✅ Zero Dependencies für Tests
- ✅ Schnelle Ausführung
- ✅ Native ESM-Support
- ✅ Ausreichend für unsere Zwecke
Zusammenfassung: Technologie-Stack
| Kategorie | Technologie | Begründung |
|---|---|---|
| Parser | unified + remark | AST-basiert, isomorph, erweiterbar |
| YAML | yaml (v2.x) | Klein, modern, spec-compliant |
| HTTP | native fetch | Zero deps, isomorph |
| Modules | ESM | Standard, zukunftssicher |
| Testing | Node.js native | Zero deps, schnell |
| Code Style | Prettier + ESLint | Standard, automatisierbar |
Änderungshistorie
| Datum | Entscheidung | Autor |
|---|---|---|
| 2025-10-01 | Initial: unified/remark | Jörg Lohrer |
| 2025-10-01 | YAML library | Jörg Lohrer |
| 2025-10-01 | Native fetch | Jörg Lohrer |
| 2025-10-01 | ESM-only | Jörg Lohrer |
Fragen oder Vorschläge? → Issue erstellen