diff options
author | Bjørn Erik Pedersen <[email protected]> | 2022-05-12 11:43:20 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2022-05-14 13:40:56 +0200 |
commit | 5c96bda70a7afb2ce97cbb3cd70c64fc8cb94446 (patch) | |
tree | 394a557b0dc7db1f6753cf2a09e8cb0577f18442 /hugolib | |
parent | 4a96df96d958a8ce122f103c4b417eaba52e6cb1 (diff) | |
download | hugo-5c96bda70a7afb2ce97cbb3cd70c64fc8cb94446.tar.gz hugo-5c96bda70a7afb2ce97cbb3cd70c64fc8cb94446.zip |
errors: Misc improvements
* Redo the server error template
* Always add the content file context if relevant
* Remove some now superflous error string matching
* Move the server error template to _server/error.html
* Add file context (with position) to codeblock render blocks
* Improve JS build errors
Fixes #9892
Fixes #9891
Fixes #9893
Diffstat (limited to 'hugolib')
-rw-r--r-- | hugolib/cascade_test.go | 2 | ||||
-rw-r--r-- | hugolib/config.go | 2 | ||||
-rw-r--r-- | hugolib/hugo_sites.go | 2 | ||||
-rw-r--r-- | hugolib/hugo_sites_build_errors_test.go | 248 | ||||
-rw-r--r-- | hugolib/page.go | 32 | ||||
-rw-r--r-- | hugolib/page__per_output.go | 2 | ||||
-rw-r--r-- | hugolib/shortcode.go | 5 |
7 files changed, 261 insertions, 32 deletions
diff --git a/hugolib/cascade_test.go b/hugolib/cascade_test.go index 0a7f66e6c..dff2082b6 100644 --- a/hugolib/cascade_test.go +++ b/hugolib/cascade_test.go @@ -77,7 +77,7 @@ kind = '{section,term}' } builders := make([]*IntegrationTestBuilder, b.N) - for i, _ := range builders { + for i := range builders { builders[i] = NewIntegrationTestBuilder(cfg) } diff --git a/hugolib/config.go b/hugolib/config.go index b2713758a..1e7cf6b06 100644 --- a/hugolib/config.go +++ b/hugolib/config.go @@ -511,5 +511,5 @@ func (configLoader) loadSiteConfig(cfg config.Provider) (scfg SiteConfig, err er } func (l configLoader) wrapFileError(err error, filename string) error { - return herrors.NewFileErrorFromFile(err, filename, filename, l.Fs, herrors.SimpleLineMatcher) + return herrors.NewFileErrorFromFile(err, filename, filename, l.Fs, nil) } diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index 9f18f33bc..4026f58d3 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -968,7 +968,7 @@ func (h *HugoSites) errWithFileContext(err error, f source.File) error { } realFilename := fim.Meta().Filename - return herrors.NewFileErrorFromFile(err, realFilename, realFilename, h.SourceSpec.Fs.Source, herrors.SimpleLineMatcher) + return herrors.NewFileErrorFromFile(err, realFilename, realFilename, h.SourceSpec.Fs.Source, nil) } diff --git a/hugolib/hugo_sites_build_errors_test.go b/hugolib/hugo_sites_build_errors_test.go index 8f983075d..ffbfe1c17 100644 --- a/hugolib/hugo_sites_build_errors_test.go +++ b/hugolib/hugo_sites_build_errors_test.go @@ -347,13 +347,75 @@ minify = true } -func TestErrorNested(t *testing.T) { +func TestErrorNestedRender(t *testing.T) { t.Parallel() files := ` -- config.toml -- +-- content/_index.md -- +--- +title: "Home" +--- +-- layouts/index.html -- +line 1 +line 2 +1{{ .Render "myview" }} +-- layouts/_default/myview.html -- +line 1 +12{{ partial "foo.html" . }} +line 4 +line 5 +-- layouts/partials/foo.html -- +line 1 +line 2 +123{{ .ThisDoesNotExist }} +line 4 +` + + b, err := NewIntegrationTestBuilder( + IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).BuildE() + + b.Assert(err, qt.IsNotNil) + errors := herrors.UnwrapFileErrorsWithErrorContext(err) + b.Assert(errors, qt.HasLen, 4) + b.Assert(errors[0].Position().LineNumber, qt.Equals, 3) + b.Assert(errors[0].Position().ColumnNumber, qt.Equals, 4) + b.Assert(errors[0].Error(), qt.Contains, filepath.FromSlash(`"/layouts/index.html:3:4": execute of template failed`)) + b.Assert(errors[0].ErrorContext().Lines, qt.DeepEquals, []string{"line 1", "line 2", "1{{ .Render \"myview\" }}"}) + b.Assert(errors[2].Position().LineNumber, qt.Equals, 2) + b.Assert(errors[2].Position().ColumnNumber, qt.Equals, 5) + b.Assert(errors[2].ErrorContext().Lines, qt.DeepEquals, []string{"line 1", "12{{ partial \"foo.html\" . }}", "line 4", "line 5"}) + + b.Assert(errors[3].Position().LineNumber, qt.Equals, 3) + b.Assert(errors[3].Position().ColumnNumber, qt.Equals, 6) + b.Assert(errors[3].ErrorContext().Lines, qt.DeepEquals, []string{"line 1", "line 2", "123{{ .ThisDoesNotExist }}", "line 4"}) + +} + +func TestErrorNestedShortocde(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +-- content/_index.md -- +--- +title: "Home" +--- + +## Hello +{{< hello >}} + -- layouts/index.html -- line 1 +line 2 +{{ .Content }} +line 5 +-- layouts/shortcodes/hello.html -- +line 1 12{{ partial "foo.html" . }} line 4 line 5 @@ -373,15 +435,183 @@ line 4 b.Assert(err, qt.IsNotNil) errors := herrors.UnwrapFileErrorsWithErrorContext(err) + + b.Assert(errors, qt.HasLen, 3) + + b.Assert(errors[0].Position().LineNumber, qt.Equals, 6) + b.Assert(errors[0].Position().ColumnNumber, qt.Equals, 1) + b.Assert(errors[0].ErrorContext().ChromaLexer, qt.Equals, "md") + b.Assert(errors[0].Error(), qt.Contains, filepath.FromSlash(`"/content/_index.md:6:1": failed to render shortcode "hello": failed to process shortcode: "/layouts/shortcodes/hello.html:2:5":`)) + b.Assert(errors[0].ErrorContext().Lines, qt.DeepEquals, []string{"", "## Hello", "{{< hello >}}", ""}) + b.Assert(errors[1].ErrorContext().Lines, qt.DeepEquals, []string{"line 1", "12{{ partial \"foo.html\" . }}", "line 4", "line 5"}) + b.Assert(errors[2].ErrorContext().Lines, qt.DeepEquals, []string{"line 1", "line 2", "123{{ .ThisDoesNotExist }}", "line 4"}) + +} + +func TestErrorRenderHookHeading(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +-- content/_index.md -- +--- +title: "Home" +--- + +## Hello + +-- layouts/index.html -- +line 1 +line 2 +{{ .Content }} +line 5 +-- layouts/_default/_markup/render-heading.html -- +line 1 +12{{ .Levels }} +line 4 +line 5 +` + + b, err := NewIntegrationTestBuilder( + IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).BuildE() + + b.Assert(err, qt.IsNotNil) + errors := herrors.UnwrapFileErrorsWithErrorContext(err) + b.Assert(errors, qt.HasLen, 2) - fmt.Println(errors[0]) - b.Assert(errors[0].Position().LineNumber, qt.Equals, 2) - b.Assert(errors[0].Position().ColumnNumber, qt.Equals, 5) - b.Assert(errors[0].Error(), qt.Contains, filepath.FromSlash(`"/layouts/index.html:2:5": execute of template failed`)) - b.Assert(errors[0].ErrorContext().Lines, qt.DeepEquals, []string{"line 1", "12{{ partial \"foo.html\" . }}", "line 4", "line 5"}) - b.Assert(errors[1].Position().LineNumber, qt.Equals, 3) - b.Assert(errors[1].Position().ColumnNumber, qt.Equals, 6) - b.Assert(errors[1].ErrorContext().Lines, qt.DeepEquals, []string{"line 1", "line 2", "123{{ .ThisDoesNotExist }}", "line 4"}) + b.Assert(errors[0].Error(), qt.Contains, filepath.FromSlash(`"/content/_index.md:1:1": "/layouts/_default/_markup/render-heading.html:2:5": execute of template failed`)) + +} + +func TestErrorRenderHookCodeblock(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +-- content/_index.md -- +--- +title: "Home" +--- + +## Hello + +§§§ foo +bar +§§§ + + +-- layouts/index.html -- +line 1 +line 2 +{{ .Content }} +line 5 +-- layouts/_default/_markup/render-codeblock-foo.html -- +line 1 +12{{ .Foo }} +line 4 +line 5 +` + + b, err := NewIntegrationTestBuilder( + IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).BuildE() + + b.Assert(err, qt.IsNotNil) + errors := herrors.UnwrapFileErrorsWithErrorContext(err) + + b.Assert(errors, qt.HasLen, 2) + first := errors[0] + b.Assert(first.Error(), qt.Contains, filepath.FromSlash(`"/content/_index.md:7:1": "/layouts/_default/_markup/render-codeblock-foo.html:2:5": execute of template failed`)) + +} + +func TestErrorInBaseTemplate(t *testing.T) { + t.Parallel() + + filesTemplate := ` +-- config.toml -- +-- content/_index.md -- +--- +title: "Home" +--- +-- layouts/baseof.html -- +line 1 base +line 2 base +{{ block "main" . }}empty{{ end }} +line 4 base +{{ block "toc" . }}empty{{ end }} +-- layouts/index.html -- +{{ define "main" }} +line 2 index +line 3 index +line 4 index +{{ end }} +{{ define "toc" }} +TOC: {{ partial "toc.html" . }} +{{ end }} +-- layouts/partials/toc.html -- +toc line 1 +toc line 2 +toc line 3 +toc line 4 + + + + +` + + t.Run("base template", func(t *testing.T) { + files := strings.Replace(filesTemplate, "line 4 base", "123{{ .ThisDoesNotExist \"abc\" }}", 1) + + b, err := NewIntegrationTestBuilder( + IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).BuildE() + + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`render of "home" failed: "/layouts/baseof.html:4:6"`)) + + }) + + t.Run("index template", func(t *testing.T) { + files := strings.Replace(filesTemplate, "line 3 index", "1234{{ .ThisDoesNotExist \"abc\" }}", 1) + + b, err := NewIntegrationTestBuilder( + IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).BuildE() + + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`render of "home" failed: "/layouts/index.html:3:7"`)) + + }) + + t.Run("partial from define", func(t *testing.T) { + files := strings.Replace(filesTemplate, "toc line 2", "12345{{ .ThisDoesNotExist \"abc\" }}", 1) + + b, err := NewIntegrationTestBuilder( + IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).BuildE() + + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`render of "home" failed: "/layouts/index.html:7:8": execute of template failed`)) + b.Assert(err.Error(), qt.Contains, `execute of template failed: template: partials/toc.html:2:8: executing "partials/toc.html"`) + + }) } diff --git a/hugolib/page.go b/hugolib/page.go index 4faefa3cc..e9f937105 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -572,25 +572,23 @@ func (p *pageState) wrapError(err error) error { filename := p.File().Filename() - if ferr := herrors.UnwrapFileError(err); ferr != nil { + // Check if it's already added. + for _, ferr := range herrors.UnwrapFileErrors(err) { errfilename := ferr.Position().Filename - if ferr.ErrorContext() != nil || errfilename == "" || !(errfilename == pageFileErrorName || filepath.IsAbs(errfilename)) { - return err - } - if filepath.IsAbs(errfilename) { - filename = errfilename - } - f, ferr2 := p.s.SourceSpec.Fs.Source.Open(filename) - if ferr2 != nil { + if errfilename == filename { + if ferr.ErrorContext() == nil { + f, ioerr := p.s.SourceSpec.Fs.Source.Open(filename) + if ioerr != nil { + return err + } + defer f.Close() + ferr.UpdateContent(f, nil) + } return err } - defer f.Close() - pos := ferr.Position() - pos.Filename = filename - return ferr.UpdatePosition(pos).UpdateContent(f, herrors.SimpleLineMatcher) } - return herrors.NewFileErrorFromFile(err, filename, filename, p.s.SourceSpec.Fs.Source, herrors.SimpleLineMatcher) + return herrors.NewFileErrorFromFile(err, filename, filename, p.s.SourceSpec.Fs.Source, herrors.NopLineMatcher) } @@ -644,8 +642,10 @@ Loop: m, err := metadecoders.Default.UnmarshalToMap(it.Val, f) if err != nil { if fe, ok := err.(herrors.FileError); ok { - // Offset the starting position of front matter. pos := fe.Position() + // Apply the error to the content file. + pos.Filename = p.File().Filename() + // Offset the starting position of front matter. offset := iter.LineNumber() - 1 if f == metadecoders.YAML { offset -= 1 @@ -788,7 +788,7 @@ func (p *pageState) outputFormat() (f output.Format) { func (p *pageState) parseError(err error, input []byte, offset int) error { pos := p.posFromInput(input, offset) - return herrors.NewFileError("page.md", err).UpdatePosition(pos) + return herrors.NewFileError(p.File().Filename(), err).UpdatePosition(pos) } func (p *pageState) pathOrTitle() string { diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go index fdce8e802..6460b120b 100644 --- a/hugolib/page__per_output.go +++ b/hugolib/page__per_output.go @@ -417,7 +417,7 @@ func (p *pageContentOutput) Render(layout ...string) (template.HTML, error) { // Make sure to send the *pageState and not the *pageContentOutput to the template. res, err := executeToString(p.p.s.Tmpl(), templ, p.p) if err != nil { - return "", p.p.wrapError(fmt.Errorf("failed to execute template %q v: %w", layout, err)) + return "", p.p.wrapError(fmt.Errorf("failed to execute template %s: %w", templ.Name(), err)) } return template.HTML(res), nil } diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go index 42877b537..f8cda6b8d 100644 --- a/hugolib/shortcode.go +++ b/hugolib/shortcode.go @@ -270,7 +270,6 @@ const ( innerNewlineRegexp = "\n" innerCleanupRegexp = `\A<p>(.*)</p>\n\z` innerCleanupExpand = "$1" - pageFileErrorName = "page.md" ) func renderShortcode( @@ -299,7 +298,7 @@ func renderShortcode( var err error tmpl, err = s.TextTmpl().Parse(templName, templStr) if err != nil { - fe := herrors.NewFileError(pageFileErrorName, err) + fe := herrors.NewFileError(p.File().Filename(), err) pos := fe.Position() pos.LineNumber += p.posOffset(sc.pos).LineNumber fe = fe.UpdatePosition(pos) @@ -392,7 +391,7 @@ func renderShortcode( result, err := renderShortcodeWithPage(s.Tmpl(), tmpl, data) if err != nil && sc.isInline { - fe := herrors.NewFileError("shortcode.md", err) + fe := herrors.NewFileError(p.File().Filename(), err) pos := fe.Position() pos.LineNumber += p.posOffset(sc.pos).LineNumber fe = fe.UpdatePosition(pos) |