diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2025-04-14 11:20:36 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2025-04-14 14:38:22 +0200 |
commit | 8a2830f2dcbf2c9b799ff8d1db9601da58f98494 (patch) | |
tree | 05c0c66d1f8da8c17f1bc06df5c9a5176a7a391a | |
parent | 1e0287f4729f477c6b956fc50f0bfde0edecaa33 (diff) | |
download | hugo-8a2830f2dcbf2c9b799ff8d1db9601da58f98494.tar.gz hugo-8a2830f2dcbf2c9b799ff8d1db9601da58f98494.zip |
tpl: Add proper file context to template parse errors
Fixes #13604
-rw-r--r-- | tpl/templates/templates_integration_test.go | 33 | ||||
-rw-r--r-- | tpl/tplimpl/templates.go | 11 | ||||
-rw-r--r-- | tpl/tplimpl/templatestore.go | 32 |
3 files changed, 60 insertions, 16 deletions
diff --git a/tpl/templates/templates_integration_test.go b/tpl/templates/templates_integration_test.go index a0a5e385a..635d521d7 100644 --- a/tpl/templates/templates_integration_test.go +++ b/tpl/templates/templates_integration_test.go @@ -17,6 +17,7 @@ import ( "path/filepath" "testing" + qt "github.com/frankban/quicktest" "github.com/gohugoio/hugo/hugolib" ) @@ -229,3 +230,35 @@ layouts/section/section.html b := hugolib.Test(t, files) b.AssertFileContent("public/mysection/index.html", "layouts/section/section.html") } + +func TestErrorMessageParseError(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +-- layouts/home.html -- +Line 1. +Line 2. {{ foo }} <- this func does not exist. +Line 3. +` + + b, err := hugolib.TestE(t, files) + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`"/layouts/home.html:2:1": parse of template failed: template: home.html:2: function "foo" not defined`)) +} + +func TestErrorMessageExecuteError(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +-- layouts/home.html -- +Line 1. +Line 2. {{ .Foo }} <- this method does not exist. +Line 3. +` + + b, err := hugolib.TestE(t, files) + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(` "/layouts/home.html:2:11": execute of template failed`)) +} diff --git a/tpl/tplimpl/templates.go b/tpl/tplimpl/templates.go index f3f98f622..19de48e38 100644 --- a/tpl/tplimpl/templates.go +++ b/tpl/tplimpl/templates.go @@ -44,7 +44,16 @@ var embeddedTemplatesAliases = map[string][]string{ "_shortcodes/twitter.html": {"_shortcodes/tweet.html"}, } -func (t *templateNamespace) parseTemplate(ti *TemplInfo) error { +func (s *TemplateStore) parseTemplate(ti *TemplInfo) error { + err := s.tns.doParseTemplate(ti) + if err != nil { + return s.addFileContext(ti, "parse of template failed", err) + } + + return err +} + +func (t *templateNamespace) doParseTemplate(ti *TemplInfo) error { if !ti.noBaseOf || ti.category == CategoryBaseof { // Delay parsing until we have the base template. return nil diff --git a/tpl/tplimpl/templatestore.go b/tpl/tplimpl/templatestore.go index 2e332e382..7544b5625 100644 --- a/tpl/tplimpl/templatestore.go +++ b/tpl/tplimpl/templatestore.go @@ -495,7 +495,7 @@ func (t *TemplateStore) ExecuteWithContext(ctx context.Context, ti *TemplInfo, w execErr := t.storeSite.executer.ExecuteWithContext(ctx, ti, wr, data) if execErr != nil { - return t.addFileContext(ti, execErr) + return t.addFileContext(ti, "execute of template failed", execErr) } return nil } @@ -822,7 +822,7 @@ func (t *TemplateStore) addDeferredTemplate(owner *TemplInfo, name string, n *pa return nil } -func (s *TemplateStore) addFileContext(ti *TemplInfo, inerr error) error { +func (s *TemplateStore) addFileContext(ti *TemplInfo, what string, inerr error) error { if ti.Fi == nil { return inerr } @@ -854,25 +854,27 @@ func (s *TemplateStore) addFileContext(ti *TemplInfo, inerr error) error { fe := herrors.NewFileErrorFromName(inErr, fi.Meta().Filename) fe.UpdateContent(f, lineMatcher) - if !fe.ErrorContext().Position.IsValid() { - return inErr, false - } - return fe, true + return fe, fe.ErrorContext().Position.IsValid() } - inerr = fmt.Errorf("execute of template failed: %w", inerr) + inerr = fmt.Errorf("%s: %w", what, inerr) - if err, ok := checkFilename(ti.Fi, inerr); ok { - return err + var ( + currentErr error + ok bool + ) + + if currentErr, ok = checkFilename(ti.Fi, inerr); ok { + return currentErr } if ti.base != nil { - if err, ok := checkFilename(ti.base.Fi, inerr); ok { - return err + if currentErr, ok = checkFilename(ti.base.Fi, inerr); ok { + return currentErr } } - return inerr + return currentErr } func (s *TemplateStore) extractIdentifiers(line string) []string { @@ -1389,7 +1391,7 @@ func (s *TemplateStore) parseTemplates() error { if vv.state == processingStateTransformed { continue } - if err := s.tns.parseTemplate(vv); err != nil { + if err := s.parseTemplate(vv); err != nil { return err } } @@ -1409,7 +1411,7 @@ func (s *TemplateStore) parseTemplates() error { // The regular expression used to detect if a template needs a base template has some // rare false positives. Assume we don't need one. vv.noBaseOf = true - if err := s.tns.parseTemplate(vv); err != nil { + if err := s.parseTemplate(vv); err != nil { return err } continue @@ -1438,7 +1440,7 @@ func (s *TemplateStore) parseTemplates() error { if vvv.state == processingStateTransformed { continue } - if err := s.tns.parseTemplate(vvv); err != nil { + if err := s.parseTemplate(vvv); err != nil { return err } } |