summaryrefslogtreecommitdiffstats
path: root/tpl
diff options
context:
space:
mode:
Diffstat (limited to 'tpl')
-rw-r--r--tpl/collections/apply.go2
-rw-r--r--tpl/collections/apply_test.go14
-rw-r--r--tpl/partials/partials.go6
-rw-r--r--tpl/resources/resources.go3
-rw-r--r--tpl/template.go12
-rw-r--r--tpl/templates/templates.go2
-rw-r--r--tpl/tplimpl/shortcodes.go3
-rw-r--r--tpl/tplimpl/template.go1163
-rw-r--r--tpl/tplimpl/templateProvider.go27
-rw-r--r--tpl/tplimpl/template_ast_transformers.go130
-rw-r--r--tpl/tplimpl/template_ast_transformers_test.go78
-rw-r--r--tpl/tplimpl/template_errors.go6
-rw-r--r--tpl/tplimpl/template_funcs_test.go4
-rw-r--r--tpl/tplimpl/template_info_test.go7
14 files changed, 659 insertions, 798 deletions
diff --git a/tpl/collections/apply.go b/tpl/collections/apply.go
index d41a3b1da..55d29d3a9 100644
--- a/tpl/collections/apply.go
+++ b/tpl/collections/apply.go
@@ -106,7 +106,7 @@ func applyFnToThis(fn, this reflect.Value, args ...interface{}) (reflect.Value,
func (ns *Namespace) lookupFunc(fname string) (reflect.Value, bool) {
if !strings.ContainsRune(fname, '.') {
- templ := ns.deps.Tmpl.(tpl.TemplateFuncGetter)
+ templ := ns.deps.Tmpl().(tpl.TemplateFuncGetter)
return templ.GetFunc(fname)
}
diff --git a/tpl/collections/apply_test.go b/tpl/collections/apply_test.go
index 5b21d5a97..0d06f52e8 100644
--- a/tpl/collections/apply_test.go
+++ b/tpl/collections/apply_test.go
@@ -22,6 +22,7 @@ import (
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/deps"
+ "github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/tpl"
)
@@ -31,10 +32,18 @@ func (templateFinder) Lookup(name string) (tpl.Template, bool) {
return nil, false
}
+func (templateFinder) HasTemplate(name string) bool {
+ return false
+}
+
func (templateFinder) LookupVariant(name string, variants tpl.TemplateVariants) (tpl.Template, bool, bool) {
return nil, false, false
}
+func (templateFinder) LookupLayout(d output.LayoutDescriptor, f output.Format) (tpl.Template, bool, error) {
+ return nil, false, nil
+}
+
func (templateFinder) Execute(t tpl.Template, wr io.Writer, data interface{}) error {
return nil
}
@@ -51,8 +60,9 @@ func (templateFinder) GetFunc(name string) (reflect.Value, bool) {
func TestApply(t *testing.T) {
t.Parallel()
c := qt.New(t)
-
- ns := New(&deps.Deps{Tmpl: new(templateFinder)})
+ d := &deps.Deps{}
+ d.SetTmpl(new(templateFinder))
+ ns := New(d)
strings := []interface{}{"a\n", "b\n"}
diff --git a/tpl/partials/partials.go b/tpl/partials/partials.go
index 6f3ba2d13..e03bf471f 100644
--- a/tpl/partials/partials.go
+++ b/tpl/partials/partials.go
@@ -105,11 +105,11 @@ func (ns *Namespace) Include(name string, contextList ...interface{}) (interface
}
n := "partials/" + name
- templ, found := ns.deps.Tmpl.Lookup(n)
+ templ, found := ns.deps.Tmpl().Lookup(n)
if !found {
// For legacy reasons.
- templ, found = ns.deps.Tmpl.Lookup(n + ".html")
+ templ, found = ns.deps.Tmpl().Lookup(n + ".html")
}
if !found {
@@ -139,7 +139,7 @@ func (ns *Namespace) Include(name string, contextList ...interface{}) (interface
w = b
}
- if err := ns.deps.Tmpl.Execute(templ, w, context); err != nil {
+ if err := ns.deps.Tmpl().Execute(templ, w, context); err != nil {
return "", err
}
diff --git a/tpl/resources/resources.go b/tpl/resources/resources.go
index 9a7b29696..fd0ffc5ec 100644
--- a/tpl/resources/resources.go
+++ b/tpl/resources/resources.go
@@ -45,6 +45,7 @@ func New(deps *deps.Deps) (*Namespace, error) {
if err != nil {
return nil, err
}
+
return &Namespace{
deps: deps,
scssClient: scssClient,
@@ -53,7 +54,7 @@ func New(deps *deps.Deps) (*Namespace, error) {
integrityClient: integrity.New(deps.ResourceSpec),
minifyClient: minifier.New(deps.ResourceSpec),
postcssClient: postcss.New(deps.ResourceSpec),
- templatesClient: templates.New(deps.ResourceSpec, deps.Tmpl, deps.TextTmpl),
+ templatesClient: templates.New(deps.ResourceSpec, deps),
}, nil
}
diff --git a/tpl/template.go b/tpl/template.go
index 0841236de..b9b0749b6 100644
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -30,9 +30,7 @@ type TemplateManager interface {
TemplateFuncGetter
AddTemplate(name, tpl string) error
AddLateTemplate(name, tpl string) error
- LoadTemplates(prefix string) error
-
- RebuildClone()
+ MarkReady() error
}
// TemplateVariants describes the possible variants of a template.
@@ -52,6 +50,8 @@ type TemplateFinder interface {
type TemplateHandler interface {
TemplateFinder
Execute(t Template, wr io.Writer, data interface{}) error
+ LookupLayout(d output.LayoutDescriptor, f output.Format) (Template, bool, error)
+ HasTemplate(name string) bool
}
type TemplateLookup interface {
@@ -105,6 +105,12 @@ type templateInfoManager struct {
InfoManager
}
+// TemplatesProvider as implemented by deps.Deps.
+type TemplatesProvider interface {
+ Tmpl() TemplateHandler
+ TextTmpl() TemplateParseFinder
+}
+
// WithInfo wraps the info in a template.
func WithInfo(templ Template, info Info) Template {
if manager, ok := info.(InfoManager); ok {
diff --git a/tpl/templates/templates.go b/tpl/templates/templates.go
index 44d397e68..80eb2d378 100644
--- a/tpl/templates/templates.go
+++ b/tpl/templates/templates.go
@@ -34,7 +34,7 @@ type Namespace struct {
// Note that this is the Unix-styled relative path including filename suffix,
// e.g. partials/header.html
func (ns *Namespace) Exists(name string) bool {
- _, found := ns.deps.Tmpl.Lookup(name)
+ _, found := ns.deps.Tmpl().Lookup(name)
return found
}
diff --git a/tpl/tplimpl/shortcodes.go b/tpl/tplimpl/shortcodes.go
index abef11e1e..cc4d99491 100644
--- a/tpl/tplimpl/shortcodes.go
+++ b/tpl/tplimpl/shortcodes.go
@@ -32,8 +32,7 @@ type shortcodeVariant struct {
// A slice of length numTemplateVariants.
variants []string
- info tpl.Info
- templ tpl.Template
+ ts *templateState
}
type shortcodeTemplates struct {
diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go
index 2d2a63cf9..d0c656a2e 100644
--- a/tpl/tplimpl/template.go
+++ b/tpl/tplimpl/template.go
@@ -14,59 +14,44 @@
package tplimpl
import (
- "fmt"
"io"
+ "os"
+ "path/filepath"
"reflect"
"regexp"
+ "strings"
+ "sync"
"time"
- "github.com/gohugoio/hugo/hugofs/files"
-
- "github.com/gohugoio/hugo/identity"
+ "github.com/gohugoio/hugo/common/types"
- "github.com/gohugoio/hugo/common/herrors"
-
- "strings"
+ "github.com/gohugoio/hugo/helpers"
- template "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"
+ "github.com/gohugoio/hugo/output"
- texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
+ "github.com/gohugoio/hugo/deps"
+ "github.com/spf13/afero"
+ "github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/hugofs"
- "github.com/gohugoio/hugo/tpl/tplimpl/embedded"
+ "github.com/gohugoio/hugo/hugofs/files"
"github.com/pkg/errors"
- "os"
-
- "github.com/gohugoio/hugo/output"
+ "github.com/gohugoio/hugo/tpl/tplimpl/embedded"
- "path/filepath"
- "sync"
+ htmltemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"
+ texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
- "github.com/gohugoio/hugo/deps"
- "github.com/gohugoio/hugo/helpers"
+ "github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/tpl"
- "github.com/spf13/afero"
)
const (
textTmplNamePrefix = "_text/"
-)
-var (
- _ tpl.TemplateManager = (*templateHandler)(nil)
- _ tpl.TemplateHandler = (*templateHandler)(nil)
- _ tpl.TemplateDebugger = (*templateHandler)(nil)
- _ tpl.TemplateFuncGetter = (*templateHandler)(nil)
- _ tpl.TemplateFinder = (*htmlTemplates)(nil)
- _ tpl.TemplateFinder = (*textTemplates)(nil)
- _ templateLoader = (*htmlTemplates)(nil)
- _ templateLoader = (*textTemplates)(nil)
-)
-
-const (
shortcodesPathPrefix = "shortcodes/"
internalPathPrefix = "_internal/"
+ baseFileBase = "baseof"
)
// The identifiers may be truncated in the log, e.g.
@@ -77,286 +62,273 @@ var embeddedTemplatesAliases = map[string][]string{
"shortcodes/twitter.html": {"shortcodes/tweet.html"},
}
-const baseFileBase = "baseof"
+var (
+ _ tpl.TemplateManager = (*templateExec)(nil)
+ _ tpl.TemplateHandler = (*templateExec)(nil)
+ _ tpl.TemplateFuncGetter = (*templateExec)(nil)
+ _ tpl.TemplateFinder = (*templateExec)(nil)
-func newTemplateAdapter(deps *deps.Deps) *templateHandler {
+ _ tpl.Template = (*templateState)(nil)
+ _ tpl.Info = (*templateState)(nil)
+)
- common := &templatesCommon{
- nameBaseTemplateName: make(map[string]string),
- transformNotFound: make(map[string]bool),
- identityNotFound: make(map[string][]identity.Manager),
- }
+var defineRe = regexp.MustCompile(`{{-?\s?define`)
- htmlT := &htmlTemplates{
- t: template.New(""),
- overlays: make(map[string]*template.Template),
- templatesCommon: common,
- }
+func newIdentity(name string) identity.Manager {
+ return identity.NewManager(identity.NewPathIdentity(files.ComponentFolderLayouts, name))
+}
- textT := &textTemplates{
- textTemplate: &textTemplate{t: texttemplate.New("")},
- standalone: &textTemplate{t: texttemplate.New("")},
- overlays: make(map[string]*texttemplate.Template),
- templatesCommon: common,
+func newStandaloneTextTemplate(funcs map[string]interface{}) tpl.TemplateParseFinder {
+ return &textTemplateWrapperWithLock{
+ RWMutex: &sync.RWMutex{},
+ Template: texttemplate.New("").Funcs(funcs),
}
+}
- h := &templateHandler{
- Deps: deps,
- layoutsFs: deps.BaseFs.Layouts.Fs,
- templateHandlerCommon: &templateHandlerCommon{
- shortcodes: make(map[string]*shortcodeTemplates),
- templateInfo: make(map[string]tpl.Info),
- templateInfoTree: make(map[string]*templateInfoTree),
- html: htmlT,
- text: textT,
- },
+func newTemplateExec(d *deps.Deps) (*templateExec, error) {
+ exec, funcs := newTemplateExecuter(d)
+ funcMap := make(map[string]interface{})
+ for k, v := range funcs {
+ funcMap[k] = v.Interface()
}
- textT.textTemplate.templates = textT
- textT.standalone.templates = textT
- common.handler = h
-
- return h
-
-}
-
-type htmlTemplates struct {
- *templatesCommon
+ h := &templateHandler{
+ nameBaseTemplateName: make(map[string]string),
+ transformNotFound: make(map[string]*templateState),
+ identityNotFound: make(map[string][]identity.Manager),
- t *template.Template
+ shortcodes: make(map[string]*shortcodeTemplates),
+ templateInfo: make(map[string]tpl.Info),
+ baseof: make(map[string]templateInfo),
+ needsBaseof: make(map[string]templateInfo),
- // 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
+ main: newTemplateNamespace(funcMap, false),
- // 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
-}
+ Deps: d,
+ layoutHandler: output.NewLayoutHandler(),
+ layoutsFs: d.BaseFs.Layouts.Fs,
+ layoutTemplateCache: make(map[layoutCacheKey]tpl.Template),
+ }
-func (t *htmlTemplates) Lookup(name string) (tpl.Template, bool) {
- templ := t.lookup(name)
- if templ == nil {
- return nil, false
+ if err := h.loadEmbedded(); err != nil {
+ return nil, err
}
- return templ, true
-}
+ if err := h.loadTemplates(); err != nil {
+ return nil, err
+ }
-func (t *htmlTemplates) LookupVariant(name string, variants tpl.TemplateVariants) (tpl.Template, bool, bool) {
- return t.handler.LookupVariant(name, variants)
-}
+ e := &templateExec{
+ d: d,
+ executor: exec,
+ funcs: funcs,
+ templateHandler: h,
+ }
-func (t *htmlTemplates) addLateTemplate(name, tpl string) error {
- _, err := t.addTemplateIn(t.clone, name, tpl)
- return err
-}
+ d.SetTmpl(e)
+ d.SetTextTmpl(newStandaloneTextTemplate(funcMap))
-func (t *htmlTemplates) addTemplate(name, tpl string) (*templateContext, error) {
- return t.addTemplateIn(t.t, name, tpl)
-}
+ if d.WithTemplate != nil {
+ if err := d.WithTemplate(e); err != nil {
+ return nil, err
-func (t *htmlTemplates) addTemplateIn(tt *template.Template, name, templstr string) (*templateContext, error) {
- templ, err := tt.New(name).Parse(templstr)
- if err != nil {
- return nil, err
+ }
}
- typ := resolveTemplateType(name)
+ return e, nil
+}
- c, err := t.handler.applyTemplateTransformersToHMLTTemplate(typ, templ)
- if err != nil {
- return nil, err
+func newTemplateNamespace(funcs map[string]interface{}, lock bool) *templateNamespace {
+ var mu *sync.RWMutex
+ if lock {
+ mu = &sync.RWMutex{}
}
- for k := range c.templateNotFound {
- t.transformNotFound[k] = true
- t.identityNotFound[k] = append(t.identityNotFound[k], c.id)
+ return &templateNamespace{
+ prototypeHTML: htmltemplate.New("").Funcs(funcs),
+ prototypeText: texttemplate.New("").Funcs(funcs),
+ templateStateMap: &templateStateMap{
+ mu: mu,
+ templates: make(map[string]*templateState),
+ },
}
+}
- for k := range c.identityNotFound {
- t.identityNotFound[k] = append(t.identityNotFound[k], c.id)
+func newTemplateState(templ tpl.Template, info templateInfo) *templateState {
+ return &templateState{
+ info: info,
+ typ: info.resolveType(),
+ Template: templ,
+ Manager: newIdentity(info.name),
+ parseInfo: tpl.DefaultParseInfo,
}
-
- return c, nil
}
-func (t *htmlTemplates) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (templateInfo, error)) error {
+type layoutCacheKey struct {
+ d output.LayoutDescriptor
+ f string
+}
- masterTpl := t.lookup(masterFilename)
+type templateExec struct {
+ d *deps.Deps
+ executor texttemplate.Executer
+ funcs map[string]reflect.Value
- if masterTpl == nil {
- templ, err := onMissing(masterFilename)
- if err != nil {
- return err
- }
+ *templateHandler
+}
- masterTpl, err = t.t.New(overlayFilename).Parse(templ.template)
- if err != nil {
- return templ.errWithFileContext("parse master failed", err)
- }
- }
+func (t templateExec) Clone(d *deps.Deps) *templateExec {
+ exec, funcs := newTemplateExecuter(d)
+ t.executor = exec
+ t.funcs = funcs
+ t.d = d
+ return &t
+}
- templ, err := onMissing(overlayFilename)
- if err != nil {
- return err
+func (t *templateExec) Execute(templ tpl.Template, wr io.Writer, data interface{}) error {
+ if rlocker, ok := templ.(types.RLocker); ok {
+ rlocker.RLock()
+ defer rlocker.RUnlock()
}
-
- overlayTpl, err := template.Must(masterTpl.Clone()).Parse(templ.template)
- if err != nil {
- return templ.errWithFileContext("parse failed", err)
+ if t.Metrics != nil {
+ defer t.Metrics.MeasureSince(templ.Name(), time.Now())
}
- // The extra lookup is a workaround, see
- // * https://github.com/golang/go/issues/16101
- // * https://github.com/gohugoio/hugo/issues/2549
- overlayTpl = overlayTpl.Lookup(overlayTpl.Name())
- if _, err := t.handler.applyTemplateTransformersToHMLTTemplate(templateUndefined, overlayTpl); err != nil {
- return err
+ execErr := t.executor.Execute(templ, wr, data)
+ if execErr != nil {
+ execErr = t.addFileContext(templ, execErr)
}
+ return execErr
+}
- t.overlays[name] = overlayTpl
- t.nameBaseTemplateName[name] = masterFilename
-
- return err
-
+func (t *templateExec) GetFunc(name string) (reflect.Value, bool) {
+ v, found := t.funcs[name]
+ return v, found
}
-func (t *htmlTemplates) lookup(name string) *template.Template {
- // Need to check in the overlay registry first as it will also be found below.
- if t.overlays != nil {
- if templ, ok := t.overlays[name]; ok {
- return templ
+func (t *templateExec) MarkReady() error {
+ var err error
+ t.readyInit.Do(func() {
+ // We only need the clones if base templates are in use.
+ if len(t.needsBaseof) > 0 {
+ err = t.main.createPrototypes()
}
- }
+ })
- if templ := t.t.Lookup(name); templ != nil {
- return templ
+ if err != nil {
+ return err
}
- if t.clone != nil {
- return t.clone.Lookup(name)
+ if t.Deps.BuildFlags.HasLateTemplate.Load() {
+ // This costs memory, so try to avoid it if we don't have to.
+ // The late templates are used to handle HTML in files in /content
+ // without front matter.
+ t.readyLateInit.Do(func() {
+ t.late = t.main.Clone(true)
+ t.late.createPrototypes()
+ })
}
return nil
}
-func (t htmlTemplates) withNewHandler(h *templateHandler) *htmlTemplates {
- t.templatesCommon = t.templatesCommon.withNewHandler(h)
- return &t
-}
-
-type nopLookupVariant int
-
-func (l nopLookupVariant) LookupVariant(name string, variants tpl.TemplateVariants) (tpl.Template, bool, bool) {
- return nil, false, false
-}
-
-// templateHandler holds the templates in play.
-// It implements the templateLoader and tpl.TemplateHandler interfaces.
-// There is one templateHandler created per Site.
type templateHandler struct {
- ready bool
+ main *templateNamespace
+ needsBaseof map[string]templateInfo
+ baseof map[string]templateInfo
- executor texttemplate.Executer
- funcs map[string]reflect.Value
+ late *templateNamespace // Templates added after main has started executing.
+
+ readyInit sync.Once
+ readyLateInit sync.Once
// This is the filesystem to load the templates from. All the templates are
// stored in the root of this filesystem.
layoutsFs afero.Fs
+ layoutHandler *output.LayoutHandler
+
+ layoutTemplateCache map[layoutCacheKey]tpl.Template
+ layoutTemplateCacheMu sync.RWMutex
+
*deps.Deps
- *templateHandlerCommon
+ // Used to get proper filenames in errors
+ nameBaseTemplateName map[string]string
+
+ // Holds name and source of template definitions not found during the first
+ // AST transformation pass.
+ transformNotFound map[string]*templateState
+
+ // Holds identities of templates not found during first pass.
+ identityNotFound map[string][]identity.Manager
+
+ // shortcodes maps shortcode name to template variants
+ // (language, output format etc.) of that shortcode.
+ shortcodes map[string]*shortcodeTemplates
+
+ // templateInfo maps template name to some additional information about that template.
+ // Note that for shortcodes that same information is embedded in the
+ // shortcodeTemplates type.
+ templateInfo map[string]tpl.Info
}
-// AddLateTemplate is used to add a template late, i.e. after the
+// AddLateTemplate is used to add a template after the
// regular templates have started its execution.
+// These are currently "pure HTML content files".
func (t *templateHandler) AddLateTemplate(name, tpl string) error {
- h := t.getTemplateHandler(name)
- if err := h.addLateTemplate(name, tpl); err != nil {
- return err
- }
- return nil
+ _, err := t.late.parse(t.newTemplateInfo(name, tpl))
+ return err
}
// AddTemplate parses and adds a template to the collection.
// Templates with name prefixed with "_text" will be handled as plain
// text templates.
-// TODO(bep) clean up these addTemplate variants
func (t *templateHandler) AddTemplate(name, tpl string) error {
- h := t.getTemplateHandler(name)
- _, err := h.addTemplate(name, tpl)
- if err != nil {
- return err
+ templ, err := t.addTemplateTo(t.newTemplateInfo(name, tpl), t.main)
+ if err == nil {
+ t.applyTemplateTransformers(t.main, templ)
}
- return nil
-}
-
-func (t *templateHandler) Debug() {
- fmt.Println("HTML templates:\n", t.html.t.DefinedTemplates())
- fmt.Println("\n\nText templates:\n", t.text.t.DefinedTemplates())
+ return err
}
-func (t *templateHandler) Execute(templ tpl.Template, wr io.Writer, data interface{}) error {
- if t.Metrics != nil {
- defer t.Metrics.MeasureSince(templ.Name(), time.Now())
+func (t *templateHandler) Lookup(name string) (tpl.Template, bool) {
+ templ, found := t.main.Lookup(name)
+ if found {
+ return templ, true
}
- execErr := t.executor.Execute(templ, wr, data)
- if execErr != nil {
- execErr = t.addFileContext(templ.Name(), execErr)
+ if t.late != nil {
+ return t.late.Lookup(name)
}
- return execErr
-
+ return nil, false
}
-func (t *templateHandler) GetFunc(name string) (reflect.Value, bool) {
- v, found := t.funcs[name]
- return v, found
-
-}
-
-// 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) error {
- return t.loadTemplates(prefix)
-
-}
-
-// 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) {
-
- if strings.HasPrefix(name, textTmplNamePrefix) {
- // The caller has explicitly asked for a text template, so only look
- // in the text template collection.
- // The templates are stored without the prefix identificator.
- name = strings.TrimPrefix(name, textTmplNamePrefix)
-
- return t.applyTemplateInfo(t.text.Lookup(name))
+func (t *templateHandler) LookupLayout(d output.LayoutDescriptor, f output.Format) (tpl.Template, bool, error) {
+ key := layoutCacheKey{d, f.Name}
+ t.layoutTemplateCacheMu.RLock()
+ if cacheVal, found := t.layoutTemplateCache[key]; found {
+ t.layoutTemplateCacheMu.RUnlock()
+ return cacheVal, true, nil
}
+ t.layoutTemplateCacheMu.RUnlock()
- // Look in both
- if te, found := t.html.Lookup(name); found {
- return t.applyTemplateInfo(te, true)
- }
+ t.layoutTemplateCacheMu.Lock()
+ defer t.layoutTemplateCacheMu.Unlock()
- return t.applyTemplateInfo(t.text.Lookup(name))
+ templ, found, err := t.findLayout(d, f)
+ if err == nil && found {
+ t.layoutTemplateCache[key] = templ
+ return templ, true, nil
+ }
+ return nil, false, err
}
// This currently only applies to shortcodes and what we get here is the
// shortcode name.
func (t *templateHandler) LookupVariant(name string, variants tpl.TemplateVariants) (tpl.Template, bool, bool) {
- if !t.ready {
- panic("handler not ready")
- }
name = templateBaseName(templateShortcode, name)
s, found := t.shortcodes[name]
if !found {
@@ -370,131 +342,142 @@ func (t *templateHandler) LookupVariant(name string, variants tpl.TemplateVarian
more := len(s.variants) > 1
- return tpl.WithInfo(sv.templ, sv.info), true, more
+ return sv.ts, true, more
}
-// markReady marks the templates as "ready for execution". No changes allowed
-// after this is set.
-func (t *templateHandler) markReady() error {
- defer func() {
- t.ready = true
- }()
-
- if err := t.postTransform(); err != nil {
- return err
+func (t *templateHandler) HasTemplate(name string) bool {
+ if _, found := t.baseof[name]; found {
+ return true
}
+ _, found := t.Lookup(name)
+ return found
+}
- 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())
- }
+func (t *templateHandler) findLayout(d output.LayoutDescriptor, f output.Format) (tpl.Template, bool, error) {
+ layouts, _ := t.layoutHandler.For(d, f)
+ for _, name := range layouts {
+ templ, found := t.main.Lookup(name)
+ if found {
+ return templ, true, nil
+ }
- return nil
-}
+ overlay, found := t.needsBaseof[name]
+
+ if !found {
+ continue
+ }
+
+ d.Baseof = true
+ baseLayouts, _ := t.layoutHandler.For(d, f)
+ var base templateInfo
+ found = false
+ for _, l := range baseLayouts {
+ base, found = t.baseof[l]
+ if found {
+ break
+ }
+ }
+
+ if !found {
+ return nil, false, errors.Errorf("no baseof layout found for %q:", name)
+ }
+
+ templ, err := t.applyBaseTemplate(overlay, base)
+ if err != nil {
+ return nil, false, err
+ }
+
+ ts := newTemplateState(templ, overlay)
+ ts.baseInfo = base
+
+ // Add the base identity to detect changes
+ ts.Add(identity.NewPathIdentity(files.ComponentFolderLayouts, base.name))
+
+ t.applyTemplateTransformers(t.main, ts)
+
+ return ts, true, nil
-// RebuildClone rebuilds the cloned templates. Used for live-reloads.
-func (t *templateHandler) RebuildClone() {
- 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())
}
+
+ return nil, false, nil
}
-func (h *templateHandler) initTemplateExecuter() {
- exec, funcs := newTemplateExecuter(h.Deps)
- h.executor = exec
- h.funcs = funcs
- funcMap := make(map[string]interface{})
- for k, v := range funcs {
- funcMap[k] = v.Interface()
+func (t *templateHandler) findTemplate(name string) *templateState {
+ if templ, found := t.Lookup(name); found {
+ return templ.(*templateState)
}