diff options
Diffstat (limited to 'tpl')
-rw-r--r-- | tpl/collections/apply.go | 2 | ||||
-rw-r--r-- | tpl/collections/apply_test.go | 14 | ||||
-rw-r--r-- | tpl/partials/partials.go | 6 | ||||
-rw-r--r-- | tpl/resources/resources.go | 3 | ||||
-rw-r--r-- | tpl/template.go | 12 | ||||
-rw-r--r-- | tpl/templates/templates.go | 2 | ||||
-rw-r--r-- | tpl/tplimpl/shortcodes.go | 3 | ||||
-rw-r--r-- | tpl/tplimpl/template.go | 1163 | ||||
-rw-r--r-- | tpl/tplimpl/templateProvider.go | 27 | ||||
-rw-r--r-- | tpl/tplimpl/template_ast_transformers.go | 130 | ||||
-rw-r--r-- | tpl/tplimpl/template_ast_transformers_test.go | 78 | ||||
-rw-r--r-- | tpl/tplimpl/template_errors.go | 6 | ||||
-rw-r--r-- | tpl/tplimpl/template_funcs_test.go | 4 | ||||
-rw-r--r-- | tpl/tplimpl/template_info_test.go | 7 |
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) } |