summaryrefslogtreecommitdiffstats
path: root/tpl
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2017-04-02 14:20:34 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2017-04-02 14:20:34 +0200
commit7eb71ee06419f9ceedfd701ab2a27513ef448829 (patch)
tree254ba6c75575e067df76b4abe4e566936619adcb /tpl
parentc97dae40d9cd24c467f5b8cfbe2ac06f3cdef1d2 (diff)
Revert "tpl: Rework to handle both text and HTML templates"
Will have to take another stab at this ... This reverts commit 5c5efa03d2512749950b0d05a7d4bde35ecbdc37. Closes #3260
Diffstat (limited to 'tpl')
-rw-r--r--tpl/template.go111
-rw-r--r--tpl/tplimpl/ace.go51
-rw-r--r--tpl/tplimpl/amber_compiler.go4
-rw-r--r--tpl/tplimpl/template.go770
-rw-r--r--tpl/tplimpl/templateFuncster.go86
-rw-r--r--tpl/tplimpl/templateProvider.go59
-rw-r--r--tpl/tplimpl/template_ast_transformers.go50
-rw-r--r--tpl/tplimpl/template_ast_transformers_test.go12
-rw-r--r--tpl/tplimpl/template_embedded.go57
-rw-r--r--tpl/tplimpl/template_funcs.go33
-rw-r--r--tpl/tplimpl/template_funcs_test.go106
-rw-r--r--tpl/tplimpl/template_test.go232
12 files changed, 644 insertions, 927 deletions
diff --git a/tpl/template.go b/tpl/template.go
index 617aa84ec..b94fc3242 100644
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -1,103 +1,28 @@
-// Copyright 2017-present 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 (
- "io"
-
- "text/template/parse"
-
"html/template"
- texttemplate "text/template"
-
- bp "github.com/spf13/hugo/bufferpool"
-)
-
-var (
- _ TemplateExecutor = (*TemplateAdapter)(nil)
+ "io"
)
-// TemplateHandler manages the collection of templates.
-type TemplateHandler interface {
- TemplateFinder
+// TODO(bep) make smaller
+type Template interface {
+ ExecuteTemplate(wr io.Writer, name string, data interface{}) error
+ ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML
+ Lookup(name string) *template.Template
+ Templates() []*template.Template
+ New(name string) *template.Template
+ GetClone() *template.Template
+ RebuildClone() *template.Template
+ LoadTemplates(absPath string)
+ LoadTemplatesWithPrefix(absPath, prefix string)
AddTemplate(name, tpl string) error
- AddLateTemplate(name, tpl string) error
- LoadTemplates(absPath, prefix string)
+ AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error
+ AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error
+ AddInternalTemplate(prefix, name, tpl string) error
+ AddInternalShortcode(name, tpl string) error
+ Partial(name string, contextList ...interface{}) template.HTML
PrintErrors()
-
+ Funcs(funcMap template.FuncMap)
MarkReady()
- RebuildClone()
-}
-
-// TemplateFinder finds templates.
-type TemplateFinder interface {
- Lookup(name string) *TemplateAdapter
-}
-
-// Template is the common interface between text/template and html/template.
-type Template interface {
- Execute(wr io.Writer, data interface{}) error
- Name() string
-}
-
-// TemplateExecutor adds some extras to Template.
-type TemplateExecutor interface {
- Template
- ExecuteToString(data interface{}) (string, error)
- Tree() string
-}
-
-// TemplateAdapter implements the TemplateExecutor interface.
-type TemplateAdapter struct {
- Template
-}
-
-// 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.Root == nil {
- return ""
- }
- s := tree.Root.String()
-
- return s
-}
-
-// 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{})
}
diff --git a/tpl/tplimpl/ace.go b/tpl/tplimpl/ace.go
deleted file mode 100644
index fc3a1e1b1..000000000
--- a/tpl/tplimpl/ace.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2017-present 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 tplimpl
-
-import (
- "path/filepath"
-
- "strings"
-
- "github.com/yosssi/ace"
-)
-
-func (t *templateHandler) addAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error {
- t.checkState()
- var base, inner *ace.File
- name = name[:len(name)-len(filepath.Ext(innerPath))] + ".html"
-
- // Fixes issue #1178
- basePath = strings.Replace(basePath, "\\", "/", -1)
- innerPath = strings.Replace(innerPath, "\\", "/", -1)
-
- if basePath != "" {
- base = ace.NewFile(basePath, baseContent)
- inner = ace.NewFile(innerPath, innerContent)
- } else {
- base = ace.NewFile(innerPath, innerContent)
- inner = ace.NewFile("", []byte{})
- }
- parsed, err := ace.ParseSource(ace.NewSource(base, inner, []*ace.File{}), nil)
- if err != nil {
- t.errors = append(t.errors, &templateErr{name: name, err: err})
- return err
- }
- templ, err := ace.CompileResultWithTemplate(t.html.t.New(name), parsed, nil)
- if err != nil {
- t.errors = append(t.errors, &templateErr{name: name, err: err})
- return err
- }
- return applyTemplateTransformersToHMLTTemplate(templ)
-}
diff --git a/tpl/tplimpl/amber_compiler.go b/tpl/tplimpl/amber_compiler.go
index 10ed0443c..252c39ffb 100644
--- a/tpl/tplimpl/amber_compiler.go
+++ b/tpl/tplimpl/amber_compiler.go
@@ -19,7 +19,7 @@ import (
"github.com/eknkc/amber"
)
-func (t *templateHandler) compileAmberWithTemplate(b []byte, path string, templ *template.Template) (*template.Template, error) {
+func (gt *GoHTMLTemplate) CompileAmberWithTemplate(b []byte, path string, t *template.Template) (*template.Template, error) {
c := amber.New()
if err := c.ParseData(b, path); err != nil {
@@ -32,7 +32,7 @@ func (t *templateHandler) compileAmberWithTemplate(b []byte, path string, templ
return nil, err
}
- tpl, err := templ.Funcs(t.amberFuncMap).Parse(data)
+ tpl, err := t.Funcs(gt.amberFuncMap).Parse(data)
if err != nil {
return nil, err
diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go
index 49be4a769..ea12cde7a 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 2016 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.
@@ -15,39 +15,23 @@ package tplimpl
import (
"html/template"
- "strings"
- texttemplate "text/template"
-
- "github.com/eknkc/amber"
-
+ "io"
"os"
-
- "github.com/spf13/hugo/output"
-
"path/filepath"
+ "strings"
+
"sync"
+ "github.com/eknkc/amber"
"github.com/spf13/afero"
+ bp "github.com/spf13/hugo/bufferpool"
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/tpl"
-)
-
-const (
- textTmplNamePrefix = "_text/"
+ "github.com/spf13/hugo/output"
+ "github.com/yosssi/ace"
)
-var (
- _ tpl.TemplateHandler = (*templateHandler)(nil)
- _ tpl.TemplateTestMocker = (*templateHandler)(nil)
- _ tpl.TemplateFinder = (*htmlTemplates)(nil)
- _ tpl.TemplateFinder = (*textTemplates)(nil)
- _ templateLoader = (*htmlTemplates)(nil)
- _ templateLoader = (*textTemplates)(nil)
- _ templateLoader = (*templateHandler)(nil)
- _ templateFuncsterTemplater = (*htmlTemplates)(nil)
- _ templateFuncsterTemplater = (*textTemplates)(nil)
-)
+// TODO(bep) globals get rid of the rest of the jww.ERR etc.
// Protecting global map access (Amber)
var amberMu sync.Mutex
@@ -57,539 +41,340 @@ type templateErr struct {
err error
}
-type templateLoader interface {
- handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error
- addTemplate(name, tpl string) error
- addLateTemplate(name, tpl string) error
-}
-
-type templateFuncsterTemplater interface {
- tpl.TemplateFinder
- setFuncs(funcMap map[string]interface{})
- setTemplateFuncster(f *templateFuncster)
-}
+type GoHTMLTemplate struct {
+ *template.Template
-// templateHandler holds the templates in play.
-// It implements the templateLoader and tpl.TemplateHandler interfaces.
-type templateHandler struct {
- // text holds all the pure text templates.
- text *textTemplates
- html *htmlTemplates
+ // This looks, and is, strange.
+ // The clone is used by non-renderable content pages, and these need to be
+ // re-parsed on content change, and to avoid the
+ // "cannot Parse after Execute" error, we need to re-clone it from the original clone.
+ clone *template.Template
+ cloneClone *template.Template
- amberFuncMap template.FuncMap
+ // a separate storage for the overlays created from cloned master templates.
+ // note: No mutex protection, so we add these in one Go routine, then just read.
+ overlays map[string]*template.Template
errors []*templateErr
+ funcster *templateFuncster
+
+ amberFuncMap template.FuncMap
+
*deps.Deps
}
-func (t *templateHandler) addError(name string, err error) {
- t.errors = append(t.errors, &templateErr{name, err})
-}
+type TemplateProvider struct{}
+
+var DefaultTemplateProvider *TemplateProvider
-// 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)
+// Update updates the Hugo Template System in the provided Deps.
+// with all the additional features, templates & functions
+func (*TemplateProvider) Update(deps *deps.Deps) error {
+ tmpl := &GoHTMLTemplate{
+ Template: template.New(""),
+ overlays: make(map[string]*template.Template),
+ errors: make([]*templateErr, 0),
+ Deps: deps,
}
-}
-// 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.TemplateAdapter {
- var te *tpl.TemplateAdapter
+ deps.Tmpl = tmpl
- isTextTemplate := strings.HasPrefix(name, textTmplNamePrefix)
+ tmpl.initFuncs(deps)
- if isTextTemplate {
- // The templates are stored without the prefix identificator.
- name = strings.TrimPrefix(name, textTmplNamePrefix)
- te = t.text.Lookup(name)
- } else {
- te = t.html.Lookup(name)
- }
+ tmpl.LoadEmbedded()
+
+ if deps.WithTemplate != nil {
+ err := deps.WithTemplate(tmpl)
+ if err != nil {
+ tmpl.errors = append(tmpl.errors, &templateErr{"init", err})
+ }
- if te == nil {
- return nil
}
- return te
+ tmpl.MarkReady()
+
+ return nil
+
}
-func (t *templateHandler) clone(d *deps.Deps) *templateHandler {
- c := &templateHandler{
- Deps: d,
- html: &htmlTemplates{t: template.Must(t.html.t.Clone()), overlays: make(map[string]*template.Template)},
- text: &textTemplates{t: texttemplate.Must(t.text.t.Clone()), overlays: make(map[string]*texttemplate.Template)},
- errors: make([]*templateErr, 0),
- }
+// Clone clones
+func (*TemplateProvider) Clone(d *deps.Deps) error {
- d.Tmpl = c
+ t := d.Tmpl.(*GoHTMLTemplate)
- c.initFuncs()
+ // 1. Clone the clone with new template funcs
+ // 2. Clone any overlays with new template funcs
+
+ tmpl := &GoHTMLTemplate{
+ Template: template.Must(t.Template.Clone()),
+ overlays: make(map[string]*template.Template),
+ errors: make([]*templateErr, 0),
+ Deps: d,
+ }
- for k, v := range t.html.overlays {
+ d.Tmpl = tmpl
+ tmpl.initFuncs(d)
+
+ for k, v := range t.overlays {
vc := template.Must(v.Clone())
// The extra lookup is a workaround, see
// * https://github.com/golang/go/issues/16101
// * https://github.com/spf13/hugo/issues/2549
vc = vc.Lookup(vc.Name())
- vc.Funcs(t.html.funcster.funcMap)
- c.html.overlays[k] = vc
- }
-
- for k, v := range t.text.overlays {
- vc := texttemplate.Must(v.Clone())
- vc = vc.Lookup(vc.Name())
- vc.Funcs(texttemplate.FuncMap(t.text.funcster.funcMap))
- c.text.overlays[k] = vc
+ vc.Funcs(tmpl.funcster.funcMap)
+ tmpl.overlays[k] = vc
}
- return c
+ tmpl.MarkReady()
+ return nil
}
-func newTemplateAdapter(deps *deps.Deps) *templateHandler {
- htmlT := &htmlTemplates{
- t: template.New(""),
- overlays: make(map[string]*template.Template),
- }
- textT := &textTemplates{
- t: texttemplate.New(""),
- overlays: make(map[string]*texttemplate.Template),
- }
- return &templateHandler{
- Deps: deps,
- html: htmlT,
- text: textT,
- errors: make([]*templateErr, 0),
- }
+func (t *GoHTMLTemplate) initFuncs(d *deps.Deps) {
-}
+ t.funcster = newTemplateFuncster(d)
-type htmlTemplates struct {
- funcster *templateFuncster
+ // The URL funcs in the funcMap is somewhat language dependent,
+ // so we need to wait until the language and site config is loaded.
+ t.funcster.initFuncMap()
- t *template.Template
+ t.amberFuncMap = template.FuncMap{}
- // This looks, and is, strange.
- // The clone is used by non-renderable content pages, and these need to be
- // re-parsed on content change, and to avoid the
- // "cannot Parse after Execute" error, we need to re-clone it from the original clone.
- clone *template.Template
- cloneClone *template.Template
+ amberMu.Lock()
+ for k, v := range amber.FuncMap {
+ t.amberFuncMap[k] = v
+ }
- // a separate storage for the overlays created from cloned master templates.
- // note: No mutex protection, so we add these in one Go routine, then just read.
- overlays map[string]*template.Template
-}
+ for k, v := range t.funcster.funcMap {
+ t.amberFuncMap[k] = v
+ // Hacky, but we need to make sure that the func names are in the global map.
+ amber.FuncMap[k] = func() string {
+ panic("should never be invoked")
+ }
+ }
+ amberMu.Unlock()
-func (t *htmlTemplates) setTemplateFuncster(f *templateFuncster) {
- t.funcster = f
}
-func (t *htmlTemplates) Lookup(name string) *tpl.TemplateAdapter {
- templ := t.lookup(name)
- if templ == nil {
- return nil
- }
- return &tpl.TemplateAdapter{templ}
+func (t *GoHTMLTemplate) Funcs(funcMap template.FuncMap) {
+ t.Template.Funcs(funcMap)
}
-func (t *htmlTemplates) lookup(name string) *template.Template {
- if templ := t.t.Lookup(name); templ != nil {
- return templ
- }
- if t.overlays != nil {
- if templ, ok := t.overlays[name]; ok {
- return templ
- }
+func (t *GoHTMLTemplate) Partial(name string, contextList ...interface{}) template.HTML {
+ if strings.HasPrefix("partials/", name) {
+ name = name[8:]
}
+ var context interface{}
- if t.clone != nil {
- return t.clone.Lookup(name)
+ if len(contextList) == 0 {
+ context = nil
+ } else {
+ context = contextList[0]
}
-
- return nil
+ return t.ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name)
}
-type textTemplates struct {
- funcster *templateFuncster
-
- t *texttemplate.Template
-
- clone *texttemplate.Template
- cloneClone *texttemplate.Template
+func (t *GoHTMLTemplate) executeTemplate(context interface{}, w io.Writer, layouts ...string) {
+ var worked bool
+ for _, layout := range layouts {
+ templ := t.Lookup(layout)
+ if templ == nil {
+ // TODO(bep) output
+ layout += ".html"
+ templ = t.Lookup(layout)
+ }
- overlays map[string]*texttemplate.Template
+ if templ != nil {
+ if err := templ.Execute(w, context); err != nil {
+ helpers.DistinctErrorLog.Println(layout, err)
+ }
+ worked = true
+ break
+ }
+ }
+ if !worked {
+ t.Log.ERROR.Println("Unable to render", layouts)
+ t.Log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
+ }
}
-func (t *textTemplates) setTemplateFuncster(f *templateFuncster) {
- t.funcster = f
+func (t *GoHTMLTemplate) ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML {
+ b := bp.GetBuffer()
+ defer bp.PutBuffer(b)
+ t.executeTemplate(context, b, layouts...)
+ return template.HTML(b.String())
}
-func (t *textTemplates) Lookup(name string) *tpl.TemplateAdapter {
- templ := t.lookup(name)
- if templ == nil {
- return nil
- }
- return &tpl.TemplateAdapter{templ}
-}
+func (t *GoHTMLTemplate) Lookup(name string) *template.Template {
-func (t *textTemplates) lookup(name string) *texttemplate.Template {
- if templ := t.t.Lookup(name); templ != nil {
+ if templ := t.Template.Lookup(name); templ != nil {
return templ
}
+
if t.overlays != nil {
if templ, ok := t.overlays[name]; ok {
return templ
}
}
+ // The clone is used for the non-renderable HTML pages (p.IsRenderable == false) that is parsed
+ // as Go templates late in the build process.
if t.clone != nil {
- return t.clone.Lookup(name)
+ if templ := t.clone.Lookup(name); templ != nil {
+ return templ
+ }
}
return nil
-}
-
-func (t *templateHandler) setFuncs(funcMap map[string]interface{}) {
- t.html.setFuncs(funcMap)
- t.text.setFuncs(funcMap)
-}
-// SetFuncs replaces the funcs in the func maps with new definitions.
-// This is only used in tests.
-func (t *templateHandler) SetFuncs(funcMap map[string]interface{}) {
- t.setFuncs(funcMap)
}
-func (t *htmlTemplates) setFuncs(funcMap map[string]interface{}) {
- t.t.Funcs(funcMap)
+func (t *GoHTMLTemplate) GetClone() *template.Template {
+ return t.clone
}
-func (t *textTemplates) setFuncs(funcMap map[string]interface{}) {
- t.t.Funcs(funcMap)
+func (t *GoHTMLTemplate) RebuildClone() *template.Template {
+ t.clone = template.Must(t.cloneClone.Clone())
+ return t.clone
}
-// LoadTemplates loads the templates, starting from the given absolute path.
-// A prefix can be given to indicate a template namespace to load the templates
-// into, i.e. "_internal" etc.
-func (t *templateHandler) LoadTemplates(absPath, prefix string) {
- // TODO(bep) output formats. Will have to get to complete list when that is ready.
- t.loadTemplates(absPath, prefix, output.Formats{output.HTMLFormat, output.RSSFormat, output.CalendarFormat, output.AMPFormat, output.JSONFormat})
-
+func (t *GoHTMLTemplate) LoadEmbedded() {
+ t.EmbedShortcodes()
+ t.EmbedTemplates()
}
-func (t *htmlTemplates) addTemplateIn(tt *template.Template, name, tpl string) error {
- templ, err := tt.New(name).Parse(tpl)
- if err != nil {
- return err
+// MarkReady marks the template as "ready for execution". No changes allowed
+// after this is set.
+// TODO(bep) if this proves to be resource heavy, we could detect
+// earlier if we really need this, or make it lazy.
+func (t *GoHTMLTemplate) MarkReady() {
+ if t.clone == nil {
+ t.clone = template.Must(t.Template.Clone())
+ t.cloneClone = template.Must(t.clone.Clone())
}
+}
- if err := applyTemplateTransformersToHMLTTemplate(templ); err != nil {
- return err
+func (t *GoHTMLTemplate) checkState() {
+ if t.clone != nil {
+ panic("template is cloned and cannot be modfified")
}
-
- return nil
}
-func (t *htmlTemplates) addTemplate(name, tpl string) error {
- return t.addTemplateIn(t.t, name, tpl)
+func (t *GoHTMLTemplate) AddInternalTemplate(prefix, name, tpl string) error {
+ if prefix != "" {
+ return t.AddTemplate("_internal/"+prefix+"/"+name, tpl)
+ }
+ return t.AddTemplate("_internal/"+name, tpl)
}
-func (t *htmlTemplates) addLateTemplate(name, tpl string) error {
- return t.addTemplateIn(t.clone, name, tpl)
+func (t *GoHTMLTemplate) AddInternalShortcode(name, content string) error {
+ return t.AddInternalTemplate("shortcodes", name, content)
}
-func (t *textTemplates) addTemplateIn(tt *texttemplate.Template, name, tpl string) error {
- name = strings.TrimPrefix(name, textTmplNamePrefix)
- templ, err := tt.New(name).Parse(tpl)
+func (t *GoHTMLTemplate) AddTemplate(name, tpl string) error {
+ t.checkState()
+ templ, err := t.New(name).Parse(tpl)
if err != nil {
+ t.errors = append(t.errors, &templateErr{name: name, err: err})
return err
}
-
- if err := applyTemplateTransformersToTextTemplate(templ); err != nil {
- return err
- }
-
- return nil
-}
-
-func (t *textTemplates) addTemplate(name, tpl string) error {
- return t.addTemplateIn(t.t, name, tpl)
-}
-
-func (t *textTemplates) addLateTemplate(name, tpl string) error {
- return t.addTemplateIn(t.clone, name, tpl)
-}
-
-func (t *templateHandler) addTemplate(name, tpl string) error {
- return t.AddTemplate(name, tpl)
-}
-
-func (t *templateHandler) addLateTemplate(name, tpl string) error {
- return t.AddLateTemplate(name, tpl)
-}
-
-// AddLateTemplate is used to add a template late, i.e. after the
-// regular templates have started its execution.
-func (t *templateHandler) AddLateTemplate(name, tpl string) error {
- h := t.getTemplateHandler(name)
- if err := h.addLateTemplate(name, tpl); err != nil {
- t.addError(name, err)
+ if err := applyTemplateTransformers(templ); err != nil {
return err
}
- return nil
-}
-// AddTemplate parses and adds a template to the collection.
-// Templates with name prefixed with "_text" will be handled as plain
-// text templates.
-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
}
-// MarkReady marks the templates as "ready for execution". No changes allowed
-// after this is set.
-// TODO(bep) if this proves to be resource heavy, we could detect
-// earlier if we really need this, or make it lazy.
-func (t *templateHandler) MarkReady() {
- if t.html.clone == nil {
- t.html.clone = template.Must(t.html.t.Clone())
- t.html.cloneClone = template.Must(t.html.clone.Clone())
- }
- if t.text.clone == nil {
- t.text.clone = texttemplate.Must(t.text.t.Clone())
- t.text.cloneClone = texttemplate.Must(t.text.clone.Clone())
- }
-}
-
-// 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())
-}
-
-func (t *templateHandler) loadTemplates(absPath string, prefix string, formats output.Formats) {
- t.Log.DEBUG.Printf("Load templates from path %q prefix %q", absPath, prefix)
- walker := func(path string, fi os.FileInfo, err error) error {
- if err != nil {
- return nil
- }
-
- t.Log.DEBUG.Println("Template path", path)
- if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
- link, err := filepath.EvalSymlinks(absPath)
- if err != nil {
- t.Log.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", absPath, err)
- return nil
- }
-
- linkfi, err := t.Fs.Source.Stat(link)
- if err != nil {
- t.Log.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
- return nil
- }
-
- if !linkfi.Mode().IsRegular() {
- t.Log.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", absPath)
- }
- return nil
- }
-
- if !fi.IsDir() {
- if isDotFile(path) || isBackupFile(path) || isBaseTemplate(path) {
- return nil
- }
-
- var (
- workingDir = t.PathSpec.WorkingDir()
- themeDir = t.PathSpec.GetThemeDir()
- layoutDir = t.PathSpec.LayoutDir()
- )
-
- if themeDir != "" && strings.HasPrefix(absPath, themeDir) {
- workingDir = themeDir
- layoutDir = "layouts"
- }
-
- li := strings.LastIndex(path, layoutDir) + len(layoutDir) + 1
- relPath := path[li:]
-
- descriptor := output.TemplateLookupDescriptor{
- WorkingDir: workingDir,
- LayoutDir: layoutDir,
- RelPath: relPath,
- Prefix: prefix,
- Theme: t.PathSpec.Theme(),
- OutputFormats: formats,
- FileExists: func(filename string) (bool, error) {
- return helpers.Exists(filename, t.Fs.Source)
- },
- ContainsAny: func(filename string, subslices [][]byte) (bool, error) {
- return helpers.FileContainsAny(filename, subslices, t.Fs.Source)
- },
- }
-
- 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 nil
- }
- if err := helpers.SymbolicWalk(t.Fs.Source, absPath, walker); err != nil {
- t.Log.ERROR.Printf("Failed to load templates: %s", err)
- }
-}
-
-func (t *templateHandler) initFuncs() {
-
- // The template funcs need separation between text and html templates.
- for _, funcsterHolder := range []templateFuncsterTemplater{t.html, t.text} {
- funcster := newTemplateFuncster(t.Deps, funcsterHolder)
-
- // The URL funcs in the funcMap is somewhat language dependent,
- // so we need to wait until the language and site config is loaded.
- funcster.initFuncMap()
-
- funcsterHolder.setTemplateFuncster(funcster)
-
- }
+func (t *GoHTMLTemplate)