-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrender.go
132 lines (106 loc) · 3.12 KB
/
render.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package main
import (
"bytes"
"fmt"
"html/template"
"io/fs"
"os"
"path/filepath"
chromaHTML "github.com/alecthomas/chroma/v2/formatters/html"
gmd "github.com/yuin/goldmark"
ghl "github.com/yuin/goldmark-highlighting/v2"
gme "github.com/yuin/goldmark/extension"
gmp "github.com/yuin/goldmark/parser"
gmh "github.com/yuin/goldmark/renderer/html"
fm "github.com/adrg/frontmatter"
)
func walker(destination *[]string) fs.WalkDirFunc {
return func(path string, d fs.DirEntry, err error) error {
if err != nil {
return fmt.Errorf("failed to read path %q: %w", path, err.(*fs.PathError).Err)
}
if d.IsDir() {
return nil
}
*destination = append(*destination, path)
return nil
}
}
func initRenderer(syntaxTheme string, lineNumbers bool) gmd.Markdown {
exts := gmd.WithExtensions(
gme.Table,
gme.Strikethrough,
gme.Footnote,
ghl.NewHighlighting(
ghl.WithStyle(syntaxTheme),
ghl.WithFormatOptions(
chromaHTML.WithLineNumbers(lineNumbers),
),
),
)
pOpts := gmd.WithParserOptions(gmp.WithAutoHeadingID())
rOpts := gmd.WithRendererOptions(gmh.WithUnsafe())
return gmd.New(exts, pOpts, rOpts)
}
func renderPage(inputFile, outputFile string) error {
// copy non markdown files as-is
if filepath.Ext(inputFile) != ".md" {
return copyFile(inputFile, outputFile)
}
metadata := make(map[string]any)
file, err := os.Open(inputFile)
if err != nil {
return fmt.Errorf("failed to open file %q: %w", inputFile, err.(*fs.PathError).Err)
}
body, err := fm.Parse(file, &metadata)
if err != nil {
return fmt.Errorf("failed to parse front matter from source file %q: %w", inputFile, err)
}
var syntaxTheme string
var lineNumbers bool
if theme, ok := metadata["__syntax_theme"]; ok {
syntaxTheme = theme.(string)
} else {
syntaxTheme = "github-dark"
}
if numbers, ok := metadata["__line_numbers"]; ok {
lineNumbers = numbers.(bool)
} else {
lineNumbers = false
}
renderer := initRenderer(syntaxTheme, lineNumbers)
var renderedBytes bytes.Buffer
renderer.Convert([]byte(body), &renderedBytes)
metadata["__body"] = template.HTML(renderedBytes.String())
outputBuf := bytes.Buffer{}
var templ string
if templateFile, ok := metadata["__template"]; ok {
templ = templateFile.(string)
} else {
templ = "__built_in"
}
if templates == nil {
panic("templates are nil")
}
if err = templates.ExecuteTemplate(&outputBuf, templ, metadata); err != nil {
return fmt.Errorf("failed to execute template: %w", err)
}
if err = os.WriteFile(outputFile, outputBuf.Bytes(), fs.ModePerm); err != nil {
return fmt.Errorf("failed to write output file %q: %w", outputFile, err.(*fs.PathError).Err)
}
return nil
}
func copyFile(inputFile, outputFile string) error {
fileData, err := os.ReadFile(inputFile)
if err != nil {
return fmt.Errorf("failed to read source file %q: %w", inputFile, err.(*fs.PathError).Err)
}
err = os.WriteFile(outputFile, fileData, fs.ModePerm)
if err != nil {
return fmt.Errorf("failed to write destination file %q: %w", outputFile, err.(*fs.PathError).Err)
}
return nil
}
func copyAsset(inputFile, outputFile string) error {
return copyFile(inputFile, outputFile)
}