finale Anpassung
This commit is contained in:
@@ -4,11 +4,10 @@ package article
|
||||
// internal/article/load.go (gekürzt)
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -18,85 +17,178 @@ import (
|
||||
|
||||
md "github.com/gomarkdown/markdown"
|
||||
"github.com/gomarkdown/markdown/html"
|
||||
"github.com/gomarkdown/markdown/parser"
|
||||
)
|
||||
|
||||
// LoadDir liest alle *.html und *.md unter root/content,
|
||||
// parst Front‑Matter (JSON‑Kommentar in der 1. Zeile) und liefert []Article.
|
||||
func LoadDir(root string) ([]Article, error) {
|
||||
var out []Article
|
||||
seen := make(map[string]bool) // Duplikats‑Check
|
||||
|
||||
// gültige Extension‑Maske
|
||||
exts := parser.CommonExtensions | parser.AutoHeadingIDs | parser.DefinitionLists
|
||||
mdRenderer := html.NewRenderer(html.RendererOptions{
|
||||
Flags: html.CommonFlags | html.HrefTargetBlank,
|
||||
})
|
||||
|
||||
err := filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {
|
||||
if err != nil || d.IsDir() {
|
||||
return err
|
||||
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
|
||||
ext := filepath.Ext(p)
|
||||
if ext != ".html" && ext != ".md" {
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.Open(p)
|
||||
if err != nil {
|
||||
return err
|
||||
ext := strings.ToLower(filepath.Ext(path))
|
||||
if ext != ".md" && ext != ".html" {
|
||||
return nil // uninteressante Datei
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
r := bufio.NewReader(f)
|
||||
headerLine, err := r.ReadBytes('\n')
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read %s: %w", path, err)
|
||||
}
|
||||
|
||||
parts := bytes.SplitN(raw, []byte("\n"), 2)
|
||||
if len(parts) < 2 {
|
||||
return fmt.Errorf("%s: missing front‑matter", path)
|
||||
}
|
||||
|
||||
headerJSON, err := extractJSONHeader(parts[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", path, err)
|
||||
}
|
||||
|
||||
var meta struct {
|
||||
Title string `json:"title"`
|
||||
Date string `json:"date"`
|
||||
Slug string `json:"slug"`
|
||||
Cover string `json:"cover"` // NEW
|
||||
Cover string `json:"cover"`
|
||||
}
|
||||
clean := strings.TrimSpace(string(headerLine))
|
||||
|
||||
clean = strings.TrimPrefix(clean, "<!--")
|
||||
clean = strings.TrimSpace(clean)
|
||||
|
||||
// alles nach dem ersten "-->" abschneiden (egal ob Leerzeichen davor)
|
||||
if idx := strings.Index(clean, "-->"); idx != -1 {
|
||||
clean = clean[:idx]
|
||||
}
|
||||
clean = strings.TrimSpace(clean)
|
||||
|
||||
if err := json.Unmarshal([]byte(clean), &meta); err != nil {
|
||||
return fmt.Errorf("Front‑Matter in %s: %w (%q)", p, err, clean)
|
||||
if err := json.Unmarshal(headerJSON, &meta); err != nil {
|
||||
return fmt.Errorf("%s: %w", path, err)
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return err
|
||||
// Fallback‑Slug aus Dateinamen
|
||||
if meta.Slug == "" {
|
||||
meta.Slug = strings.TrimSuffix(filepath.Base(path), ext)
|
||||
}
|
||||
if seen[meta.Slug] {
|
||||
return fmt.Errorf("%s: duplicate slug %q", path, meta.Slug)
|
||||
}
|
||||
seen[meta.Slug] = true
|
||||
|
||||
htmlBody := bodyBytes
|
||||
body := parts[1]
|
||||
if ext == ".md" {
|
||||
mdParser := parser.NewWithExtensions(exts)
|
||||
htmlBody = md.ToHTML(bodyBytes, mdParser, mdRenderer)
|
||||
body = md.ToHTML(body, nil, mdRenderer) // frischer Parser pro Call
|
||||
}
|
||||
|
||||
date, err := time.Parse("2006-01-02", meta.Date)
|
||||
if err != nil {
|
||||
fmt.Println("Time", err, date)
|
||||
return fmt.Errorf("%s: parse date: %w", path, err)
|
||||
}
|
||||
|
||||
out = append(out, Article{
|
||||
Title: meta.Title,
|
||||
Slug: meta.Slug,
|
||||
Date: date,
|
||||
Cover: meta.Cover, // NEW
|
||||
Body: template.HTML(htmlBody),
|
||||
Cover: meta.Cover,
|
||||
Body: template.HTML(body),
|
||||
})
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Date.After(out[j].Date) })
|
||||
return out, err
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ---------- NEU ----------
|
||||
type StaticPage struct {
|
||||
Title string
|
||||
Slug string
|
||||
Body template.HTML
|
||||
}
|
||||
|
||||
// helper.go – einmal zentral verwenden
|
||||
func extractJSONHeader(line []byte) ([]byte, error) {
|
||||
s := strings.TrimSpace(string(line))
|
||||
s = strings.TrimPrefix(s, "<!--")
|
||||
|
||||
// alles bis zum ersten "-->" abschneiden
|
||||
if idx := strings.Index(s, "-->"); idx != -1 {
|
||||
s = s[:idx]
|
||||
}
|
||||
s = strings.TrimSpace(s)
|
||||
|
||||
// optional: falls doch kein JSON, besserer Fehler
|
||||
if !strings.HasPrefix(s, "{") {
|
||||
return nil, fmt.Errorf("no JSON front‑matter found")
|
||||
}
|
||||
return []byte(s), nil
|
||||
}
|
||||
|
||||
// LoadStatic liest alle .md/.html‑Dateien unter dir und liefert sie als Map[slug]StaticPage.
|
||||
func LoadStatic(dir string) (map[string]StaticPage, error) {
|
||||
pages := make(map[string]StaticPage)
|
||||
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, e := range entries {
|
||||
if e.IsDir() {
|
||||
continue
|
||||
}
|
||||
ext := filepath.Ext(e.Name())
|
||||
if ext != ".md" && ext != ".html" {
|
||||
continue // unbekanntes Format
|
||||
}
|
||||
|
||||
path := filepath.Join(dir, e.Name())
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read %s: %w", path, err)
|
||||
}
|
||||
|
||||
// Front‑Matter (erste Zeile) herauslösen
|
||||
parts := bytes.SplitN(raw, []byte("\n"), 2)
|
||||
if len(parts) < 2 {
|
||||
return nil, fmt.Errorf("%s: missing front‑matter", path)
|
||||
}
|
||||
|
||||
headerJSON, err := extractJSONHeader(parts[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", path, err)
|
||||
}
|
||||
|
||||
var meta struct {
|
||||
Title string `json:"title"`
|
||||
Slug string `json:"slug"`
|
||||
}
|
||||
if err := json.Unmarshal(headerJSON, &meta); err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", path, err)
|
||||
}
|
||||
|
||||
// Fallback: Slug aus Dateinamen ableiten, falls im Header leer
|
||||
if meta.Slug == "" {
|
||||
meta.Slug = strings.TrimSuffix(e.Name(), ext)
|
||||
}
|
||||
if _, dup := pages[meta.Slug]; dup {
|
||||
return nil, fmt.Errorf("%s: duplicate slug %q", path, meta.Slug)
|
||||
}
|
||||
|
||||
body := parts[1]
|
||||
if ext == ".md" {
|
||||
body = md.ToHTML(body, nil, nil) // eigener Parser je Aufruf
|
||||
}
|
||||
|
||||
pages[meta.Slug] = StaticPage{
|
||||
Title: meta.Title,
|
||||
Slug: meta.Slug,
|
||||
Body: template.HTML(body),
|
||||
}
|
||||
}
|
||||
return pages, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user