summaryrefslogtreecommitdiffstats
path: root/tpl/template.go
diff options
context:
space:
mode:
Diffstat (limited to 'tpl/template.go')
-rw-r--r--tpl/template.go218
1 files changed, 19 insertions, 199 deletions
diff --git a/tpl/template.go b/tpl/template.go
index 63bf42730..db715c306 100644
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -14,39 +14,22 @@
package tpl
import (
- "fmt"
- "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"
+ "reflect"
"io"
- "path/filepath"
"regexp"
- "strings"
- "time"
"github.com/gohugoio/hugo/output"
- "github.com/gohugoio/hugo/common/herrors"
-
- "github.com/gohugoio/hugo/hugofs"
-
- "github.com/spf13/afero"
-
texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
- "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
-
- bp "github.com/gohugoio/hugo/bufferpool"
- "github.com/gohugoio/hugo/metrics"
- "github.com/pkg/errors"
)
-var (
- _ TemplateExecutor = (*TemplateAdapter)(nil)
- _ TemplateInfoProvider = (*TemplateAdapter)(nil)
-)
+var _ TemplateInfoProvider = (*TemplateInfo)(nil)
-// TemplateHandler manages the collection of templates.
-type TemplateHandler interface {
- TemplateFinder
+// TemplateManager manages the collection of templates.
+type TemplateManager interface {
+ TemplateHandler
+ TemplateFuncGetter
AddTemplate(name, tpl string) error
AddLateTemplate(name, tpl string) error
LoadTemplates(prefix string) error
@@ -68,6 +51,12 @@ type TemplateFinder interface {
TemplateLookupVariant
}
+// TemplateHandler finds and executes templates.
+type TemplateHandler interface {
+ TemplateFinder
+ Execute(t Template, wr io.Writer, data interface{}) error
+}
+
type TemplateLookup interface {
Lookup(name string) (Template, bool)
}
@@ -87,8 +76,8 @@ type TemplateLookupVariant interface {
// Template is the common interface between text/template and html/template.
type Template interface {
- Execute(wr io.Writer, data interface{}) error
Name() string
+ Prepare() (*texttemplate.Template, error)
}
// TemplateInfoProvider provides some contextual information about a template.
@@ -107,30 +96,15 @@ type TemplateParseFinder interface {
TemplateFinder
}
-// TemplateExecutor adds some extras to Template.
-type TemplateExecutor interface {
- Template
- ExecuteToString(data interface{}) (string, error)
- Tree() string
-}
-
// TemplateDebugger prints some debug info to stdoud.
type TemplateDebugger interface {
Debug()
}
-// TemplateAdapter implements the TemplateExecutor interface.
-type TemplateAdapter struct {
+// TemplateInfo wraps a Template with some additional information.
+type TemplateInfo struct {
Template
- Metrics metrics.Provider
-
Info Info
-
- // 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: (.*?):")
@@ -143,165 +117,11 @@ func extractBaseOf(err string) string {
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{}) (execErr error) {
- defer func() {
- // Panics in templates are a little bit too common (nil pointers etc.)
- // See https://github.com/gohugoio/hugo/issues/5327
- if r := recover(); r != nil {
- execErr = t.addFileContext(t.Name(), fmt.Errorf(`panic in Execute: %s. See "https://github.com/gohugoio/hugo/issues/5327" for the reason why we cannot provide a better error message for this`, r))
- }
- }()
-
- if t.Metrics != nil {
- defer t.Metrics.MeasureSince(t.Name(), time.Now())
- }
-
- execErr = t.Template.Execute(w, data)
- if execErr != nil {
- execErr = t.addFileContext(t.Name(), execErr)
- }
-
- return
-}
-
-func (t *TemplateAdapter) TemplateInfo() Info {
+func (t *TemplateInfo) TemplateInfo() Info {
return t.Info
}
-// The identifiers may be truncated in the log, e.g.
-// "executing "main" at <$scaled.SRelPermalin...>: can't evaluate field SRelPermalink in type *resource.Image"
-var identifiersRe = regexp.MustCompile(`at \<(.*?)(\.{3})?\>:`)
-
-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 {
- if strings.HasPrefix(t.Name(), "_internal") {
- return inerr
- }
-
- f, realFilename, err := t.fileAndFilename(t.Name())
- if err != nil {
- return inerr
-
- }
- defer f.Close()
-
- master, hasMaster := t.NameBaseTemplateName[name]
-
- ferr := errors.Wrap(inerr, "execute of template failed")
-
- // 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(m herrors.LineMatcher) bool {
- if m.Position.LineNumber != m.LineNumber {
- return false
- }
- if !hasMaster {
- return true
- }
-
- identifiers := t.extractIdentifiers(m.Error.Error())
-
- for _, id := range identifiers {
- if strings.Contains(m.Line, id) {
- return true
- }
- }
- return false
- }
-
- fe, ok := herrors.WithFileContext(ferr, realFilename, f, 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()
-
- fe, ok = herrors.WithFileContext(ferr, realFilename, f, lineMatcher)
-
- if !ok {
- // Return the most specific.
- return ferr
-
- }
- 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, "", err
- }
- fim := fi.(hugofs.FileMetaInfo)
- meta := fim.Meta()
-
- f, err := meta.Open()
- if err != nil {
- return nil, "", errors.Wrapf(err, "failed to open template file %q:", filename)
- }
-
- return f, meta.Filename(), nil
-}
-
-// ExecuteToString executes the current template and returns the result as a
-// string.
-func (t *TemplateAdapter) ExecuteToString(data interface{}) (string, error) {
- b := bp.GetBuffer()
- defer bp.PutBuffer(b)
- if err := t.Execute(b, data); err != nil {
- return "", err
- }
- return b.String(), nil
-}
-
-// Tree returns the template Parse tree as a string.
-// Note: this isn't safe for parallel execution on the same template
-// vs Lookup and Execute.
-func (t *TemplateAdapter) Tree() string {
- var tree *parse.Tree
- switch tt := t.Template.(type) {
- case *template.Template:
- tree = tt.Tree
- case *texttemplate.Template:
- tree = tt.Tree
- default:
- panic("Unknown template")
- }
-
- if tree == nil || tree.Root == nil {
- return ""
- }
- s := tree.Root.String()
-
- return s
-}
-
-// TemplateFuncsGetter allows to get a map of functions.
-type TemplateFuncsGetter interface {
- GetFuncs() map[string]interface{}
-}
-
-// TemplateTestMocker adds a way to override some template funcs during tests.
-// The interface is named so it's not used in regular application code.
-type TemplateTestMocker interface {
- SetFuncs(funcMap map[string]interface{})
+// TemplateFuncGetter allows to find a template func by name.
+type TemplateFuncGetter interface {
+ GetFunc(name string) (reflect.Value, bool)
}