diff options
Diffstat (limited to 'tpl/tplimpl/template.go')
-rw-r--r-- | tpl/tplimpl/template.go | 1228 |
1 files changed, 621 insertions, 607 deletions
diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go index 0feb3a0de..dd8de9067 100644 --- a/tpl/tplimpl/template.go +++ b/tpl/tplimpl/template.go @@ -15,6 +15,12 @@ package tplimpl import ( "fmt" + "io" + "reflect" + "regexp" + "time" + + "github.com/gohugoio/hugo/common/herrors" "strings" @@ -45,119 +51,196 @@ const ( ) var ( - _ tpl.TemplateHandler = (*templateHandler)(nil) - _ tpl.TemplateDebugger = (*templateHandler)(nil) - _ tpl.TemplateFuncsGetter = (*templateHandler)(nil) - _ tpl.TemplateTestMocker = (*templateHandler)(nil) - _ tpl.TemplateFinder = (*htmlTemplates)(nil) - _ tpl.TemplateFinder = (*textTemplates)(nil) - _ templateLoader = (*htmlTemplates)(nil) - _ templateLoader = (*textTemplates)(nil) - _ templateFuncsterTemplater = (*htmlTemplates)(nil) - _ templateFuncsterTemplater = (*textTemplates)(nil) + _ 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) ) -type templateErr struct { - name string - err error -} +const ( + shortcodesPathPrefix = "shortcodes/" + internalPathPrefix = "_internal/" +) -type templateLoader interface { - handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (templateInfo, error)) error - addTemplate(name, tpl string) (*templateContext, error) - addLateTemplate(name, tpl string) error -} +// 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})?\>:`) -type templateFuncsterTemplater interface { - templateFuncsterSetter - tpl.TemplateFinder - setFuncs(funcMap map[string]interface{}) +var embeddedTemplatesAliases = map[string][]string{ + "shortcodes/twitter.html": {"shortcodes/tweet.html"}, } -type templateFuncsterSetter interface { - setTemplateFuncster(f *templateFuncster) -} +const baseFileBase = "baseof" -// templateHandler holds the templates in play. -// It implements the templateLoader and tpl.TemplateHandler interfaces. -type templateHandler struct { - mu sync.Mutex +func newTemplateAdapter(deps *deps.Deps) *templateHandler { - // shortcodes maps shortcode name to template variants - // (language, output format etc.) of that shortcode. - shortcodes map[string]*shortcodeTemplates + common := &templatesCommon{ + nameBaseTemplateName: make(map[string]string), + transformNotFound: make(map[string]bool), + } - // 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 + htmlT := &htmlTemplates{ + t: template.New(""), + overlays: make(map[string]*template.Template), + templatesCommon: common, + } - // text holds all the pure text templates. - text *textTemplates - html *htmlTemplates + textT := &textTemplates{ + textTemplate: &textTemplate{t: texttemplate.New("")}, + standalone: &textTemplate{t: texttemplate.New("")}, + overlays: make(map[string]*texttemplate.Template), + templatesCommon: common, + } + + h := &templateHandler{ + Deps: deps, + layoutsFs: deps.BaseFs.Layouts.Fs, + templateHandlerCommon: &templateHandlerCommon{ + shortcodes: make(map[string]*shortcodeTemplates), + templateInfo: make(map[string]tpl.Info), + html: htmlT, + text: textT, + }, + } - errors []*templateErr + common.handler = h - // This is the filesystem to load the templates from. All the templates are - // stored in the root of this filesystem. - layoutsFs afero.Fs + return h - *deps.Deps } -const ( - shortcodesPathPrefix = "shortcodes/" - internalPathPrefix = "_internal/" -) +type htmlTemplates struct { + *templatesCommon -// resolves _internal/shortcodes/param.html => param.html etc. -func templateBaseName(typ templateType, name string) string { - name = strings.TrimPrefix(name, internalPathPrefix) - switch typ { - case templateShortcode: - return strings.TrimPrefix(name, shortcodesPathPrefix) - default: - panic("not implemented") + t *template.Template + + // 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 + + // 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 +} + +func (t *htmlTemplates) Lookup(name string) (tpl.Template, bool) { + templ := t.lookup(name) + if templ == nil { + return nil, false } + return templ, true } -func (t *templateHandler) addShortcodeVariant(name string, info tpl.Info, templ tpl.Template) { - base := templateBaseName(templateShortcode, name) +func (t *htmlTemplates) LookupVariant(name string, variants tpl.TemplateVariants) (tpl.Template, bool, bool) { + return t.handler.LookupVariant(name, variants) +} - shortcodename, variants := templateNameAndVariants(base) +func (t *htmlTemplates) addLateTemplate(name, tpl string) error { + _, err := t.addTemplateIn(t.clone, name, tpl) + return err +} - templs, found := t.shortcodes[shortcodename] - if !found { - templs = &shortcodeTemplates{} - t.shortcodes[shortcodename] = templs +func (t *htmlTemplates) addTemplate(name, tpl string) (*templateContext, error) { + return t.addTemplateIn(t.t, name, tpl) +} + +func (t *htmlTemplates) addTemplateIn(tt *template.Template, name, tpl string) (*templateContext, error) { + templ, err := tt.New(name).Parse(tpl) + if err != nil { + return nil, err } - sv := shortcodeVariant{variants: variants, info: info, templ: templ} + typ := resolveTemplateType(name) - i := templs.indexOf(variants) + c, err := applyTemplateTransformersToHMLTTemplate(typ, templ) + if err != nil { + return nil, err + } - if i != -1 { - // Only replace if it's an override of an internal template. - if !isInternal(name) { - templs.variants[i] = sv - } + for k := range c.notFound { + t.transformNotFound[k] = true + } + + if typ == templateShortcode { + t.handler.addShortcodeVariant(name, c.Info, templ) } else { - templs.variants = append(templs.variants, sv) + t.handler.templateInfo[name] = c.Info } + + return c, nil } -func (t *templateHandler) wrapTextTemplate(tt *textTemplate) tpl.TemplateParseFinder { - return struct { - tpl.TemplateParser - tpl.TemplateLookup - tpl.TemplateLookupVariant - }{ - tt, - tt, - new(nopLookupVariant), +func (t *htmlTemplates) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (templateInfo, error)) error { + + masterTpl := t.lookup(masterFilename) + + if masterTpl == nil { + templ, err := onMissing(masterFilename) + if err != nil { + return err + } + + masterTpl, err = t.t.New(overlayFilename).Parse(templ.template) + if err != nil { + return templ.errWithFileContext("parse master failed", err) + } + } + + templ, err := onMissing(overlayFilename) + if err != nil { + return err + } + + overlayTpl, err := template.Must(masterTpl.Clone()).Parse(templ.template) + if err != nil { + return templ.errWithFileContext("parse failed", err) + } + + // 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 := applyTemplateTransformersToHMLTTemplate(templateUndefined, overlayTpl); err != nil { + return err + } + + t.overlays[name] = overlayTpl + t.nameBaseTemplateName[name] = masterFilename + + return err + +} + +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 + } + } + + if templ := t.t.Lookup(name); templ != nil { + return templ + } + + if t.clone != nil { + return t.clone.Lookup(name) } + return nil +} + +func (t htmlTemplates) withNewHandler(h *templateHandler) *htmlTemplates { + t.templatesCommon = t.templatesCommon.withNewHandler(h) + return &t } type nopLookupVariant int @@ -166,15 +249,81 @@ func (l nopLookupVariant) LookupVariant(name string, variants tpl.TemplateVarian 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 { + executor texttemplate.Executer + funcs map[string]reflect.Value + + // This is the filesystem to load the templates from. All the templates are + // stored in the root of this filesystem. + layoutsFs afero.Fs + + *deps.Deps + + *templateHandlerCommon +} + +// 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 { + 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. +// 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 + } + 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()) } +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()) + } + + execErr := t.executor.Execute(templ, wr, data) + if execErr != nil { + execErr = t.addFileContext(templ.Name(), execErr) + } + + return execErr + +} + +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. @@ -193,18 +342,6 @@ func (t *templateHandler) Lookup(name string) (tpl.Template, bool) { } -func (t *templateHandler) applyTemplateInfo(templ tpl.Template, found bool) (tpl.Template, bool) { - if adapter, ok := templ.(*tpl.TemplateAdapter); ok { - if adapter.Info.IsZero() { - if info, found := t.templateInfo[templ.Name()]; found { - adapter.Info = info - } - } - } - - return templ, found -} - // 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) { @@ -221,382 +358,349 @@ func (t *templateHandler) LookupVariant(name string, variants tpl.TemplateVarian more := len(s.variants) > 1 - return &tpl.TemplateAdapter{ - Template: sv.templ, - Info: sv.info, - Metrics: t.Deps.Metrics, - Fs: t.layoutsFs, - NameBaseTemplateName: t.html.nameBaseTemplateName}, true, more + return &tpl.TemplateInfo{ + Template: sv.templ, + Info: sv.info, + }, true, more } -func (t *textTemplates) LookupVariant(name string, variants tpl.TemplateVariants) (tpl.Template, bool, bool) { - return t.handler.LookupVariant(name, variants) -} +// 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() error { + if err := t.postTransform(); err != nil { + return err + } -func (t *htmlTemplates) LookupVariant(name string, variants tpl.TemplateVariants) (tpl.Template, bool, bool) { - return t.handler.LookupVariant(name, variants) + 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()) + } + + return nil } -func (t *templateHandler) lookupTemplate(in interface{}) tpl.Template { - switch templ := in.(type) { - case *texttemplate.Template: - return t.text.lookup(templ.Name()) - case *template.Template: - return t.html.lookup(templ.Name()) +// 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()) } - - panic(fmt.Sprintf("%T is not a template", in)) } -func (t *templateHandler) setFuncMapInTemplate(in interface{}, funcs map[string]interface{}) { - switch templ := in.(type) { - case *texttemplate.Template: - templ.Funcs(funcs) - return - case *template.Template: - templ.Funcs(funcs) - return +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() } - panic(fmt.Sprintf("%T is not a template", in)) + // Note that these funcs are not the ones getting called + // on execution, but they are needed at parse time. + h.text.textTemplate.t.Funcs(funcMap) + h.text.standalone.t.Funcs(funcMap) + h.html.t.Funcs(funcMap) } -func (t *templateHandler) clone(d *deps.Deps) *templateHandler { - c := &templateHandler{ - Deps: d, - layoutsFs: d.BaseFs.Layouts.Fs, - shortcodes: make(map[string]*shortcodeTemplates), - templateInfo: t.templateInfo, - 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())}, - standalone: &textTemplate{t: texttemplate.New("")}, - overlays: make(map[string]*texttemplate.Template), templatesCommon: t.text.templatesCommon}, - errors: make([]*templateErr, 0), - } - - for k, v := range t.shortcodes { - other := *v - variantsc := make([]shortcodeVariant, len(v.variants)) - for i, variant := range v.variants { - variantsc[i] = shortcodeVariant{ - info: variant.info, - variants: variant.variants, - templ: c.lookupTemplate(variant.templ), - } - } - other.variants = variantsc - c.shortcodes[k] = &other +func (t *templateHandler) getTemplateHandler(name string) templateLoader { + if strings.HasPrefix(name, textTmplNamePrefix) { + return t.text } + return t.html +} - d.Tmpl = c - d.TextTmpl = c.wrapTextTemplate(c.text.standalone) +func (t *templateHandler) addFileContext(name string, inerr error) error { + if strings.HasPrefix(name, "_internal") { + return inerr + } - c.initFuncs() + f, realFilename, err := t.fileAndFilename(name) + if err != nil { + return inerr - for k, v := range t.html.overlays { - vc := template.Must(v.Clone()) - // The extra lookup is a workaround, see - // * https://github.com/golang/go/issues/16101 - // * https://github.com/gohugoio/hugo/issues/2549 - vc = vc.Lookup(vc.Name()) - vc.Funcs(c.html.funcster.funcMap) - c.html.overlays[k] = vc } + defer f.Close() - for k, v := range t.text.overlays { - vc := texttemplate.Must(v.Clone()) - vc = vc.Lookup(vc.Name()) - vc.Funcs(texttemplate.FuncMap(c.text.funcster.funcMap)) - c.text.overlays[k] = vc - } + master, hasMaster := t.html.nameBaseTemplateName[name] - return c + 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 + } -func newTemplateAdapter(deps *deps.Deps) *templateHandler { - common := &templatesCommon{ - nameBaseTemplateName: make(map[string]string), - transformNotFound: make(map[string]bool), - } + identifiers := t.extractIdentifiers(m.Error.Error()) - htmlT := &htmlTemplates{ - t: template.New(""), - overlays: make(map[string]*template.Template), - templatesCommon: common, - } - textT := &textTemplates{ - textTemplate: &textTemplate{t: texttemplate.New("")}, - standalone: &textTemplate{t: texttemplate.New("")}, - overlays: make(map[string]*texttemplate.Template), - templatesCommon: common, + for _, id := range identifiers { + if strings.Contains(m.Line, id) { + return true + } + } + return false } - h := &templateHandler{ - Deps: deps, - layoutsFs: deps.BaseFs.Layouts.Fs, - shortcodes: make(map[string]*shortcodeTemplates), - templateInfo: make(map[string]tpl.Info), - html: htmlT, - text: textT, - errors: make([]*templateErr, 0), + + fe, ok := herrors.WithFileContext(ferr, realFilename, f, lineMatcher) + if ok || !hasMaster { + return fe } - common.handler = h + // Try the base template if relevant + f, realFilename, err = t.fileAndFilename(master) + if err != nil { + return err + } + defer f.Close() - return h + fe, ok = herrors.WithFileContext(ferr, realFilename, f, lineMatcher) -} + if !ok { + // Return the most specific. + return ferr -// Shared by both HTML and text templates. -type templatesCommon struct { - handler *templateHandler - funcster *templateFuncster + } + return fe - // Used to get proper filenames in errors - nameBaseTemplateName map[string]string +} - // Holds names of the templates not found during the first AST transformation - // pass. - transformNotFound map[string]bool +func (t *templateHandler) addInternalTemplate(name, tpl string) error { + return t.AddTemplate("_internal/"+name, tpl) } -type htmlTemplates struct { - mu sync.RWMutex - *templatesCommon +func (t *templateHandler) addShortcodeVariant(name string, info tpl.Info, templ tpl.Template) { + base := templateBaseName(templateShortcode, name) - t *template.Template + shortcodename, variants := templateNameAndVariants(base) - // 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 + templs, found := t.shortcodes[shortcodename] + if !found { + templs = &shortcodeTemplates{} + t.shortcodes[shortcodename] = templs + } - // 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 -} + sv := shortcodeVariant{variants: variants, info: info, templ: templ} -func (t *htmlTemplates) setTemplateFuncster(f *templateFuncster) { - t.funcster = f -} + i := templs.indexOf(variants) -func (t *htmlTemplates) Lookup(name string) (tpl.Template, bool) { - templ := t.lookup(name) - if templ == nil { - return nil, false + if i != -1 { + // Only replace if it's an override of an internal template. + if !isInternal(name) { + templs.variants[i] = sv + } + } else { + templs.variants = append(templs.variants, sv) } - - 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 { - t.mu.RLock() - defer t.mu.RUnlock() +func (t *templateHandler) addTemplateFile(name, baseTemplatePath, path string) error { + t.checkState() - // 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 + t.Log.DEBUG.Printf("Add template file: name %q, baseTemplatePath %q, path %q", name, baseTemplatePath, path) + + getTemplate := func(filename string) (templateInfo, error) { + fs := t.Layouts.Fs + b, err := afero.ReadFile(fs, filename) + if err != nil { + return templateInfo{filename: filename, fs: fs}, err } - } - if templ := t.t.Lookup(name); templ != nil { - return templ - } + s := removeLeadingBOM(string(b)) - if t.clone != nil { - return t.clone.Lookup(name) + realFilename := filename + if fi, err := fs.Stat(filename); err == nil { + if fim, ok := fi.(hugofs.FileMetaInfo); ok { + realFilename = fim.Meta().Filename() + } + } + + return templateInfo{template: s, filename: filename, realFilename: realFilename, fs: fs}, nil } - return nil -} + // get the suffix and switch on that + ext := filepath.Ext(path) + switch ext { + case ".amber": + helpers.Deprecated("Amber templates are no longer supported.", "Use Go templates or a Hugo version <= 0.60.", true) + return nil + case ".ace": + helpers.Deprecated("ACE templates are no longer supported.", "Use Go templates or a Hugo version <= 0.60.", true) + return nil + default: -func (t *textTemplates) setTemplateFuncster(f *templateFuncster) { - t.funcster = f -} + if baseTemplatePath != "" { + return t.handleMaster(name, path, baseTemplatePath, getTemplate) + } -type textTemplates struct { - *templatesCommon - *textTemplate - standalone *textTemplate - clone *texttemplate.Template - cloneClone *texttemplate.Template + templ, err := getTemplate(path) - overlays map[string]*texttemplate.Template -} + if err != nil { + return err + } -func (t *textTemplates) Lookup(name string) (tpl.Template, bool) { - templ := t.lookup(name) - if templ == nil { - return nil, false + err = t.AddTemplate(name, templ.template) + if err != nil { + return templ.errWithFileContext("parse failed", err) + } + return nil } - 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 { - - // 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 *templateHandler) applyTemplateInfo(templ tpl.Template, found bool) (tpl.Template, bool) { + if adapter, ok := templ.(*tpl.TemplateInfo); ok { + if adapter.Info.IsZero() { + if info, found := t.templateInfo[templ.Name()]; found { + adapter.Info = info + } + } + } else if templ != nil { + if info, found := t.templateInfo[templ.Name()]; found { + return &tpl.TemplateInfo{ + Template: templ, + Info: info, + }, true } } - if templ := t.t.Lookup(name); templ != nil { - return templ - } - - if t.clone != nil { - return t.clone.Lookup(name) - } - - return nil + return templ, found } -func (t *templateHandler) setFuncs(funcMap map[string]interface{}) { - t.html.setFuncs(funcMap) - t.text.setFuncs(funcMap) - t.setFuncMapInTemplate(t.text.standalone.t, funcMap) +func (t *templateHandler) checkState() { + if t.html.clone != nil || t.text.clone != nil { + panic("template is cloned and cannot be modfified") + } } -// 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 *templateHandler) clone(d *deps.Deps) *templateHandler { + c := &templateHandler{ + Deps: d, + layoutsFs: d.BaseFs.Layouts.Fs, + } -func (t *templateHandler) GetFuncs() map[string]interface{} { - return t.html.funcster.funcMap -} + c.templateHandlerCommon = t.templateHandlerCommon.withNewHandler(c) + d.Tmpl = c + d.TextTmpl = c.wrapTextTemplate(c.text.standalone) + c.executor, c.funcs = newTemplateExecuter(d) -func (t *htmlTemplates) setFuncs(funcMap map[string]interface{}) { - t.t.Funcs(funcMap) -} + return c -func (t *textTemplates) setFuncs(funcMap map[string]interface{}) { - t.t.Funcs(funcMap) } -// 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) - +func (t *templateHandler) 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 *htmlTemplates) addTemplateIn(tt *template.Template, name, tpl string) (*templateContext, error) { - t.mu.Lock() - defer t.mu.Unlock() +func (t *templateHandler) fileAndFilename(name string) (afero.File, string, error) { + fs := t.layoutsFs + filename := filepath.FromSlash(name) - templ, err := tt.New(name).Parse(tpl) + fi, err := fs.Stat(filename) if err != nil { - return nil, err + return nil, "", err } + fim := fi.(hugofs.FileMetaInfo) + meta := fim.Meta() - typ := resolveTemplateType(name) - - c, err := applyTemplateTransformersToHMLTTemplate(typ, templ) + f, err := meta.Open() if err != nil { - return nil, err + return nil, "", errors.Wrapf(err, "failed to open template file %q:", filename) } - for k := range c.notFound { - t.transformNotFound[k] = true - } - - if typ == templateShortcode { - t.handler.addShortcodeVariant(name, c.Info, templ) - } else { - t.handler.templateInfo[name] = c.Info - } - - return c, nil + return f, meta.Filename(), nil } -func (t *htmlTemplates) addTemplate(name, tpl string) (*templateContext, error) { - return t.addTemplateIn(t.t, name, tpl) -} - -func (t *htmlTemplates) addLateTemplate(name, tpl string) error { - _, err := t.addTemplateIn(t.clone, name, tpl) - return err +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) } -type textTemplate struct { - mu sync.RWMutex - t *texttemplate.Template -} +func (t *templateHandler) loadEmbedded() error { + for _, kv := range embedded.EmbeddedTemplates { + name, templ := kv[0], kv[1] + if err := t.addInternalTemplate(name, templ); err != nil { + return err + } + if aliases, found := embeddedTemplatesAliases[name]; found { + for _, alias := range aliases { + if err := t.addInternalTemplate(alias, templ); err != nil { + return err + } + } -func (t *textTemplate) Parse(name, tpl string) (tpl.Template, error) { - return t.parseIn(t.t, name, tpl) -} + } + } -func (t *textTemplate) Lookup(name string) (tpl.Template, bool) { - t.mu.RLock() - defer t.mu.RUnlock() + return nil - tpl := t.t.Lookup(name) - return tpl, tpl != nil } -func (t *textTemplate) parseIn(tt *texttemplate.Template, name, tpl string) (*texttemplate.Template, error) { - t.mu.Lock() - defer t.mu.Unlock() +func (t *templateHandler) loadTemplates(prefix string) error { - templ, err := tt.New(name).Parse(tpl) - if err != nil { - return nil, err - } |