mirror of
https://github.com/caddyserver/caddy.git
synced 2024-11-30 12:43:55 +08:00
a2dae1d43f
This makes it choose first matching closing fence instead of last one, which could appear in document body.
128 lines
2.9 KiB
Go
128 lines
2.9 KiB
Go
package templates
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"github.com/naoina/toml"
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
func extractFrontMatter(input string) (map[string]interface{}, string, error) {
|
|
// get the bounds of the first non-empty line
|
|
var firstLineStart, firstLineEnd int
|
|
lineEmpty := true
|
|
for i, b := range input {
|
|
if b == '\n' {
|
|
firstLineStart = firstLineEnd
|
|
if firstLineStart > 0 {
|
|
firstLineStart++ // skip newline character
|
|
}
|
|
firstLineEnd = i
|
|
if !lineEmpty {
|
|
break
|
|
}
|
|
continue
|
|
}
|
|
lineEmpty = lineEmpty && unicode.IsSpace(b)
|
|
}
|
|
firstLine := input[firstLineStart:firstLineEnd]
|
|
|
|
// ensure residue windows carriage return byte is removed
|
|
firstLine = strings.TrimSpace(firstLine)
|
|
|
|
// see what kind of front matter there is, if any
|
|
var closingFence []string
|
|
var fmParser func([]byte) (map[string]interface{}, error)
|
|
for _, fmType := range supportedFrontMatterTypes {
|
|
if firstLine == fmType.FenceOpen {
|
|
closingFence = fmType.FenceClose
|
|
fmParser = fmType.ParseFunc
|
|
}
|
|
}
|
|
|
|
if fmParser == nil {
|
|
// no recognized front matter; whole document is body
|
|
return nil, input, nil
|
|
}
|
|
|
|
// find end of front matter
|
|
var fmEndFence string
|
|
fmEndFenceStart := -1
|
|
for _, fence := range closingFence {
|
|
index := strings.Index(input[firstLineEnd:], "\n"+fence)
|
|
if index >= 0 {
|
|
fmEndFenceStart = index
|
|
fmEndFence = fence
|
|
break
|
|
}
|
|
}
|
|
if fmEndFenceStart < 0 {
|
|
return nil, "", fmt.Errorf("unterminated front matter")
|
|
}
|
|
fmEndFenceStart += firstLineEnd + 1 // add 1 to account for newline
|
|
|
|
// extract and parse front matter
|
|
frontMatter := input[firstLineEnd:fmEndFenceStart]
|
|
fm, err := fmParser([]byte(frontMatter))
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
// the rest is the body
|
|
body := input[fmEndFenceStart+len(fmEndFence):]
|
|
|
|
return fm, body, nil
|
|
}
|
|
|
|
func yamlFrontMatter(input []byte) (map[string]interface{}, error) {
|
|
m := make(map[string]interface{})
|
|
err := yaml.Unmarshal(input, &m)
|
|
return m, err
|
|
}
|
|
|
|
func tomlFrontMatter(input []byte) (map[string]interface{}, error) {
|
|
m := make(map[string]interface{})
|
|
err := toml.Unmarshal(input, &m)
|
|
return m, err
|
|
}
|
|
|
|
func jsonFrontMatter(input []byte) (map[string]interface{}, error) {
|
|
input = append([]byte{'{'}, input...)
|
|
input = append(input, '}')
|
|
m := make(map[string]interface{})
|
|
err := json.Unmarshal(input, &m)
|
|
return m, err
|
|
}
|
|
|
|
type parsedMarkdownDoc struct {
|
|
Meta map[string]interface{} `json:"meta,omitempty"`
|
|
Body string `json:"body,omitempty"`
|
|
}
|
|
|
|
type frontMatterType struct {
|
|
FenceOpen string
|
|
FenceClose []string
|
|
ParseFunc func(input []byte) (map[string]interface{}, error)
|
|
}
|
|
|
|
var supportedFrontMatterTypes = []frontMatterType{
|
|
{
|
|
FenceOpen: "---",
|
|
FenceClose: []string{"---", "..."},
|
|
ParseFunc: yamlFrontMatter,
|
|
},
|
|
{
|
|
FenceOpen: "+++",
|
|
FenceClose: []string{"+++"},
|
|
ParseFunc: tomlFrontMatter,
|
|
},
|
|
{
|
|
FenceOpen: "{",
|
|
FenceClose: []string{"}"},
|
|
ParseFunc: jsonFrontMatter,
|
|
},
|
|
}
|