mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-26 08:12:43 -04:00 
			
		
		
		
	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,
 | |
| 	},
 | |
| }
 |