ref: baa975082c6809c8a02a8109ec3062a2b7d48344
dir: /tpl/tplimpl/template.go/
// Copyright 2019 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 (
	"fmt"
	"html/template"
	"strings"
	texttemplate "text/template"
	"text/template/parse"
	"github.com/gohugoio/hugo/hugofs"
	"github.com/gohugoio/hugo/tpl/tplimpl/embedded"
	"github.com/pkg/errors"
	"github.com/eknkc/amber"
	"os"
	"github.com/gohugoio/hugo/output"
	"path/filepath"
	"sync"
	"github.com/gohugoio/hugo/deps"
	"github.com/gohugoio/hugo/helpers"
	"github.com/gohugoio/hugo/tpl"
	"github.com/spf13/afero"
)
const (
	textTmplNamePrefix = "_text/"
)
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)
)
// Protecting  global map access (Amber)
var amberMu sync.Mutex
type templateErr struct {
	name string
	err  error
}
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
}
type templateFuncsterTemplater interface {
	templateFuncsterSetter
	tpl.TemplateFinder
	setFuncs(funcMap map[string]interface{})
}
type templateFuncsterSetter interface {
	setTemplateFuncster(f *templateFuncster)
}
// templateHandler holds the templates in play.
// It implements the templateLoader and tpl.TemplateHandler interfaces.
type templateHandler struct {
	mu sync.Mutex
	// 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
	// text holds all the pure text templates.
	text *textTemplates
	html *htmlTemplates
	extTextTemplates []*textTemplate
	amberFuncMap template.FuncMap
	errors []*templateErr
	// 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
}
const (
	shortcodesPathPrefix = "shortcodes/"
	internalPathPrefix   = "_internal/"
)
// 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")
	}
}
func (t *templateHandler) addShortcodeVariant(name string, info tpl.Info, templ tpl.Template) {
	base := templateBaseName(templateShortcode, name)
	shortcodename, variants := templateNameAndVariants(base)
	templs, found := t.shortcodes[shortcodename]
	if !found {
		templs = &shortcodeTemplates{}
		t.shortcodes[shortcodename] = templs
	}
	sv := shortcodeVariant{variants: variants, info: info, templ: templ}
	i := templs.indexOf(variants)
	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)
	}
}
// NewTextTemplate provides a text template parser that has all the Hugo
// template funcs etc. built-in.
func (t *templateHandler) NewTextTemplate() tpl.TemplateParseFinder {
	t.mu.Lock()
	defer t.mu.Unlock()
	tt := &textTemplate{t: texttemplate.New("")}
	t.extTextTemplates = append(t.extTextTemplates, tt)
	return struct {
		tpl.TemplateParser
		tpl.TemplateLookup
		tpl.TemplateLookupVariant
	}{
		tt,
		tt,
		new(nopLookupVariant),
	}
}
type nopLookupVariant int
func (l nopLookupVariant) LookupVariant(name string, variants tpl.TemplateVariants) (tpl.Template, bool, bool) {
	return nil, false, false
}
func (t *templateHandler) Debug() {
	fmt.Println("HTML templates:\n", t.html.t.DefinedTemplates())
	fmt.Println("\n\nText templates:\n", t.text.t.DefinedTemplates())
}
// 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))
	}
	// Look in both
	if te, found := t.html.Lookup(name); found {
		return t.applyTemplateInfo(te, true)
	}
	return t.applyTemplateInfo(t.text.Lookup(name))
}
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) {
	name = templateBaseName(templateShortcode, name)
	s, found := t.shortcodes[name]
	if !found {
		return nil, false, false
	}
	sv, found := s.fromVariants(variants)
	if !found {
		return nil, false, false
	}
	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
}
func (t *textTemplates) LookupVariant(name string, variants tpl.TemplateVariants) (tpl.Template, bool, bool) {
	return t.handler.LookupVariant(name, variants)
}
func (t *htmlTemplates) LookupVariant(name string, variants tpl.TemplateVariants) (tpl.Template, bool, bool) {
	return t.handler.LookupVariant(name, variants)
}
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())
	}
	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
	}
	panic(fmt.Sprintf("%T is not a template", in))
}
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())}, 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
	}
	d.Tmpl = c
	c.initFuncs()
	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
	}
	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
	}
	return c
}
func newTemplateAdapter(deps *deps.Deps) *templateHandler {
	common := &templatesCommon{
		nameBaseTemplateName: make(map[string]string),
		transformNotFound:    make(map[string]bool),
	}
	htmlT := &htmlTemplates{
		t:               template.New(""),
		overlays:        make(map[string]*template.Template),
		templatesCommon: common,
	}
	textT := &textTemplates{
		textTemplate:    &textTemplate{t: texttemplate.New("")},
		overlays:        make(map[string]*texttemplate.Template),
		templatesCommon: common,
	}
	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),
	}
	common.handler = h
	return h
}
// Shared by both HTML and text templates.
type templatesCommon struct {
	handler  *templateHandler
	funcster *templateFuncster
	// 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
}
type htmlTemplates struct {
	mu sync.RWMutex
	*templatesCommon
	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) setTemplateFuncster(f *templateFuncster) {
	t.funcster = f
}
func (t *htmlTemplates) Lookup(name string) (tpl.Template, bool) {
	templ := t.lookup(name)
	if templ == nil {
		return nil, false
	}
	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()
	// 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 *textTemplates) setTemplateFuncster(f *templateFuncster) {
	t.funcster = f
}
type textTemplates struct {
	*templatesCommon
	*textTemplate
	clone      *texttemplate.Template
	cloneClone *texttemplate.Template
	overlays map[string]*texttemplate.Template
}
func (t *textTemplates) Lookup(name string) (tpl.Template, bool) {
	templ := t.lookup(name)
	if templ == nil {
		return nil, false
	}
	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
		}
	}
	if templ := t.t.Lookup(name); templ != nil {
		return templ
	}
	if t.clone != nil {
		return t.clone.Lookup(name)
	}
	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 *templateHandler) GetFuncs() map[string]interface{} {
	return t.html.funcster.funcMap
}
func (t *htmlTemplates) setFuncs(funcMap map[string]interface{}) {
	t.t.Funcs(funcMap)
}
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 *htmlTemplates) addTemplateIn(tt *template.Template, name, tpl string) (*templateContext, error) {
	t.mu.Lock()
	defer t.mu.Unlock()
	templ, err := tt.New(name).Parse(tpl)
	if err != nil {
		return nil, err
	}
	typ := resolveTemplateType(name)
	c, err := applyTemplateTransformersToHMLTTemplate(typ, templ)
	if err != nil {
		return nil, err
	}
	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
}
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
}
type textTemplate struct {
	mu sync.RWMutex
	t  *texttemplate.Template
}
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()
	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()
	templ, err := tt.New(name).Parse(tpl)
	if err != nil {
		return nil, err
	}
	if _, err := applyTemplateTransformersToTextTemplate(templateUndefined, templ); err != nil {
		return nil, err
	}
	return templ, nil
}
func (t *textTemplates) addTemplateIn(tt *texttemplate.Template, name, tpl string) (*templateContext, error) {
	name = strings.TrimPrefix(name, textTmplNamePrefix)
	templ, err := t.parseIn(tt, name, tpl)
	if err != nil {
		return nil, err
	}
	typ := resolveTemplateType(name)
	c, err := applyTemplateTransformersToTextTemplate(typ, templ)
	if err != nil {
		return nil, err
	}
	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
}
func (t *textTemplates) addTemplate(name, tpl string) (*templateContext, error) {
	return t.addTemplateIn(t.t, name, tpl)
}
func (t *textTemplates) addLateTemplate(name, tpl string) error {
	_, err := t.addTemplateIn(t.clone, name, tpl)
	return err
}
func (t *templateHandler) addTemplate(name, tpl string) error {
	return t.AddTemplate(name, tpl)
}
func (t *templateHandler) postTransform() error {
	if len(t.html.transformNotFound) == 0 && len(t.text.transformNotFound) == 0 {
		return nil
	}
	defer func() {
		t.text.transformNotFound = make(map[string]bool)
		t.html.transformNotFound = make(map[string]bool)
	}()
	for _, s := range []struct {
		lookup            func(name string) *parse.Tree
		transformNotFound map[string]bool
	}{
		// html templates
		{func(name string) *parse.Tree {
			templ := t.html.lookup(name)
			if templ == nil {
				return nil
			}
			return templ.Tree
		}, t.html.transformNotFound},
		// text templates
		{func(name string) *parse.Tree {
			templT := t.text.lookup(name)
			if templT == nil {
				return nil
			}
			return templT.Tree
		}, t.text.transformNotFound},
	} {
		for name := range s.transformNotFound {
			templ := s.lookup(name)
			if templ != nil {
				_, err := applyTemplateTransformers(templateUndefined, templ, s.lookup)
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}
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 {
		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
}
// 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
	}
	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
}
// 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())
	}
}
func (t *templateHandler) loadTemplates(prefix string) error {
	walker := func(path string, fi hugofs.FileMetaInfo, err error) error {
		if err != nil || fi.IsDir() {
			return err
		}
		if isDotFile(path) || isBackupFile(path) || isBaseTemplate(path) {
			return nil
		}
		workingDir := t.PathSpec.WorkingDir
		descriptor := output.TemplateLookupDescriptor{
			WorkingDir:    workingDir,
			RelPath:       path,
			Prefix:        prefix,
			OutputFormats: t.OutputFormatsConfig,
			FileExists: func(filename string) (bool, error) {
				return helpers.Exists(filename, t.Layouts.Fs)
			},
			ContainsAny: func(filename string, subslices [][]byte) (bool, error) {
				return helpers.FileContainsAny(filename, subslices, t.Layouts.Fs)
			},
		}
		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 {
			return err
		}
		return nil
	}
	if err := helpers.SymbolicWalk(t.Layouts.Fs, "", walker); err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		return nil
	}
	return nil
}
func (t *templateHandler) initFuncs() {
	// Both template types will get their own funcster instance, which
	// in the current case contains the same set of funcs.
	funcMap := createFuncMap(t.Deps)
	for _, funcsterHolder := range []templateFuncsterSetter{t.html, t.text} {
		funcster := newTemplateFuncster(t.Deps)
		// 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(funcMap)
		funcsterHolder.setTemplateFuncster(funcster)
	}
	for _, v := range t.shortcodes {
		for _, variant := range v.variants {
			t.setFuncMapInTemplate(variant.templ, funcMap)
		}
	}
	for _, extText := range t.extTextTemplates {
		extText.t.Funcs(funcMap)
	}
	// Amber is HTML only.
	t.amberFuncMap = template.FuncMap{}
	amberMu.Lock()
	for k, v := range amber.FuncMap {
		t.amberFuncMap[k] = v
	}
	for k, v := range t.html.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 *templateHandler) getTemplateHandler(name string) templateLoader {
	if strings.HasPrefix(name, textTmplNamePrefix) {
		return t.text
	}
	return t.html
}
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)
}
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 *textTemplates) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (templateInfo, error)) error {
	name = strings.TrimPrefix(name, textTmplNamePrefix)
	masterTpl := t.lookup(masterFilename)
	if masterTpl == nil {
		templ, err := onMissing(masterFilename)
		if err != nil {
			return err
		}
		masterTpl, err = t.t.New(masterFilename).Parse(templ.template)
		if err != nil {
			return errors.Wrapf(err, "failed to parse %q:", templ.filename)
		}
		t.nameBaseTemplateName[masterFilename] = templ.filename
	}
	templ, err := onMissing(overlayFilename)
	if err != nil {
		return err
	}
	overlayTpl, err := texttemplate.Must(masterTpl.Clone()).Parse(templ.template)
	if err != nil {
		return errors.Wrapf(err, "failed to parse %q:", templ.filename)
	}
	overlayTpl = overlayTpl.Lookup(overlayTpl.Name())
	if _, err := applyTemplateTransformersToTextTemplate(templateUndefined, overlayTpl); err != nil {
		return err
	}
	t.overlays[name] = overlayTpl
	t.nameBaseTemplateName[name] = templ.filename
	return err
}
func removeLeadingBOM(s string) string {
	const bom = '\ufeff'
	for i, r := range s {
		if i == 0 && r != bom {
			return s
		}
		if i > 0 {
			return s[i:]
		}
	}
	return s
}
func (t *templateHandler) addTemplateFile(name, baseTemplatePath, path string) error {
	t.checkState()
	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
		}
		s := removeLeadingBOM(string(b))
		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
	}
	// get the suffix and switch on that
	ext := filepath.Ext(path)
	switch ext {
	case ".amber":
		//	Only HTML support for Amber
		withoutExt := strings.TrimSuffix(name, filepath.Ext(name))
		templateName := withoutExt + ".html"
		b, err := afero.ReadFile(t.Layouts.Fs, path)
		if err != nil {
			return err
		}
		amberMu.Lock()
		templ, err := t.compileAmberWithTemplate(b, path, t.html.t.New(templateName))
		amberMu.Unlock()
		if err != nil {
			return err
		}
		typ := resolveTemplateType(name)
		c, err := applyTemplateTransformersToHMLTTemplate(typ, templ)
		if err != nil {
			return err
		}
		if typ == templateShortcode {
			t.addShortcodeVariant(templateName, c.Info, templ)
		} else {
			t.templateInfo[name] = c.Info
		}
		return nil
	case ".ace":
		//	Only HTML support for Ace
		var innerContent, baseContent []byte
		innerContent, err := afero.ReadFile(t.Layouts.Fs, path)
		if err != nil {
			return err
		}
		if baseTemplatePath != "" {
			baseContent, err = afero.ReadFile(t.Layouts.Fs, baseTemplatePath)
			if err != nil {
				return err
			}
		}
		return t.addAceTemplate(name, baseTemplatePath, path, baseContent, innerContent)
	default:
		if baseTemplatePath != "" {
			return t.handleMaster(name, path, baseTemplatePath, getTemplate)
		}
		templ, err := getTemplate(path)
		if err != nil {
			return err
		}
		err = t.AddTemplate(name, templ.template)
		if err != nil {
			return templ.errWithFileContext("parse failed", err)
		}
		return nil
	}
}
var embeddedTemplatesAliases = map[string][]string{
	"shortcodes/twitter.html": {"shortcodes/tweet.html"},
}
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
				}
			}
		}
	}
	return nil
}
func (t *templateHandler) addInternalTemplate(name, tpl string) error {
	return t.AddTemplate("_internal/"+name, tpl)
}
func (t *templateHandler) checkState() {
	if t.html.clone != nil || t.text.clone != nil {
		panic("template is cloned and cannot be modfified")
	}
}
func isDotFile(path string) bool {
	return filepath.Base(path)[0] == '.'
}
func isBackupFile(path string) bool {
	return path[len(path)-1] == '~'
}
const baseFileBase = "baseof"
func isBaseTemplate(path string) bool {
	return strings.Contains(filepath.Base(path), baseFileBase)
}