mirror of
https://github.com/gohugoio/hugo.git
synced 2025-04-26 13:40:38 +03:00
Fix parsing edge case of frontmatter
When the frontmatter contains a - (or other delimiter) close to the closing frontmatter delimiter, frontmatter detection would fail.
This commit is contained in:
parent
a82efe5bb1
commit
d8e1834910
8 changed files with 100 additions and 68 deletions
|
@ -2,9 +2,9 @@ package parser
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"unicode"
|
||||
)
|
||||
|
@ -164,22 +164,34 @@ func determineDelims(firstLine []byte) (left, right []byte) {
|
|||
}
|
||||
|
||||
func extractFrontMatterDelims(r *bufio.Reader, left, right []byte) (fm FrontMatter, err error) {
|
||||
var level int = 0
|
||||
var sameDelim = bytes.Equal(left, right)
|
||||
var (
|
||||
c byte
|
||||
level int = 0
|
||||
bytesRead int = 0
|
||||
sameDelim = bytes.Equal(left, right)
|
||||
)
|
||||
|
||||
wr := new(bytes.Buffer)
|
||||
for {
|
||||
c, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if c, err = r.ReadByte(); err != nil {
|
||||
return nil, fmt.Errorf("Unable to read frontmatter at filepos %d: %s", bytesRead, err)
|
||||
}
|
||||
bytesRead += 1
|
||||
|
||||
switch c {
|
||||
case left[0]:
|
||||
match, err := matches(r, wr, []byte{c}, left)
|
||||
if err != nil {
|
||||
var (
|
||||
buf []byte = []byte{c}
|
||||
remaining []byte
|
||||
)
|
||||
|
||||
if remaining, err = r.Peek(len(left) - 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if match {
|
||||
|
||||
buf = append(buf, remaining...)
|
||||
|
||||
if bytes.Equal(buf, left) {
|
||||
if sameDelim {
|
||||
if level == 0 {
|
||||
level = 1
|
||||
|
@ -190,6 +202,19 @@ func extractFrontMatterDelims(r *bufio.Reader, left, right []byte) (fm FrontMatt
|
|||
level += 1
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = wr.Write([]byte{c}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if level == 0 {
|
||||
if _, err = r.Read(remaining); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = wr.Write(remaining); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case right[0]:
|
||||
match, err := matches(r, wr, []byte{c}, right)
|
||||
if err != nil {
|
||||
|
@ -216,6 +241,10 @@ func extractFrontMatterDelims(r *bufio.Reader, left, right []byte) (fm FrontMatt
|
|||
return nil, errors.New("Could not find front matter.")
|
||||
}
|
||||
|
||||
func matches_quick(buf, expected []byte) (ok bool, err error) {
|
||||
return bytes.Equal(expected, buf), nil
|
||||
}
|
||||
|
||||
func matches(r *bufio.Reader, wr io.Writer, c, expected []byte) (ok bool, err error) {
|
||||
if len(expected) == 1 {
|
||||
if _, err = wr.Write(c); err != nil {
|
||||
|
@ -223,16 +252,13 @@ func matches(r *bufio.Reader, wr io.Writer, c, expected []byte) (ok bool, err er
|
|||
}
|
||||
return bytes.Equal(c, expected), nil
|
||||
}
|
||||
|
||||
buf := make([]byte, len(expected)-1)
|
||||
if _, err = r.Read(buf); err != nil {
|
||||
if buf, err = r.Peek(len(expected) - 1); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf = append(c, buf...)
|
||||
if _, err = wr.Write(buf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return bytes.Equal(expected, buf), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -24,12 +24,15 @@ var (
|
|||
CONTENT_INCOMPLETE_BEG_FM_DELIM = "--\ntitle: incomplete beg fm delim\n---\nincomplete frontmatter delim"
|
||||
CONTENT_INCOMPLETE_END_FM_DELIM = "---\ntitle: incomplete end fm delim\n--\nincomplete frontmatter delim"
|
||||
CONTENT_MISSING_END_FM_DELIM = "---\ntitle: incomplete end fm delim\nincomplete frontmatter delim"
|
||||
CONTENT_SLUG_WORKING = "---\ntitle: slug doc 2\nslug: slug-doc-2\n\n---\nslug doc 2 content"
|
||||
CONTENT_SLUG_WORKING_VARIATION = "---\ntitle: slug doc 3\nslug: slug-doc 3\n---\nslug doc 3 content"
|
||||
CONTENT_SLUG_BUG = "---\ntitle: slug doc 2\nslug: slug-doc-2\n---\nslug doc 2 content"
|
||||
CONTENT_FM_NO_DOC = "---\ntitle: no doc\n---"
|
||||
CONTENT_WITH_JS_FM = "{\n \"categories\": \"d\",\n \"tags\": [\n \"a\", \n \"b\", \n \"c\"\n ]\n}\nJSON Front Matter with tags and categories"
|
||||
CONTENT_WITH_JS_FM = "{\n \"categories\": \"d\",\n \"tags\": [\n \"a\", \n \"b\", \n \"c\"\n ]\n}\nJSON Front Matter with tags and categories"
|
||||
)
|
||||
|
||||
var lineEndings = []string{"\n", "\r\n"}
|
||||
var delimiters = []string{"-", "+"}
|
||||
var delimiters = []string{"---", "+++"}
|
||||
|
||||
func pageMust(p Page, err error) *page {
|
||||
if err != nil {
|
||||
|
@ -83,13 +86,13 @@ func checkPageFrontMatterContent(t *testing.T, p *page, frontMatter string) {
|
|||
return
|
||||
}
|
||||
if !bytes.Equal(p.frontmatter, []byte(frontMatter)) {
|
||||
t.Errorf("expected frontmatter %q, got %q", frontMatter, p.frontmatter)
|
||||
t.Errorf("frontmatter mismatch\nexp: %q\ngot: %q", frontMatter, p.frontmatter)
|
||||
}
|
||||
}
|
||||
|
||||
func checkPageContent(t *testing.T, p *page, expected string) {
|
||||
if !bytes.Equal(p.content, []byte(expected)) {
|
||||
t.Errorf("expected content %q, got %q", expected, p.content)
|
||||
t.Errorf("content mismatch\nexp: %q\ngot: %q", expected, p.content)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,6 +104,7 @@ func TestStandaloneCreatePageFrom(t *testing.T) {
|
|||
frontMatter string
|
||||
bodycontent string
|
||||
}{
|
||||
|
||||
{CONTENT_NO_FRONTMATTER, true, true, "", "a page with no front matter"},
|
||||
{CONTENT_WITH_FRONTMATTER, true, false, "---\ntitle: front matter\n---\n", "Content with front matter"},
|
||||
{CONTENT_HTML_NODOCTYPE, false, true, "", "<html>\n\t<body>\n\t</body>\n</html>"},
|
||||
|
@ -109,6 +113,9 @@ func TestStandaloneCreatePageFrom(t *testing.T) {
|
|||
{CONTENT_LWS_HTML, false, true, "", "<html><body></body></html>"},
|
||||
{CONTENT_LWS_LF_HTML, false, true, "", "<html><body></body></html>"},
|
||||
{CONTENT_WITH_JS_FM, true, false, "{\n \"categories\": \"d\",\n \"tags\": [\n \"a\", \n \"b\", \n \"c\"\n ]\n}", "JSON Front Matter with tags and categories"},
|
||||
{CONTENT_SLUG_WORKING, true, false, "---\ntitle: slug doc 2\nslug: slug-doc-2\n\n---\n", "slug doc 2 content"},
|
||||
{CONTENT_SLUG_WORKING_VARIATION, true, false, "---\ntitle: slug doc 3\nslug: slug-doc 3\n---\n", "slug doc 3 content"},
|
||||
{CONTENT_SLUG_BUG, true, false, "---\ntitle: slug doc 2\nslug: slug-doc-2\n---\n", "slug doc 2 content"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
@ -224,6 +231,7 @@ func TestExtractFrontMatter(t *testing.T) {
|
|||
{"---\nralb\n---\n", []byte("---\nralb\n---\n"), true},
|
||||
{"---\nminc\n---\ncontent", []byte("---\nminc\n---\n"), true},
|
||||
{"---\ncnim\n---\ncontent\n", []byte("---\ncnim\n---\n"), true},
|
||||
{"---\ntitle: slug doc 2\nslug: slug-doc-2\n---\ncontent\n", []byte("---\ntitle: slug doc 2\nslug: slug-doc-2\n---\n"), true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
@ -231,8 +239,8 @@ func TestExtractFrontMatter(t *testing.T) {
|
|||
test.frontmatter = strings.Replace(test.frontmatter, "\n", ending, -1)
|
||||
test.extracted = bytes.Replace(test.extracted, []byte("\n"), []byte(ending), -1)
|
||||
for _, delim := range delimiters {
|
||||
test.frontmatter = strings.Replace(test.frontmatter, "-", delim, -1)
|
||||
test.extracted = bytes.Replace(test.extracted, []byte("-"), []byte(delim), -1)
|
||||
test.frontmatter = strings.Replace(test.frontmatter, "---", delim, -1)
|
||||
test.extracted = bytes.Replace(test.extracted, []byte("---"), []byte(delim), -1)
|
||||
line, err := peekLine(bufio.NewReader(strings.NewReader(test.frontmatter)))
|
||||
if err != nil {
|
||||
continue
|
||||
|
@ -245,8 +253,7 @@ func TestExtractFrontMatter(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
if !bytes.Equal(fm, test.extracted) {
|
||||
t.Logf("\n%q\n", string(test.frontmatter))
|
||||
t.Errorf("Expected front matter %q. got %q", string(test.extracted), fm)
|
||||
t.Errorf("Frontmatter did not match:\nexp: %q\ngot: %q", string(test.extracted), fm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -285,8 +292,7 @@ func TestExtractFrontMatterDelim(t *testing.T) {
|
|||
}
|
||||
if !bytes.Equal(fm, []byte(test.extracted)) {
|
||||
t.Logf("\n%q\n", string(test.frontmatter))
|
||||
t.Errorf("Expected front matter %q. got %q", string(test.extracted), fm)
|
||||
t.Errorf("Frontmatter did not match:\nexp: %q\ngot: %q", string(test.extracted), fm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue