mirror of
https://github.com/danog/gojekyll.git
synced 2024-11-26 21:14:48 +01:00
Report markdown error filename
This commit is contained in:
parent
a4f352c505
commit
32b1d0e7c4
@ -1,6 +1,8 @@
|
|||||||
package pages
|
package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -21,11 +23,15 @@ func (s siteFake) RendererManager() renderers.Renderers { return &renderManagerF
|
|||||||
|
|
||||||
type renderManagerFake struct{ t *testing.T }
|
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)
|
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 {
|
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: ")
|
_, err := io.WriteString(w, "rendered: ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/osteele/gojekyll/frontmatter"
|
"github.com/osteele/gojekyll/frontmatter"
|
||||||
|
"github.com/osteele/gojekyll/utils"
|
||||||
"github.com/osteele/gojekyll/version"
|
"github.com/osteele/gojekyll/version"
|
||||||
"github.com/osteele/liquid/evaluator"
|
"github.com/osteele/liquid/evaluator"
|
||||||
)
|
)
|
||||||
@ -169,24 +170,23 @@ func (f *file) PostDate() time.Time {
|
|||||||
|
|
||||||
// Write applies Liquid and Markdown, as appropriate.
|
// Write applies Liquid and Markdown, as appropriate.
|
||||||
func (p *page) Write(w io.Writer) error {
|
func (p *page) Write(w io.Writer) error {
|
||||||
err := p.Render()
|
if err := p.Render(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.RLock()
|
p.RLock()
|
||||||
defer p.RUnlock()
|
defer p.RUnlock()
|
||||||
content := p.content
|
cn := p.content
|
||||||
layout, ok := p.frontMatter["layout"].(string)
|
lo, ok := p.frontMatter["layout"].(string)
|
||||||
if ok && layout != "" {
|
if ok && lo != "" {
|
||||||
rp := p.site.RendererManager()
|
rm := p.site.RendererManager()
|
||||||
b, e := rp.ApplyLayout(layout, []byte(content), p.TemplateContext())
|
b, err := rm.ApplyLayout(lo, []byte(cn), p.TemplateContext())
|
||||||
if e != nil {
|
if err != nil {
|
||||||
return e
|
return err
|
||||||
}
|
}
|
||||||
_, err = w.Write(b)
|
_, err = w.Write(b)
|
||||||
} else {
|
return err
|
||||||
_, err = io.WriteString(w, content)
|
|
||||||
}
|
}
|
||||||
|
_, err := io.WriteString(w, cn)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ func (p *page) Render() error {
|
|||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
p.content = cn
|
p.content = cn
|
||||||
p.contentError = err
|
p.contentError = utils.WrapPathError(err, p.filename)
|
||||||
p.excerpt = ex
|
p.excerpt = ex
|
||||||
p.rendered = true
|
p.rendered = true
|
||||||
})
|
})
|
||||||
|
@ -2,10 +2,14 @@ package pages
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/osteele/gojekyll/config"
|
"github.com/osteele/gojekyll/config"
|
||||||
|
"github.com/osteele/gojekyll/frontmatter"
|
||||||
|
"github.com/osteele/gojekyll/utils"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,17 +31,43 @@ func TestPage_TemplateContext(t *testing.T) {
|
|||||||
|
|
||||||
func TestPage_Categories(t *testing.T) {
|
func TestPage_Categories(t *testing.T) {
|
||||||
s := siteFake{t, config.Default()}
|
s := siteFake{t, config.Default()}
|
||||||
fm := map[string]interface{}{"categories": "b a"}
|
fm := frontmatter.FrontMatter{"categories": "b a"}
|
||||||
f := file{site: s, frontMatter: fm}
|
f := file{site: s, frontMatter: fm}
|
||||||
p := page{file: f}
|
p := page{file: f}
|
||||||
require.Equal(t, []string{"a", "b"}, p.Categories())
|
require.Equal(t, []string{"a", "b"}, p.Categories())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPage_Write(t *testing.T) {
|
func TestPage_Write(t *testing.T) {
|
||||||
cfg := config.Default()
|
t.Run("rendering", func(t *testing.T) {
|
||||||
p, err := NewFile(siteFake{t, cfg}, "testdata/page_with_layout.md", "page_with_layout.md", map[string]interface{}{})
|
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.NoError(t, err)
|
||||||
require.NotNil(t, p)
|
require.NotNil(t, p)
|
||||||
buf := new(bytes.Buffer)
|
return p
|
||||||
require.NoError(t, p.Write(buf))
|
|
||||||
}
|
}
|
||||||
|
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"
|
"github.com/osteele/liquid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ApplyLayout applies the named layout to the data.
|
// ApplyLayout applies the named layout to the content.
|
||||||
func (p *Manager) ApplyLayout(name string, data []byte, vars liquid.Bindings) ([]byte, error) {
|
func (p *Manager) ApplyLayout(name string, content []byte, vars liquid.Bindings) ([]byte, error) {
|
||||||
for name != "" {
|
for name != "" {
|
||||||
var lfm map[string]interface{}
|
var lfm map[string]interface{}
|
||||||
tpl, err := p.FindLayout(name, &lfm)
|
tpl, err := p.FindLayout(name, &lfm)
|
||||||
@ -22,16 +22,16 @@ func (p *Manager) ApplyLayout(name string, data []byte, vars liquid.Bindings) ([
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
b := utils.MergeStringMaps(vars, map[string]interface{}{
|
b := utils.MergeStringMaps(vars, map[string]interface{}{
|
||||||
"content": string(data),
|
"content": string(content),
|
||||||
"layout": lfm,
|
"layout": lfm,
|
||||||
})
|
})
|
||||||
data, err = tpl.Render(b)
|
content, err = tpl.Render(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, utils.WrapPathError(err, name)
|
return nil, utils.WrapPathError(err, name)
|
||||||
}
|
}
|
||||||
name = templates.VariableMap(lfm).String("layout", "")
|
name = templates.VariableMap(lfm).String("layout", "")
|
||||||
}
|
}
|
||||||
return data, nil
|
return content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindLayout returns a template for the named layout.
|
// FindLayout returns a template for the named layout.
|
||||||
|
@ -2,6 +2,7 @@ package renderers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
@ -29,17 +30,31 @@ const blackfridayExtensions = 0 |
|
|||||||
// added relative to commonExtensions
|
// added relative to commonExtensions
|
||||||
blackfriday.EXTENSION_AUTO_HEADER_IDS
|
blackfriday.EXTENSION_AUTO_HEADER_IDS
|
||||||
|
|
||||||
func renderMarkdown(md []byte) []byte {
|
func renderMarkdown(md []byte) ([]byte, error) {
|
||||||
renderer := blackfriday.HtmlRenderer(blackfridayFlags, "", "")
|
renderer := blackfriday.HtmlRenderer(blackfridayFlags, "", "")
|
||||||
html := blackfriday.MarkdownOptions(md, renderer, blackfriday.Options{
|
html := blackfriday.MarkdownOptions(
|
||||||
Extensions: blackfridayExtensions})
|
md,
|
||||||
|
renderer,
|
||||||
|
blackfriday.Options{Extensions: blackfridayExtensions},
|
||||||
|
)
|
||||||
html, err := renderInnerMarkdown(html)
|
html, err := renderInnerMarkdown(html)
|
||||||
if err != nil {
|
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) {
|
func renderInnerMarkdown(b []byte) ([]byte, error) {
|
||||||
z := html.NewTokenizer(bytes.NewReader(b))
|
z := html.NewTokenizer(bytes.NewReader(b))
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
@ -54,7 +69,7 @@ outer:
|
|||||||
return nil, z.Err()
|
return nil, z.Err()
|
||||||
case html.StartTagToken:
|
case html.StartTagToken:
|
||||||
if hasMarkdownAttr(z) {
|
if hasMarkdownAttr(z) {
|
||||||
_, err := buf.Write(removeMarkdownAttr(z.Raw()))
|
_, err := buf.Write(stripMarkdownAttr(z.Raw()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -62,6 +77,7 @@ outer:
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// the above leaves z set to the end token
|
// the above leaves z set to the end token
|
||||||
|
// fall through to render it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err := buf.Write(z.Raw())
|
_, err := buf.Write(z.Raw())
|
||||||
@ -72,8 +88,6 @@ outer:
|
|||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var markdownAttrRE = regexp.MustCompile(`\s*markdown\s*=\s*("1"|'1'|1)\s*`)
|
|
||||||
|
|
||||||
func hasMarkdownAttr(z *html.Tokenizer) bool {
|
func hasMarkdownAttr(z *html.Tokenizer) bool {
|
||||||
for {
|
for {
|
||||||
k, v, more := z.TagAttr()
|
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 = markdownAttrRE.ReplaceAll(tag, []byte(" "))
|
||||||
tag = bytes.Replace(tag, []byte(" >"), []byte(">"), 1)
|
tag = bytes.Replace(tag, []byte(" >"), []byte(">"), 1)
|
||||||
return tag
|
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 {
|
func processInnerMarkdown(w io.Writer, z *html.Tokenizer) error {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
depth := 1
|
depth := 1
|
||||||
@ -111,10 +131,13 @@ loop:
|
|||||||
}
|
}
|
||||||
_, err := buf.Write(z.Raw())
|
_, err := buf.Write(z.Raw())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
html := renderMarkdown(buf.Bytes())
|
html, err := _renderMarkdown(buf.Bytes())
|
||||||
_, err := w.Write(html)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = w.Write(html)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,35 @@
|
|||||||
package renderers
|
package renderers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func renderMarkdownString(s string) string {
|
func TestRenderMarkdown(t *testing.T) {
|
||||||
return string(renderMarkdown([]byte(s)))
|
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) {
|
func mustMarkdownString(md string) string {
|
||||||
require.Equal(t, "<p><em>b</em></p>\n", renderMarkdownString("*b*"))
|
s, err := renderMarkdown([]byte(md))
|
||||||
require.Equal(t, "<div>*b*</div>\n", renderMarkdownString("<div>*b*</div>"))
|
if err != nil {
|
||||||
require.Equal(t, "<div a=1><p><em>b</em></p>\n</div>\n", renderMarkdownString(`<div a=1 markdown="1">*b*</div>`))
|
log.Fatal(err)
|
||||||
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>`))
|
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
|
return err
|
||||||
}
|
}
|
||||||
if p.cfg.IsMarkdown(filename) {
|
if p.cfg.IsMarkdown(filename) {
|
||||||
src = renderMarkdown(src)
|
src, err = renderMarkdown(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_, err = w.Write(src)
|
_, err = w.Write(src)
|
||||||
return err
|
return err
|
||||||
|
@ -19,8 +19,12 @@ type pathError struct {
|
|||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pathError) Error() string {
|
func (pe *pathError) Error() string {
|
||||||
return fmt.Sprintf("%s: %s", p.path, p.cause)
|
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.\
|
// WrapPathError returns an error that will print with a path.\
|
||||||
|
Loading…
Reference in New Issue
Block a user