1
0
mirror of https://github.com/danog/gojekyll.git synced 2024-11-26 19:24:45 +01:00

Report markdown error filename

This commit is contained in:
Oliver Steele 2017-08-22 10:47:34 -04:00
parent a4f352c505
commit 32b1d0e7c4
9 changed files with 133 additions and 47 deletions

View File

@ -1,6 +1,8 @@
package pages
import (
"bytes"
"fmt"
"io"
"testing"
@ -21,11 +23,15 @@ func (s siteFake) RendererManager() renderers.Renderers { return &renderManagerF
type renderManagerFake struct{ t *testing.T }
func (rm renderManagerFake) ApplyLayout(layout string, src []byte, vars liquid.Bindings) ([]byte, error) {
func (rm renderManagerFake) ApplyLayout(layout string, content []byte, vars liquid.Bindings) ([]byte, error) {
require.Equal(rm.t, "layout1", layout)
return nil, nil
return content, nil
}
func (rm renderManagerFake) Render(w io.Writer, src []byte, vars liquid.Bindings, filename string, lineNo int) error {
if bytes.Contains(src, []byte("{% error %}")) {
return fmt.Errorf("render error")
}
_, err := io.WriteString(w, "rendered: ")
if err != nil {
return err

View File

@ -12,6 +12,7 @@ import (
"time"
"github.com/osteele/gojekyll/frontmatter"
"github.com/osteele/gojekyll/utils"
"github.com/osteele/gojekyll/version"
"github.com/osteele/liquid/evaluator"
)
@ -169,24 +170,23 @@ func (f *file) PostDate() time.Time {
// Write applies Liquid and Markdown, as appropriate.
func (p *page) Write(w io.Writer) error {
err := p.Render()
if err != nil {
if err := p.Render(); err != nil {
return err
}
p.RLock()
defer p.RUnlock()
content := p.content
layout, ok := p.frontMatter["layout"].(string)
if ok && layout != "" {
rp := p.site.RendererManager()
b, e := rp.ApplyLayout(layout, []byte(content), p.TemplateContext())
if e != nil {
return e
cn := p.content
lo, ok := p.frontMatter["layout"].(string)
if ok && lo != "" {
rm := p.site.RendererManager()
b, err := rm.ApplyLayout(lo, []byte(cn), p.TemplateContext())
if err != nil {
return err
}
_, err = w.Write(b)
} else {
_, err = io.WriteString(w, content)
return err
}
_, err := io.WriteString(w, cn)
return err
}
@ -197,7 +197,7 @@ func (p *page) Render() error {
p.Lock()
defer p.Unlock()
p.content = cn
p.contentError = err
p.contentError = utils.WrapPathError(err, p.filename)
p.excerpt = ex
p.rendered = true
})

View File

@ -2,10 +2,14 @@ package pages
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/osteele/gojekyll/config"
"github.com/osteele/gojekyll/frontmatter"
"github.com/osteele/gojekyll/utils"
"github.com/stretchr/testify/require"
)
@ -27,17 +31,43 @@ func TestPage_TemplateContext(t *testing.T) {
func TestPage_Categories(t *testing.T) {
s := siteFake{t, config.Default()}
fm := map[string]interface{}{"categories": "b a"}
fm := frontmatter.FrontMatter{"categories": "b a"}
f := file{site: s, frontMatter: fm}
p := page{file: f}
require.Equal(t, []string{"a", "b"}, p.Categories())
}
func TestPage_Write(t *testing.T) {
cfg := config.Default()
p, err := NewFile(siteFake{t, cfg}, "testdata/page_with_layout.md", "page_with_layout.md", map[string]interface{}{})
t.Run("rendering", func(t *testing.T) {
p := requirePageFromFile(t, "page_with_layout.md")
buf := new(bytes.Buffer)
require.NoError(t, p.Write(buf))
require.Contains(t, buf.String(), "page with layout")
})
t.Run("rendering error", func(t *testing.T) {
p := requirePageFromFile(t, "liquid_error.md")
err := p.Write(ioutil.Discard)
require.NotNil(t, err)
require.Contains(t, err.Error(), "render error")
pe, ok := err.(utils.PathError)
require.True(t, ok)
require.Equal(t, "testdata/liquid_error.md", pe.Path())
})
}
func fakePageFromFile(t *testing.T, file string) (Document, error) {
return NewFile(
siteFake{t, config.Default()},
filepath.Join("testdata", file),
file,
frontmatter.FrontMatter{},
)
}
func requirePageFromFile(t *testing.T, file string) Document {
p, err := fakePageFromFile(t, file)
require.NoError(t, err)
require.NotNil(t, p)
buf := new(bytes.Buffer)
require.NoError(t, p.Write(buf))
return p
}

4
pages/testdata/liquid_error.md vendored Normal file
View File

@ -0,0 +1,4 @@
---
---
{% error %}

View File

@ -13,8 +13,8 @@ import (
"github.com/osteele/liquid"
)
// ApplyLayout applies the named layout to the data.
func (p *Manager) ApplyLayout(name string, data []byte, vars liquid.Bindings) ([]byte, error) {
// ApplyLayout applies the named layout to the content.
func (p *Manager) ApplyLayout(name string, content []byte, vars liquid.Bindings) ([]byte, error) {
for name != "" {
var lfm map[string]interface{}
tpl, err := p.FindLayout(name, &lfm)
@ -22,16 +22,16 @@ func (p *Manager) ApplyLayout(name string, data []byte, vars liquid.Bindings) ([
return nil, err
}
b := utils.MergeStringMaps(vars, map[string]interface{}{
"content": string(data),
"content": string(content),
"layout": lfm,
})
data, err = tpl.Render(b)
content, err = tpl.Render(b)
if err != nil {
return nil, utils.WrapPathError(err, name)
}
name = templates.VariableMap(lfm).String("layout", "")
}
return data, nil
return content, nil
}
// FindLayout returns a template for the named layout.

View File

@ -2,6 +2,7 @@ package renderers
import (
"bytes"
"fmt"
"io"
"regexp"
@ -29,17 +30,31 @@ const blackfridayExtensions = 0 |
// added relative to commonExtensions
blackfriday.EXTENSION_AUTO_HEADER_IDS
func renderMarkdown(md []byte) []byte {
func renderMarkdown(md []byte) ([]byte, error) {
renderer := blackfriday.HtmlRenderer(blackfridayFlags, "", "")
html := blackfriday.MarkdownOptions(md, renderer, blackfriday.Options{
Extensions: blackfridayExtensions})
html := blackfriday.MarkdownOptions(
md,
renderer,
blackfriday.Options{Extensions: blackfridayExtensions},
)
html, err := renderInnerMarkdown(html)
if err != nil {
panic(err)
return nil, fmt.Errorf("%s while rendering markdown", err)
}
return html
return html, nil
}
func _renderMarkdown(md []byte) ([]byte, error) {
renderer := blackfriday.HtmlRenderer(blackfridayFlags, "", "")
html := blackfriday.MarkdownOptions(
md,
renderer,
blackfriday.Options{Extensions: blackfridayExtensions},
)
return html, nil
}
// search HTML for markdown=1, and process if found
func renderInnerMarkdown(b []byte) ([]byte, error) {
z := html.NewTokenizer(bytes.NewReader(b))
buf := new(bytes.Buffer)
@ -54,7 +69,7 @@ outer:
return nil, z.Err()
case html.StartTagToken:
if hasMarkdownAttr(z) {
_, err := buf.Write(removeMarkdownAttr(z.Raw()))
_, err := buf.Write(stripMarkdownAttr(z.Raw()))
if err != nil {
return nil, err
}
@ -62,6 +77,7 @@ outer:
return nil, err
}
// the above leaves z set to the end token
// fall through to render it
}
}
_, err := buf.Write(z.Raw())
@ -72,8 +88,6 @@ outer:
return buf.Bytes(), nil
}
var markdownAttrRE = regexp.MustCompile(`\s*markdown\s*=\s*("1"|'1'|1)\s*`)
func hasMarkdownAttr(z *html.Tokenizer) bool {
for {
k, v, more := z.TagAttr()
@ -86,12 +100,18 @@ func hasMarkdownAttr(z *html.Tokenizer) bool {
}
}
func removeMarkdownAttr(tag []byte) []byte {
var markdownAttrRE = regexp.MustCompile(`\s*markdown\s*=[^\s>]*\s*`)
// return the text of a start tag, w/out the markdown attribute
func stripMarkdownAttr(tag []byte) []byte {
tag = markdownAttrRE.ReplaceAll(tag, []byte(" "))
tag = bytes.Replace(tag, []byte(" >"), []byte(">"), 1)
return tag
}
// called once markdown="1" attribute is detected.
// Collects the HTML tokens into a string, applies markdown to them,
// and writes the result
func processInnerMarkdown(w io.Writer, z *html.Tokenizer) error {
buf := new(bytes.Buffer)
depth := 1
@ -111,10 +131,13 @@ loop:
}
_, err := buf.Write(z.Raw())
if err != nil {
panic(err)
return err
}
}
html := renderMarkdown(buf.Bytes())
_, err := w.Write(html)
html, err := _renderMarkdown(buf.Bytes())
if err != nil {
return err
}
_, err = w.Write(html)
return err
}

View File

@ -1,19 +1,35 @@
package renderers
import (
"log"
"testing"
"github.com/stretchr/testify/require"
)
func renderMarkdownString(s string) string {
return string(renderMarkdown([]byte(s)))
func TestRenderMarkdown(t *testing.T) {
require.Equal(t, "<p><em>b</em></p>\n", mustMarkdownString("*b*"))
require.Equal(t, "<div>*b*</div>\n", mustMarkdownString("<div>*b*</div>"))
require.Equal(t, "<div a=1><p><em>b</em></p>\n</div>\n", mustMarkdownString(`<div a=1 markdown="1">*b*</div>`))
require.Equal(t, "<div a=1><p><em>b</em></p>\n</div>\n", mustMarkdownString(`<div a=1 markdown='1'>*b*</div>`))
require.Equal(t, "<div a=1><p><em>b</em></p>\n</div>\n", mustMarkdownString(`<div a=1 markdown=1>*b*</div>`))
_, err := renderMarkdownString(`<div a=1 markdown=1><p></div>`)
require.NotNil(t, err)
}
func TestRenderMarkdown(t *testing.T) {
require.Equal(t, "<p><em>b</em></p>\n", renderMarkdownString("*b*"))
require.Equal(t, "<div>*b*</div>\n", renderMarkdownString("<div>*b*</div>"))
require.Equal(t, "<div a=1><p><em>b</em></p>\n</div>\n", renderMarkdownString(`<div a=1 markdown="1">*b*</div>`))
require.Equal(t, "<div a=1><p><em>b</em></p>\n</div>\n", renderMarkdownString(`<div a=1 markdown='1'>*b*</div>`))
require.Equal(t, "<div a=1><p><em>b</em></p>\n</div>\n", renderMarkdownString(`<div a=1 markdown=1>*b*</div>`))
func mustMarkdownString(md string) string {
s, err := renderMarkdown([]byte(md))
if err != nil {
log.Fatal(err)
}
return string(s)
}
func renderMarkdownString(md string) (string, error) {
s, err := renderMarkdown([]byte(md))
if err != nil {
return "", err
}
return string(s), err
}

View File

@ -64,7 +64,10 @@ func (p *Manager) Render(w io.Writer, src []byte, vars liquid.Bindings, filename
return err
}
if p.cfg.IsMarkdown(filename) {
src = renderMarkdown(src)
src, err = renderMarkdown(src)
if err != nil {
return err
}
}
_, err = w.Write(src)
return err

View File

@ -19,8 +19,12 @@ type pathError struct {
path string
}
func (p *pathError) Error() string {
return fmt.Sprintf("%s: %s", p.path, p.cause)
func (pe *pathError) Error() string {
return fmt.Sprintf("%s: %s", pe.path, pe.cause)
}
func (pe *pathError) Path() string {
return pe.path
}
// WrapPathError returns an error that will print with a path.\