119 lines
3.2 KiB
Go
119 lines
3.2 KiB
Go
// parse.go
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"git.else-if.org/jess/blog/templates"
|
|
"github.com/yuin/goldmark"
|
|
"github.com/yuin/goldmark/extension"
|
|
"github.com/yuin/goldmark/parser"
|
|
"github.com/yuin/goldmark/renderer/html"
|
|
"go.abhg.dev/goldmark/frontmatter"
|
|
)
|
|
|
|
// GetContent is the primary entry point for retrieving parsed content.
|
|
func GetContent(file ContentFile) ([]byte, error) {
|
|
// 1. Ask Cache
|
|
if data, found := CheckCache(file.OriginalPath); found {
|
|
return data, nil
|
|
}
|
|
|
|
// 2. Read Raw
|
|
raw, err := ReadRaw(file.OriginalPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read file: %w", err)
|
|
}
|
|
|
|
// If not markdown (e.g. css, images), return raw bytes immediately
|
|
if !file.IsMarkdown {
|
|
_ = StoreCache(file.OriginalPath, raw)
|
|
return raw, nil
|
|
}
|
|
|
|
// 3. Configure Goldmark
|
|
// We use the Extender pattern which is the standard way to add frontmatter support.
|
|
// We also enable Unsafe mode to allow raw HTML tags (like <site-headline>) to pass through
|
|
// so we can replace them later.
|
|
md := goldmark.New(
|
|
goldmark.WithExtensions(
|
|
extension.GFM,
|
|
&frontmatter.Extender{},
|
|
),
|
|
goldmark.WithRendererOptions(
|
|
html.WithUnsafe(),
|
|
),
|
|
)
|
|
|
|
// 4. Parse Markdown
|
|
var buf bytes.Buffer
|
|
ctx := parser.NewContext()
|
|
|
|
if err := md.Convert(raw, &buf, parser.WithContext(ctx)); err != nil {
|
|
return nil, fmt.Errorf("failed to parse markdown: %w", err)
|
|
}
|
|
|
|
// 5. Extract Frontmatter
|
|
// We retrieve the metadata using the context after parsing
|
|
var meta templates.PageMetadata
|
|
d := frontmatter.Get(ctx)
|
|
if d != nil {
|
|
if err := d.Decode(&meta); err != nil {
|
|
// If decoding fails, we just proceed without metadata
|
|
fmt.Printf("Warning: failed to decode frontmatter for %s: %v\n", file.OriginalPath, err)
|
|
}
|
|
}
|
|
|
|
// If title is missing, try to grab the filename
|
|
if meta.Title == "" {
|
|
base := filepath.Base(file.OriginalPath)
|
|
meta.Title = strings.TrimSuffix(base, filepath.Ext(base))
|
|
}
|
|
|
|
// 6. Special Handling for Index (Dynamic Components)
|
|
htmlContent := buf.String()
|
|
|
|
if file.RoutePath == "/" {
|
|
// Generate the list of posts
|
|
var posts []templates.PostSnippet
|
|
for _, f := range AllContent {
|
|
if f.IsMarkdown && f.RoutePath != "/" {
|
|
name := filepath.Base(f.OriginalPath)
|
|
title := strings.TrimSuffix(name, filepath.Ext(name))
|
|
|
|
posts = append(posts, templates.PostSnippet{
|
|
Title: title,
|
|
URL: f.RoutePath,
|
|
Date: f.ModTime,
|
|
})
|
|
}
|
|
}
|
|
|
|
latestPostsHTML := templates.RenderLatestPosts(posts)
|
|
dirLink := templates.RenderDirectoryLink()
|
|
|
|
// Replace custom tags if they exist in the markdown
|
|
if strings.Contains(htmlContent, "<latest-posts>") {
|
|
htmlContent = strings.ReplaceAll(htmlContent, "<latest-posts>", latestPostsHTML)
|
|
} else {
|
|
// Fallback: Append if not present
|
|
htmlContent = htmlContent + "\n" + dirLink + "\n" + latestPostsHTML + "\n" + dirLink
|
|
}
|
|
|
|
// Handle site-headline if present
|
|
// You can add <site-headline>Your Text</site-headline> in your markdown
|
|
// For now, we just let it pass through as HTML, or you can add specific replacement logic here.
|
|
}
|
|
|
|
// 7. Build Full Page (HTML Shell)
|
|
finalPage := templates.BuildFullPage([]byte(htmlContent), meta)
|
|
|
|
// 8. Cache
|
|
_ = StoreCache(file.OriginalPath, finalPage)
|
|
|
|
return finalPage, nil
|
|
}
|