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 |
package doc
import (
"strings"
"gopkg.in/yaml.v3"
)
// splitFrontmatter separates a leading YAML block delimited by "---" lines,
// the convention gray-matter, Hugo, Eleventy and Astro share. A file whose
// first line is "---" but never closes it has no frontmatter.
func splitFrontmatter(text string) (yamlSrc, body string, ok bool) {
first, rest, found := strings.Cut(text, "\n")
if !found || strings.TrimRight(first, " \t") != "---" {
return "", text, false
}
offset := 0
for {
line, after, more := strings.Cut(rest[offset:], "\n")
if t := strings.TrimRight(line, " \t"); t == "---" || t == "..." {
return rest[:offset], after, true
}
if !more {
return "", text, false
}
offset += len(line) + 1
}
}
func parseFrontmatter(src string) (map[string]any, error) {
fm := map[string]any{}
if strings.TrimSpace(src) == "" {
return fm, nil
}
if err := yaml.Unmarshal([]byte(src), &fm); err != nil {
return map[string]any{}, err
}
if fm == nil {
fm = map[string]any{}
}
return fm, nil
}
// stringField reads a scalar frontmatter value as trimmed text. YAML turns
// `revision: 2` into an int and `maturity: yes` into a bool; the author still
// meant the words.
func stringField(fm map[string]any, key string) string {
v, ok := fm[key]
if !ok || v == nil {
return ""
}
switch t := v.(type) {
case string:
return strings.TrimSpace(t)
case []any, map[string]any:
return ""
}
b, err := yaml.Marshal(v)
if err != nil {
return ""
}
return strings.TrimSpace(string(b))
}
|