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:
parent
a4f352c505
commit
32b1d0e7c4
@ -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
|
||||
|
@ -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
|
||||
})
|
||||
|
@ -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
4
pages/testdata/liquid_error.md
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
---
|
||||
|
||||
{% error %}
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.\
|
||||
|
Loading…
Reference in New Issue
Block a user