summaryrefslogtreecommitdiffstats
path: root/tpl
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2018-10-03 14:58:09 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2018-10-16 22:10:56 +0200
commit35fbfb19a173b01bc881f2bbc5d104136633a7ec (patch)
tree636d0d51fa262dc808eb3c5cc9cf92ad977a0c6a /tpl
parent3a3089121b852332b5744d1f566959c8cf93cef4 (diff)
commands: Show server error info in browser
The main item in this commit is showing of errors with a file context when running `hugo server`. This can be turned off: `hugo server --disableBrowserError` (can also be set in `config.toml`). But to get there, the error handling in Hugo needed a revision. There are some items left TODO for commits soon to follow, most notable errors in content and config files. Fixes #5284 Fixes #5290 See #5325 See #5324
Diffstat (limited to 'tpl')
-rw-r--r--tpl/collections/collections_test.go7
-rw-r--r--tpl/data/data.go6
-rw-r--r--tpl/data/data_test.go10
-rw-r--r--tpl/fmt/fmt.go5
-rw-r--r--tpl/fmt/init.go2
-rw-r--r--tpl/fmt/init_test.go3
-rw-r--r--tpl/partials/init_test.go2
-rw-r--r--tpl/resources/resources.go4
-rw-r--r--tpl/strings/strings.go8
-rw-r--r--tpl/template.go127
-rw-r--r--tpl/template_test.go31
-rw-r--r--tpl/tplimpl/template.go147
-rw-r--r--tpl/tplimpl/templateProvider.go7
-rw-r--r--tpl/tplimpl/template_errors.go46
-rw-r--r--tpl/tplimpl/template_funcs_test.go8
-rw-r--r--tpl/urls/urls.go7
16 files changed, 327 insertions, 93 deletions
diff --git a/tpl/collections/collections_test.go b/tpl/collections/collections_test.go
index c8a7207ea..b3ac8a8f2 100644
--- a/tpl/collections/collections_test.go
+++ b/tpl/collections/collections_test.go
@@ -17,20 +17,17 @@ import (
"errors"
"fmt"
"html/template"
- "io/ioutil"
- "log"
"math/rand"
- "os"
"reflect"
"testing"
"time"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/langs"
- jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -856,7 +853,7 @@ func newDeps(cfg config.Provider) *deps.Deps {
Cfg: cfg,
Fs: hugofs.NewMem(l),
ContentSpec: cs,
- Log: jww.NewNotepad(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime),
+ Log: loggers.NewErrorLogger(),
}
}
diff --git a/tpl/data/data.go b/tpl/data/data.go
index 14a4975a5..3f87eda31 100644
--- a/tpl/data/data.go
+++ b/tpl/data/data.go
@@ -18,12 +18,12 @@ import (
"encoding/csv"
"encoding/json"
"errors"
- "fmt"
"net/http"
"strings"
"time"
"github.com/gohugoio/hugo/deps"
+ _errors "github.com/pkg/errors"
)
// New returns a new instance of the data-namespaced template functions.
@@ -59,7 +59,7 @@ func (ns *Namespace) GetCSV(sep string, urlParts ...string) (d [][]string, err e
var req *http.Request
req, err = http.NewRequest("GET", url, nil)
if err != nil {
- return nil, fmt.Errorf("Failed to create request for getCSV for resource %s: %s", url, err)
+ return nil, _errors.Wrapf(err, "Failed to create request for getCSV for resource %s:", url)
}
req.Header.Add("Accept", "text/csv")
@@ -103,7 +103,7 @@ func (ns *Namespace) GetJSON(urlParts ...string) (v interface{}, err error) {
var req *http.Request
req, err = http.NewRequest("GET", url, nil)
if err != nil {
- return nil, fmt.Errorf("Failed to create request for getJSON resource %s: %s", url, err)
+ return nil, _errors.Wrapf(err, "Failed to create request for getJSON resource %s:", url)
}
req.Header.Add("Accept", "application/json")
diff --git a/tpl/data/data_test.go b/tpl/data/data_test.go
index 6bee0d524..9ef969244 100644
--- a/tpl/data/data_test.go
+++ b/tpl/data/data_test.go
@@ -113,11 +113,11 @@ func TestGetCSV(t *testing.T) {
require.NoError(t, err, msg)
if _, ok := test.expect.(bool); ok {
- require.Equal(t, 1, int(ns.deps.Log.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError)))
+ require.Equal(t, 1, int(ns.deps.Log.ErrorCounter.Count()))
require.Nil(t, got)
continue
}
- require.Equal(t, 0, int(ns.deps.Log.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError)))
+ require.Equal(t, 0, int(ns.deps.Log.ErrorCounter.Count()))
require.NotNil(t, got, msg)
assert.EqualValues(t, test.expect, got, msg)
@@ -198,14 +198,14 @@ func TestGetJSON(t *testing.T) {
continue
}
- if errLevel, ok := test.expect.(jww.Threshold); ok {
- logCount := ns.deps.Log.LogCountForLevelsGreaterThanorEqualTo(errLevel)
+ if errLevel, ok := test.expect.(jww.Threshold); ok && errLevel >= jww.LevelError {
+ logCount := ns.deps.Log.ErrorCounter.Count()
require.True(t, logCount >= 1, fmt.Sprintf("got log count %d", logCount))
continue
}
require.NoError(t, err, msg)
- require.Equal(t, 0, int(ns.deps.Log.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError)), msg)
+ require.Equal(t, 0, int(ns.deps.Log.ErrorCounter.Count()), msg)
require.NotNil(t, got, msg)
assert.EqualValues(t, test.expect, got, msg)
diff --git a/tpl/fmt/fmt.go b/tpl/fmt/fmt.go
index 0f4f906c2..09e4f5a40 100644
--- a/tpl/fmt/fmt.go
+++ b/tpl/fmt/fmt.go
@@ -16,12 +16,13 @@ package fmt
import (
_fmt "fmt"
+ "github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/helpers"
)
// New returns a new instance of the fmt-namespaced template functions.
-func New() *Namespace {
- return &Namespace{helpers.NewDistinctErrorLogger()}
+func New(d *deps.Deps) *Namespace {
+ return &Namespace{helpers.NewDistinctLogger(d.Log.ERROR)}
}
// Namespace provides template functions for the "fmt" namespace.
diff --git a/tpl/fmt/init.go b/tpl/fmt/init.go
index 76c68957a..117055801 100644
--- a/tpl/fmt/init.go
+++ b/tpl/fmt/init.go
@@ -22,7 +22,7 @@ const name = "fmt"
func init() {
f := func(d *deps.Deps) *internal.TemplateFuncsNamespace {
- ctx := New()
+ ctx := New(d)
ns := &internal.TemplateFuncsNamespace{
Name: name,
diff --git a/tpl/fmt/init_test.go b/tpl/fmt/init_test.go
index 01eb2fa69..b693ffa2b 100644
--- a/tpl/fmt/init_test.go
+++ b/tpl/fmt/init_test.go
@@ -16,6 +16,7 @@ package fmt
import (
"testing"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/tpl/internal"
"github.com/stretchr/testify/require"
@@ -26,7 +27,7 @@ func TestInit(t *testing.T) {
var ns *internal.TemplateFuncsNamespace
for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
- ns = nsf(&deps.Deps{})
+ ns = nsf(&deps.Deps{Log: loggers.NewErrorLogger()})
if ns.Name == name {
found = true
break
diff --git a/tpl/partials/init_test.go b/tpl/partials/init_test.go
index 4832e6b66..0513f1572 100644
--- a/tpl/partials/init_test.go
+++ b/tpl/partials/init_test.go
@@ -16,6 +16,7 @@ package partials
import (
"testing"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/tpl/internal"
"github.com/stretchr/testify/require"
@@ -28,6 +29,7 @@ func TestInit(t *testing.T) {
for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
ns = nsf(&deps.Deps{
BuildStartListeners: &deps.Listeners{},
+ Log: loggers.NewErrorLogger(),
})
if ns.Name == name {
found = true
diff --git a/tpl/resources/resources.go b/tpl/resources/resources.go
index 883afbcd7..c24cd2b42 100644
--- a/tpl/resources/resources.go
+++ b/tpl/resources/resources.go
@@ -18,6 +18,8 @@ import (
"fmt"
"path/filepath"
+ _errors "github.com/pkg/errors"
+
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/resource"
"github.com/gohugoio/hugo/resource/bundler"
@@ -256,7 +258,7 @@ func (ns *Namespace) resolveArgs(args []interface{}) (resource.Resource, map[str
m, err := cast.ToStringMapE(args[0])
if err != nil {
- return nil, nil, fmt.Errorf("invalid options type: %s", err)
+ return nil, nil, _errors.Wrap(err, "invalid options type")
}
return r, m, nil
diff --git a/tpl/strings/strings.go b/tpl/strings/strings.go
index 9b8409ed6..1853cd727 100644
--- a/tpl/strings/strings.go
+++ b/tpl/strings/strings.go
@@ -20,6 +20,8 @@ import (
_strings "strings"
"unicode/utf8"
+ _errors "github.com/pkg/errors"
+
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/helpers"
"github.com/spf13/cast"
@@ -44,7 +46,7 @@ type Namespace struct {
func (ns *Namespace) CountRunes(s interface{}) (int, error) {
ss, err := cast.ToStringE(s)
if err != nil {
- return 0, fmt.Errorf("Failed to convert content to string: %s", err)
+ return 0, _errors.Wrap(err, "Failed to convert content to string")
}
counter := 0
@@ -61,7 +63,7 @@ func (ns *Namespace) CountRunes(s interface{}) (int, error) {
func (ns *Namespace) RuneCount(s interface{}) (int, error) {
ss, err := cast.ToStringE(s)
if err != nil {
- return 0, fmt.Errorf("Failed to convert content to string: %s", err)
+ return 0, _errors.Wrap(err, "Failed to convert content to string")
}
return utf8.RuneCountInString(ss), nil
}
@@ -70,7 +72,7 @@ func (ns *Namespace) RuneCount(s interface{}) (int, error) {
func (ns *Namespace) CountWords(s interface{}) (int, error) {
ss, err := cast.ToStringE(s)
if err != nil {
- return 0, fmt.Errorf("Failed to convert content to string: %s", err)
+ return 0, _errors.Wrap(err, "Failed to convert content to string")
}
counter := 0
diff --git a/tpl/template.go b/tpl/template.go
index 2cef92bb2..02b2d4a9b 100644
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -1,4 +1,4 @@
-// Copyright 2017-present The Hugo Authors. All rights reserved.
+// Copyright 2018 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,16 +14,26 @@
package tpl
import (
+ "fmt"
"io"
+ "path/filepath"
+ "regexp"
+ "strings"
"time"
- "text/template/parse"
+ "github.com/gohugoio/hugo/common/herrors"
+
+ "github.com/gohugoio/hugo/hugofs"
+
+ "github.com/spf13/afero"
"html/template"
texttemplate "text/template"
+ "text/template/parse"
bp "github.com/gohugoio/hugo/bufferpool"
"github.com/gohugoio/hugo/metrics"
+ "github.com/pkg/errors"
)
var (
@@ -35,8 +45,7 @@ type TemplateHandler interface {
TemplateFinder
AddTemplate(name, tpl string) error
AddLateTemplate(name, tpl string) error
- LoadTemplates(prefix string)
- PrintErrors()
+ LoadTemplates(prefix string) error
NewTextTemplate() TemplateParseFinder
@@ -82,16 +91,122 @@ type TemplateDebugger interface {
type TemplateAdapter struct {
Template
Metrics metrics.Provider
+
+ // The filesystem where the templates are stored.
+ Fs afero.Fs
+
+ // Maps to base template if relevant.
+ NameBaseTemplateName map[string]string
+}
+
+var baseOfRe = regexp.MustCompile("template: (.*?):")
+
+func extractBaseOf(err string) string {
+ m := baseOfRe.FindStringSubmatch(err)
+ if len(m) == 2 {
+ return m[1]
+ }
+ return ""
}
// Execute executes the current template. The actual execution is performed
// by the embedded text or html template, but we add an implementation here so
// we can add a timer for some metrics.
-func (t *TemplateAdapter) Execute(w io.Writer, data interface{}) error {
+func (t *TemplateAdapter) Execute(w io.Writer, data interface{}) (execErr error) {
+ defer func() {
+ // Panics in templates are a little bit too common (nil pointers etc.)
+ if r := recover(); r != nil {
+ execErr = t.addFileContext(t.Name(), fmt.Errorf("panic in Execute: %s", r))
+ }
+ }()
+
if t.Metrics != nil {
defer t.Metrics.MeasureSince(t.Name(), time.Now())
}
- return t.Template.Execute(w, data)
+
+ execErr = t.Template.Execute(w, data)
+ if execErr != nil {
+ execErr = t.addFileContext(t.Name(), execErr)
+ }
+
+ return
+}
+
+var identifiersRe = regexp.MustCompile("at \\<(.*?)\\>:")
+
+func (t *TemplateAdapter) extractIdentifiers(line string) []string {
+ m := identifiersRe.FindAllStringSubmatch(line, -1)
+ identifiers := make([]string, len(m))
+ for i := 0; i < len(m); i++ {
+ identifiers[i] = m[i][1]
+ }
+ return identifiers
+}
+
+func (t *TemplateAdapter) addFileContext(name string, inerr error) error {
+ f, realFilename, err := t.fileAndFilename(t.Name())
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ master, hasMaster := t.NameBaseTemplateName[name]
+
+ ferr := errors.Wrapf(inerr, "execute of template %q failed", realFilename)
+
+ // Since this can be a composite of multiple template files (single.html + baseof.html etc.)
+ // we potentially need to look in both -- and cannot rely on line number alone.
+ lineMatcher := func(le herrors.FileError, lineNumber int, line string) bool {
+ if le.LineNumber() != lineNumber {
+ return false
+ }
+ if !hasMaster {
+ return true
+ }
+
+ identifiers := t.extractIdentifiers(le.Error())
+
+ for _, id := range identifiers {
+ if strings.Contains(line, id) {
+ return true
+ }
+ }
+ return false
+ }
+
+ // TODO(bep) 2errors text vs HTML
+ fe, ok := herrors.WithFileContext(ferr, f, "go-html-template", lineMatcher)
+ if ok || !hasMaster {
+ return fe
+ }
+
+ // Try the base template if relevant
+ f, realFilename, err = t.fileAndFilename(master)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ ferr = errors.Wrapf(inerr, "execute of template %q failed", realFilename)
+ fe, _ = herrors.WithFileContext(ferr, f, "go-html-template", lineMatcher)
+ return fe
+
+}
+
+func (t *TemplateAdapter) fileAndFilename(name string) (afero.File, string, error) {
+ fs := t.Fs
+ filename := filepath.FromSlash(name)
+
+ fi, err := fs.Stat(filename)
+ if err != nil {
+ return nil, "", errors.Wrapf(err, "failed to Stat %q", filename)
+ }
+ f, err := fs.Open(filename)
+ if err != nil {
+ return nil, "", errors.Wrapf(err, "failed to open template file %q:", filename)
+ }
+
+ return f, fi.(hugofs.RealFilenameInfo).RealFilename(), nil
}
// ExecuteToString executes the current template and returns the result as a
diff --git a/tpl/template_test.go b/tpl/template_test.go
new file mode 100644
index 000000000..73e9640be
--- /dev/null
+++ b/tpl/template_test.go
@@ -0,0 +1,31 @@
+// Copyright 2018 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tpl
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestExtractBaseof(t *testing.T) {
+ assert := require.New(t)
+
+ replaced := extractBaseOf(`failed: template: _default/baseof.html:37:11: executing "_default/baseof.html" at <.Parents>: can't evaluate field Parents in type *hugolib.PageOutput`)
+
+ assert.Equal("_default/baseof.html", replaced)
+ assert.Equal("", extractBaseOf("not baseof for you"))
+ assert.Equal("blog/baseof.html", extractBaseOf("template: blog/baseof.html:23:11:"))
+ assert.Equal("blog/baseof.ace", extractBaseOf("template: blog/baseof.ace:23:11:"))
+}
diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go
index f19c312ec..fc77bb1af 100644
--- a/tpl/tplimpl/template.go
+++ b/tpl/tplimpl/template.go
@@ -1,4 +1,4 @@
-// Copyright 2017-present The Hugo Authors. All rights reserved.
+// Copyright 2018 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -20,7 +20,9 @@ import (
"strings"
texttemplate "text/template"
+ "github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/tpl/tplimpl/embedded"
+ "github.com/pkg/errors"
"github.com/eknkc/amber"
@@ -64,7 +66,7 @@ type templateErr struct {
}
type templateLoader interface {
- handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error
+ handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (templateInfo, error)) error
addTemplate(name, tpl string) error
addLateTemplate(name, tpl string) error
}
@@ -114,22 +116,11 @@ func (t *templateHandler) NewTextTemplate() tpl.TemplateParseFinder {
}
-func (t *templateHandler) addError(name string, err error) {
- t.errors = append(t.errors, &templateErr{name, err})
-}
-
func (t *templateHandler) Debug() {
fmt.Println("HTML templates:\n", t.html.t.DefinedTemplates())
fmt.Println("\n\nText templates:\n", t.text.t.DefinedTemplates())
}
-// PrintErrors prints the accumulated errors as ERROR to the log.
-func (t *templateHandler) PrintErrors() {
- for _, e := range t.errors {
- t.Log.ERROR.Println(e.name, ":", e.err)
- }
-}
-
// Lookup tries to find a template with the given name in both template
// collections: First HTML, then the plain text template collection.
func (t *templateHandler) Lookup(name string) (tpl.Template, bool) {
@@ -156,8 +147,8 @@ func (t *templateHandler) clone(d *deps.Deps) *templateHandler {
c := &templateHandler{
Deps: d,
layoutsFs: d.BaseFs.Layouts.Fs,
- html: &htmlTemplates{t: template.Must(t.html.t.Clone()), overlays: make(map[string]*template.Template)},
- text: &textTemplates{textTemplate: &textTemplate{t: texttemplate.Must(t.text.t.Clone())}, overlays: make(map[string]*texttemplate.Template)},
+ html: &htmlTemplates{t: template.Must(t.html.t.Clone()), overlays: make(map[string]*template.Template), templatesCommon: t.html.templatesCommon},
+ text: &textTemplates{textTemplate: &textTemplate{t: texttemplate.Must(t.text.t.Clone())}, overlays: make(map[string]*texttemplate.Template), templatesCommon: t.text.templatesCommon},
errors: make([]*templateErr, 0),
}
@@ -187,15 +178,21 @@ func (t *templateHandler) clone(d *deps.Deps) *templateHandler {
}
func newTemplateAdapter(deps *deps.Deps) *templateHandler {
+ common := &templatesCommon{
+ nameBaseTemplateName: make(map[string]string),
+ }
+
htmlT := &htmlTemplates{
- t: template.New(""),
- overlays: make(map[string]*template.Template),
+ t: template.New(""),
+ overlays: make(map[string]*template.Template),
+ templatesCommon: common,
}
textT := &textTemplates{
- textTemplate: &textTemplate{t: texttemplate.New("")},
- overlays: make(map[string]*texttemplate.Template),
+ textTemplate: &textTemplate{t: texttemplate.New("")},
+ overlays: make(map[string]*texttemplate.Template),
+ templatesCommon: common,
}
- return &templateHandler{
+ h := &templateHandler{
Deps: deps,
layoutsFs: deps.BaseFs.Layouts.Fs,
html: htmlT,
@@ -203,11 +200,23 @@ func newTemplateAdapter(deps *deps.Deps) *templateHandler {
errors: make([]*templateErr, 0),
}
+ common.handler = h
+
+ return h
+
}
-type htmlTemplates struct {
+// Shared by both HTML and text templates.
+type templatesCommon struct {
+ handler *templateHandler
funcster *templateFuncster
+ // Used to get proper filenames in errors
+ nameBaseTemplateName map[string]string
+}
+type htmlTemplates struct {
+ *templatesCommon
+
t *template.Template
// This looks, and is, strange.
@@ -231,7 +240,8 @@ func (t *htmlTemplates) Lookup(name string) (tpl.Template, bool) {
if templ == nil {
return nil, false
}
- return &tpl.TemplateAdapter{Template: templ, Metrics: t.funcster.Deps.Metrics}, true
+
+ return &tpl.TemplateAdapter{Template: templ, Metrics: t.funcster.Deps.Metrics, Fs: t.handler.layoutsFs, NameBaseTemplateName: t.nameBaseTemplateName}, true
}
func (t *htmlTemplates) lookup(name string) *template.Template {
@@ -259,8 +269,8 @@ func (t *textTemplates) setTemplateFuncster(f *templateFuncster) {
}
type textTemplates struct {
+ *templatesCommon
*textTemplate
- funcster *templateFuncster
clone *texttemplate.Template
cloneClone *texttemplate.Template
@@ -272,7 +282,7 @@ func (t *textTemplates) Lookup(name string) (tpl.Template, bool) {
if templ == nil {
return nil, false
}
- return &tpl.TemplateAdapter{Template: templ, Metrics: t.funcster.Deps.Metrics}, true
+ return &tpl.TemplateAdapter{Template: templ, Metrics: t.funcster.Deps.Metrics, Fs: t.handler.layoutsFs, NameBaseTemplateName: t.nameBaseTemplateName}, true
}
func (t *textTemplates) lookup(name string) *texttemplate.Template {
@@ -321,8 +331,8 @@ func (t *textTemplates) setFuncs(funcMap map[string]interface{}) {
// LoadTemplates loads the templates from the layouts filesystem.
// A prefix can be given to indicate a template namespace to load the templates
// into, i.e. "_internal" etc.
-func (t *templateHandler) LoadTemplates(prefix string) {
- t.loadTemplates(prefix)
+func (t *templateHandler) LoadTemplates(prefix string) error {
+ return t.loadTemplates(prefix)
}
@@ -423,7 +433,6 @@ func (t *templateHandler) addLateTemplate(name, tpl string) error {
func (t *templateHandler) AddLateTemplate(name, tpl string) error {
h := t.getTemplateHandler(name)
if err := h.addLateTemplate(name, tpl); err != nil {
- t.addError(name, err)
return err
}
return nil
@@ -435,7 +444,6 @@ func (t *templateHandler) AddLateTemplate(name, tpl string) error {
func (t *templateHandler) AddTemplate(name, tpl string) error {
h := t.getTemplateHandler(name)
if err := h.addTemplate(name, tpl); err != nil {
- t.addError(name, err)
return err
}
return nil
@@ -458,14 +466,19 @@ func (t *templateHandler) MarkReady() {
// RebuildClone rebuilds the cloned templates. Used for live-reloads.
func (t *templateHandler) RebuildClone() {
- t.html.clone = template.Must(t.html.cloneClone.Clone())
- t.text.clone = texttemplate.Must(t.text.cloneClone.Clone())
+ if t.html != nil && t.html.cloneClone != nil {
+ t.html.clone = template.Must(t.html.cloneClone.Clone())
+ }
+ if t.text != nil && t.text.cloneClone != nil {
+ t.text.clone = texttemplate.Must(t.text.cloneClone.Clone())
+ }
}
-func (t *templateHandler) loadTemplates(prefix string) {
+func (t *templateHandler) loadTemplates(prefix string) error {
+
walker := func(path string, fi os.FileInfo, err error) error {
if err != nil || fi.IsDir() {
- return nil
+ return err
}
if isDotFile(path) || isBackupFile(path) || isBaseTemplate(path) {
@@ -490,21 +503,25 @@ func (t *templateHandler) loadTemplates(prefix string) {
tplID, err := output.CreateTemplateNames(descriptor)
if err != nil {
t.Log.ERROR.Printf("Failed to resolve template in path %q: %s", path, err)
-
return nil
}
if err := t.addTemplateFile(tplID.Name, tplID.MasterFilename, tplID.OverlayFilename); err != nil {
- t.Log.ERROR.Printf("Failed to add template %q in path %q: %s", tplID.Name, path, err)
+ return err
}
return nil
}
if err := helpers.SymbolicWalk(t.Layouts.Fs, "", walker); err != nil {
- t.Log.ERROR.Printf("Failed to load templates: %s", err)
+ if !os.IsNotExist(err) {
+ return err
+ }
+ return nil
}
+ return nil
+
}
func (t *templateHandler) initFuncs() {
@@ -553,12 +570,12 @@ func (t *templateHandler) getTemplateHandler(name string) templateLoader {
return t.html
}
-func (t *templateHandler) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error {
+func (t *templateHandler) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (templateInfo, error)) error {
h := t.getTemplateHandler(name)
return h.handleMaster(name, overlayFilename, masterFilename, onMissing)
}
-func (t *htmlTemplates) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error {
+func (t *htmlTemplates) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (templateInfo, error)) error {
masterTpl := t.lookup(masterFilename)
@@ -568,9 +585,9 @@ func (t *htmlTemplates) handleMaster(name, overlayFilename, masterFilename strin
return err
}
- masterTpl, err = t.t.New(overlayFilename).Parse(templ)
+ masterTpl, err = t.t.New(overlayFilename).Parse(templ.template)
if err != nil {
- return err
+ return templ.errWithFileContext("parse master failed", err)
}
}
@@ -579,9 +596,9 @@ func (t *htmlTemplates) handleMaster(name, overlayFilename, masterFilename strin
return err
}
- overlayTpl, err := template.Must(masterTpl.Clone()).Parse(templ)
+ overlayTpl, err := template.Must(masterTpl.Clone()).Parse(templ.template)
if err != nil {
- return err
+ return templ.errWithFileContext("parse failed", err)
}
// The extra lookup is a workaround, see
@@ -593,12 +610,13 @@ func (t *htmlTemplates) handleMaster(name, overlayFilename, masterFilename strin
}
t.overlays[name] = overlayTpl
+ t.nameBaseTemplateName[name] = masterFilename
return err
}
-func (t *textTemplates) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error {
+func (t *textTemplates) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (templateInfo, error)) error {
name = strings.TrimPrefix(name, textTmplNamePrefix)
masterTpl := t.lookup(masterFilename)
@@ -609,10 +627,11 @@ func (t *textTemplates) handleMaster(name, overlayFilename, masterFilename strin
return err
}
- masterTpl, err = t.t.New(overlayFilename).Parse(templ)
+ masterTpl, err = t.t.New(masterFilename).Parse(templ.template)
if err != nil {
- return err
+ return errors.Wrapf(err, "failed to parse %q:", templ.filename)
}
+ t.nameBaseTemplateName[masterFilename] = templ.filename
}
templ, err := onMissing(overlayFilename)
@@ -620,9 +639,9 @@ func (t *textTemplates) handleMaster(name, overlayFilename, masterFilename strin
return err