github.com/fighterlyt/hugo@v0.47.1/tpl/tplimpl/template.go (about)

     1  // Copyright 2017-present The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package tplimpl
    15  
    16  import (
    17  	"fmt"
    18  	"html/template"
    19  	"path"
    20  	"strings"
    21  	texttemplate "text/template"
    22  
    23  	"github.com/gohugoio/hugo/tpl/tplimpl/embedded"
    24  
    25  	"github.com/eknkc/amber"
    26  
    27  	"os"
    28  
    29  	"github.com/gohugoio/hugo/output"
    30  
    31  	"path/filepath"
    32  	"sync"
    33  
    34  	"github.com/gohugoio/hugo/deps"
    35  	"github.com/gohugoio/hugo/helpers"
    36  	"github.com/gohugoio/hugo/tpl"
    37  	"github.com/spf13/afero"
    38  )
    39  
    40  const (
    41  	textTmplNamePrefix = "_text/"
    42  )
    43  
    44  var (
    45  	_ tpl.TemplateHandler       = (*templateHandler)(nil)
    46  	_ tpl.TemplateDebugger      = (*templateHandler)(nil)
    47  	_ tpl.TemplateFuncsGetter   = (*templateHandler)(nil)
    48  	_ tpl.TemplateTestMocker    = (*templateHandler)(nil)
    49  	_ tpl.TemplateFinder        = (*htmlTemplates)(nil)
    50  	_ tpl.TemplateFinder        = (*textTemplates)(nil)
    51  	_ templateLoader            = (*htmlTemplates)(nil)
    52  	_ templateLoader            = (*textTemplates)(nil)
    53  	_ templateLoader            = (*templateHandler)(nil)
    54  	_ templateFuncsterTemplater = (*htmlTemplates)(nil)
    55  	_ templateFuncsterTemplater = (*textTemplates)(nil)
    56  )
    57  
    58  // Protecting  global map access (Amber)
    59  var amberMu sync.Mutex
    60  
    61  type templateErr struct {
    62  	name string
    63  	err  error
    64  }
    65  
    66  type templateLoader interface {
    67  	handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error
    68  	addTemplate(name, tpl string) error
    69  	addLateTemplate(name, tpl string) error
    70  }
    71  
    72  type templateFuncsterTemplater interface {
    73  	templateFuncsterSetter
    74  	tpl.TemplateFinder
    75  	setFuncs(funcMap map[string]interface{})
    76  }
    77  
    78  type templateFuncsterSetter interface {
    79  	setTemplateFuncster(f *templateFuncster)
    80  }
    81  
    82  // templateHandler holds the templates in play.
    83  // It implements the templateLoader and tpl.TemplateHandler interfaces.
    84  type templateHandler struct {
    85  	mu sync.Mutex
    86  
    87  	// text holds all the pure text templates.
    88  	text *textTemplates
    89  	html *htmlTemplates
    90  
    91  	extTextTemplates []*textTemplate
    92  
    93  	amberFuncMap template.FuncMap
    94  
    95  	errors []*templateErr
    96  
    97  	// This is the filesystem to load the templates from. All the templates are
    98  	// stored in the root of this filesystem.
    99  	layoutsFs afero.Fs
   100  
   101  	*deps.Deps
   102  }
   103  
   104  // NewTextTemplate provides a text template parser that has all the Hugo
   105  // template funcs etc. built-in.
   106  func (t *templateHandler) NewTextTemplate() tpl.TemplateParseFinder {
   107  	t.mu.Lock()
   108  	t.mu.Unlock()
   109  
   110  	tt := &textTemplate{t: texttemplate.New("")}
   111  	t.extTextTemplates = append(t.extTextTemplates, tt)
   112  
   113  	return tt
   114  
   115  }
   116  
   117  func (t *templateHandler) addError(name string, err error) {
   118  	t.errors = append(t.errors, &templateErr{name, err})
   119  }
   120  
   121  func (t *templateHandler) Debug() {
   122  	fmt.Println("HTML templates:\n", t.html.t.DefinedTemplates())
   123  	fmt.Println("\n\nText templates:\n", t.text.t.DefinedTemplates())
   124  }
   125  
   126  // PrintErrors prints the accumulated errors as ERROR to the log.
   127  func (t *templateHandler) PrintErrors() {
   128  	for _, e := range t.errors {
   129  		t.Log.ERROR.Println(e.name, ":", e.err)
   130  	}
   131  }
   132  
   133  // Lookup tries to find a template with the given name in both template
   134  // collections: First HTML, then the plain text template collection.
   135  func (t *templateHandler) Lookup(name string) (tpl.Template, bool) {
   136  
   137  	if strings.HasPrefix(name, textTmplNamePrefix) {
   138  		// The caller has explicitly asked for a text template, so only look
   139  		// in the text template collection.
   140  		// The templates are stored without the prefix identificator.
   141  		name = strings.TrimPrefix(name, textTmplNamePrefix)
   142  
   143  		return t.text.Lookup(name)
   144  	}
   145  
   146  	// Look in both
   147  	if te, found := t.html.Lookup(name); found {
   148  		return te, true
   149  	}
   150  
   151  	return t.text.Lookup(name)
   152  
   153  }
   154  
   155  func (t *templateHandler) clone(d *deps.Deps) *templateHandler {
   156  	c := &templateHandler{
   157  		Deps:      d,
   158  		layoutsFs: d.BaseFs.Layouts.Fs,
   159  		html:      &htmlTemplates{t: template.Must(t.html.t.Clone()), overlays: make(map[string]*template.Template)},
   160  		text:      &textTemplates{textTemplate: &textTemplate{t: texttemplate.Must(t.text.t.Clone())}, overlays: make(map[string]*texttemplate.Template)},
   161  		errors:    make([]*templateErr, 0),
   162  	}
   163  
   164  	d.Tmpl = c
   165  
   166  	c.initFuncs()
   167  
   168  	for k, v := range t.html.overlays {
   169  		vc := template.Must(v.Clone())
   170  		// The extra lookup is a workaround, see
   171  		// * https://github.com/golang/go/issues/16101
   172  		// * https://github.com/gohugoio/hugo/issues/2549
   173  		vc = vc.Lookup(vc.Name())
   174  		vc.Funcs(c.html.funcster.funcMap)
   175  		c.html.overlays[k] = vc
   176  	}
   177  
   178  	for k, v := range t.text.overlays {
   179  		vc := texttemplate.Must(v.Clone())
   180  		vc = vc.Lookup(vc.Name())
   181  		vc.Funcs(texttemplate.FuncMap(c.text.funcster.funcMap))
   182  		c.text.overlays[k] = vc
   183  	}
   184  
   185  	return c
   186  
   187  }
   188  
   189  func newTemplateAdapter(deps *deps.Deps) *templateHandler {
   190  	htmlT := &htmlTemplates{
   191  		t:        template.New(""),
   192  		overlays: make(map[string]*template.Template),
   193  	}
   194  	textT := &textTemplates{
   195  		textTemplate: &textTemplate{t: texttemplate.New("")},
   196  		overlays:     make(map[string]*texttemplate.Template),
   197  	}
   198  	return &templateHandler{
   199  		Deps:      deps,
   200  		layoutsFs: deps.BaseFs.Layouts.Fs,
   201  		html:      htmlT,
   202  		text:      textT,
   203  		errors:    make([]*templateErr, 0),
   204  	}
   205  
   206  }
   207  
   208  type htmlTemplates struct {
   209  	funcster *templateFuncster
   210  
   211  	t *template.Template
   212  
   213  	// This looks, and is, strange.
   214  	// The clone is used by non-renderable content pages, and these need to be
   215  	// re-parsed on content change, and to avoid the
   216  	// "cannot Parse after Execute" error, we need to re-clone it from the original clone.
   217  	clone      *template.Template
   218  	cloneClone *template.Template
   219  
   220  	// a separate storage for the overlays created from cloned master templates.
   221  	// note: No mutex protection, so we add these in one Go routine, then just read.
   222  	overlays map[string]*template.Template
   223  }
   224  
   225  func (t *htmlTemplates) setTemplateFuncster(f *templateFuncster) {
   226  	t.funcster = f
   227  }
   228  
   229  func (t *htmlTemplates) Lookup(name string) (tpl.Template, bool) {
   230  	templ := t.lookup(name)
   231  	if templ == nil {
   232  		return nil, false
   233  	}
   234  	return &tpl.TemplateAdapter{Template: templ, Metrics: t.funcster.Deps.Metrics}, true
   235  }
   236  
   237  func (t *htmlTemplates) lookup(name string) *template.Template {
   238  
   239  	// Need to check in the overlay registry first as it will also be found below.
   240  	if t.overlays != nil {
   241  		if templ, ok := t.overlays[name]; ok {
   242  			return templ
   243  		}
   244  	}
   245  
   246  	if templ := t.t.Lookup(name); templ != nil {
   247  		return templ
   248  	}
   249  
   250  	if t.clone != nil {
   251  		return t.clone.Lookup(name)
   252  	}
   253  
   254  	return nil
   255  }
   256  
   257  func (t *textTemplates) setTemplateFuncster(f *templateFuncster) {
   258  	t.funcster = f
   259  }
   260  
   261  type textTemplates struct {
   262  	*textTemplate
   263  	funcster   *templateFuncster
   264  	clone      *texttemplate.Template
   265  	cloneClone *texttemplate.Template
   266  
   267  	overlays map[string]*texttemplate.Template
   268  }
   269  
   270  func (t *textTemplates) Lookup(name string) (tpl.Template, bool) {
   271  	templ := t.lookup(name)
   272  	if templ == nil {
   273  		return nil, false
   274  	}
   275  	return &tpl.TemplateAdapter{Template: templ, Metrics: t.funcster.Deps.Metrics}, true
   276  }
   277  
   278  func (t *textTemplates) lookup(name string) *texttemplate.Template {
   279  
   280  	// Need to check in the overlay registry first as it will also be found below.
   281  	if t.overlays != nil {
   282  		if templ, ok := t.overlays[name]; ok {
   283  			return templ
   284  		}
   285  	}
   286  
   287  	if templ := t.t.Lookup(name); templ != nil {
   288  		return templ
   289  	}
   290  
   291  	if t.clone != nil {
   292  		return t.clone.Lookup(name)
   293  	}
   294  
   295  	return nil
   296  }
   297  
   298  func (t *templateHandler) setFuncs(funcMap map[string]interface{}) {
   299  	t.html.setFuncs(funcMap)
   300  	t.text.setFuncs(funcMap)
   301  }
   302  
   303  // SetFuncs replaces the funcs in the func maps with new definitions.
   304  // This is only used in tests.
   305  func (t *templateHandler) SetFuncs(funcMap map[string]interface{}) {
   306  	t.setFuncs(funcMap)
   307  }
   308  
   309  func (t *templateHandler) GetFuncs() map[string]interface{} {
   310  	return t.html.funcster.funcMap
   311  }
   312  
   313  func (t *htmlTemplates) setFuncs(funcMap map[string]interface{}) {
   314  	t.t.Funcs(funcMap)
   315  }
   316  
   317  func (t *textTemplates) setFuncs(funcMap map[string]interface{}) {
   318  	t.t.Funcs(funcMap)
   319  }
   320  
   321  // LoadTemplates loads the templates from the layouts filesystem.
   322  // A prefix can be given to indicate a template namespace to load the templates
   323  // into, i.e. "_internal" etc.
   324  func (t *templateHandler) LoadTemplates(prefix string) {
   325  	t.loadTemplates(prefix)
   326  
   327  }
   328  
   329  func (t *htmlTemplates) addTemplateIn(tt *template.Template, name, tpl string) error {
   330  	templ, err := tt.New(name).Parse(tpl)
   331  	if err != nil {
   332  		return err
   333  	}
   334  
   335  	if err := applyTemplateTransformersToHMLTTemplate(templ); err != nil {
   336  		return err
   337  	}
   338  
   339  	if strings.Contains(name, "shortcodes") {
   340  		// We need to keep track of one ot the output format's shortcode template
   341  		// without knowing the rendering context.
   342  		withoutExt := strings.TrimSuffix(name, path.Ext(name))
   343  		clone := template.Must(templ.Clone())
   344  		tt.AddParseTree(withoutExt, clone.Tree)
   345  	}
   346  
   347  	return nil
   348  }
   349  
   350  func (t *htmlTemplates) addTemplate(name, tpl string) error {
   351  	return t.addTemplateIn(t.t, name, tpl)
   352  }
   353  
   354  func (t *htmlTemplates) addLateTemplate(name, tpl string) error {
   355  	return t.addTemplateIn(t.clone, name, tpl)
   356  }
   357  
   358  type textTemplate struct {
   359  	t *texttemplate.Template
   360  }
   361  
   362  func (t *textTemplate) Parse(name, tpl string) (tpl.Template, error) {
   363  	return t.parSeIn(t.t, name, tpl)
   364  }
   365  
   366  func (t *textTemplate) Lookup(name string) (tpl.Template, bool) {
   367  	tpl := t.t.Lookup(name)
   368  	return tpl, tpl != nil
   369  }
   370  
   371  func (t *textTemplate) parSeIn(tt *texttemplate.Template, name, tpl string) (*texttemplate.Template, error) {
   372  	templ, err := tt.New(name).Parse(tpl)
   373  	if err != nil {
   374  		return nil, err
   375  	}
   376  
   377  	if err := applyTemplateTransformersToTextTemplate(templ); err != nil {
   378  		return nil, err
   379  	}
   380  	return templ, nil
   381  }
   382  
   383  func (t *textTemplates) addTemplateIn(tt *texttemplate.Template, name, tpl string) error {
   384  	name = strings.TrimPrefix(name, textTmplNamePrefix)
   385  	templ, err := t.parSeIn(tt, name, tpl)
   386  	if err != nil {
   387  		return err
   388  	}
   389  
   390  	if err := applyTemplateTransformersToTextTemplate(templ); err != nil {
   391  		return err
   392  	}
   393  
   394  	if strings.Contains(name, "shortcodes") {
   395  		// We need to keep track of one ot the output format's shortcode template
   396  		// without knowing the rendering context.
   397  		withoutExt := strings.TrimSuffix(name, path.Ext(name))
   398  		clone := texttemplate.Must(templ.Clone())
   399  		tt.AddParseTree(withoutExt, clone.Tree)
   400  	}
   401  
   402  	return nil
   403  }
   404  
   405  func (t *textTemplates) addTemplate(name, tpl string) error {
   406  	return t.addTemplateIn(t.t, name, tpl)
   407  }
   408  
   409  func (t *textTemplates) addLateTemplate(name, tpl string) error {
   410  	return t.addTemplateIn(t.clone, name, tpl)
   411  }
   412  
   413  func (t *templateHandler) addTemplate(name, tpl string) error {
   414  	return t.AddTemplate(name, tpl)
   415  }
   416  
   417  func (t *templateHandler) addLateTemplate(name, tpl string) error {
   418  	return t.AddLateTemplate(name, tpl)
   419  }
   420  
   421  // AddLateTemplate is used to add a template late, i.e. after the
   422  // regular templates have started its execution.
   423  func (t *templateHandler) AddLateTemplate(name, tpl string) error {
   424  	h := t.getTemplateHandler(name)
   425  	if err := h.addLateTemplate(name, tpl); err != nil {
   426  		t.addError(name, err)
   427  		return err
   428  	}
   429  	return nil
   430  }
   431  
   432  // AddTemplate parses and adds a template to the collection.
   433  // Templates with name prefixed with "_text" will be handled as plain
   434  // text templates.
   435  func (t *templateHandler) AddTemplate(name, tpl string) error {
   436  	h := t.getTemplateHandler(name)
   437  	if err := h.addTemplate(name, tpl); err != nil {
   438  		t.addError(name, err)
   439  		return err
   440  	}
   441  	return nil
   442  }
   443  
   444  // MarkReady marks the templates as "ready for execution". No changes allowed
   445  // after this is set.
   446  // TODO(bep) if this proves to be resource heavy, we could detect
   447  // earlier if we really need this, or make it lazy.
   448  func (t *templateHandler) MarkReady() {
   449  	if t.html.clone == nil {
   450  		t.html.clone = template.Must(t.html.t.Clone())
   451  		t.html.cloneClone = template.Must(t.html.clone.Clone())
   452  	}
   453  	if t.text.clone == nil {
   454  		t.text.clone = texttemplate.Must(t.text.t.Clone())
   455  		t.text.cloneClone = texttemplate.Must(t.text.clone.Clone())
   456  	}
   457  }
   458  
   459  // RebuildClone rebuilds the cloned templates. Used for live-reloads.
   460  func (t *templateHandler) RebuildClone() {
   461  	t.html.clone = template.Must(t.html.cloneClone.Clone())
   462  	t.text.clone = texttemplate.Must(t.text.cloneClone.Clone())
   463  }
   464  
   465  func (t *templateHandler) loadTemplates(prefix string) {
   466  	walker := func(path string, fi os.FileInfo, err error) error {
   467  		if err != nil || fi.IsDir() {
   468  			return nil
   469  		}
   470  
   471  		if isDotFile(path) || isBackupFile(path) || isBaseTemplate(path) {
   472  			return nil
   473  		}
   474  
   475  		workingDir := t.PathSpec.WorkingDir
   476  
   477  		descriptor := output.TemplateLookupDescriptor{
   478  			WorkingDir:    workingDir,
   479  			RelPath:       path,
   480  			Prefix:        prefix,
   481  			OutputFormats: t.OutputFormatsConfig,
   482  			FileExists: func(filename string) (bool, error) {
   483  				return helpers.Exists(filename, t.Layouts.Fs)
   484  			},
   485  			ContainsAny: func(filename string, subslices [][]byte) (bool, error) {
   486  				return helpers.FileContainsAny(filename, subslices, t.Layouts.Fs)
   487  			},
   488  		}
   489  
   490  		tplID, err := output.CreateTemplateNames(descriptor)
   491  		if err != nil {
   492  			t.Log.ERROR.Printf("Failed to resolve template in path %q: %s", path, err)
   493  
   494  			return nil
   495  		}
   496  
   497  		if err := t.addTemplateFile(tplID.Name, tplID.MasterFilename, tplID.OverlayFilename); err != nil {
   498  			t.Log.ERROR.Printf("Failed to add template %q in path %q: %s", tplID.Name, path, err)
   499  		}
   500  
   501  		return nil
   502  	}
   503  
   504  	if err := helpers.SymbolicWalk(t.Layouts.Fs, "", walker); err != nil {
   505  		t.Log.ERROR.Printf("Failed to load templates: %s", err)
   506  	}
   507  
   508  }
   509  
   510  func (t *templateHandler) initFuncs() {
   511  
   512  	// Both template types will get their own funcster instance, which
   513  	// in the current case contains the same set of funcs.
   514  	funcMap := createFuncMap(t.Deps)
   515  	for _, funcsterHolder := range []templateFuncsterSetter{t.html, t.text} {
   516  		funcster := newTemplateFuncster(t.Deps)
   517  
   518  		// The URL funcs in the funcMap is somewhat language dependent,
   519  		// so we need to wait until the language and site config is loaded.
   520  		funcster.initFuncMap(funcMap)
   521  
   522  		funcsterHolder.setTemplateFuncster(funcster)
   523  
   524  	}
   525  
   526  	for _, extText := range t.extTextTemplates {
   527  		extText.t.Funcs(funcMap)
   528  	}
   529  
   530  	// Amber is HTML only.
   531  	t.amberFuncMap = template.FuncMap{}
   532  
   533  	amberMu.Lock()
   534  	for k, v := range amber.FuncMap {
   535  		t.amberFuncMap[k] = v
   536  	}
   537  
   538  	for k, v := range t.html.funcster.funcMap {
   539  		t.amberFuncMap[k] = v
   540  		// Hacky, but we need to make sure that the func names are in the global map.
   541  		amber.FuncMap[k] = func() string {
   542  			panic("should never be invoked")
   543  		}
   544  	}
   545  	amberMu.Unlock()
   546  
   547  }
   548  
   549  func (t *templateHandler) getTemplateHandler(name string) templateLoader {
   550  	if strings.HasPrefix(name, textTmplNamePrefix) {
   551  		return t.text
   552  	}
   553  	return t.html
   554  }
   555  
   556  func (t *templateHandler) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error {
   557  	h := t.getTemplateHandler(name)
   558  	return h.handleMaster(name, overlayFilename, masterFilename, onMissing)
   559  }
   560  
   561  func (t *htmlTemplates) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error {
   562  
   563  	masterTpl := t.lookup(masterFilename)
   564  
   565  	if masterTpl == nil {
   566  		templ, err := onMissing(masterFilename)
   567  		if err != nil {
   568  			return err
   569  		}
   570  
   571  		masterTpl, err = t.t.New(overlayFilename).Parse(templ)
   572  		if err != nil {
   573  			return err
   574  		}
   575  	}
   576  
   577  	templ, err := onMissing(overlayFilename)
   578  	if err != nil {
   579  		return err
   580  	}
   581  
   582  	overlayTpl, err := template.Must(masterTpl.Clone()).Parse(templ)
   583  	if err != nil {
   584  		return err
   585  	}
   586  
   587  	// The extra lookup is a workaround, see
   588  	// * https://github.com/golang/go/issues/16101
   589  	// * https://github.com/gohugoio/hugo/issues/2549
   590  	overlayTpl = overlayTpl.Lookup(overlayTpl.Name())
   591  	if err := applyTemplateTransformersToHMLTTemplate(overlayTpl); err != nil {
   592  		return err
   593  	}
   594  
   595  	t.overlays[name] = overlayTpl
   596  
   597  	return err
   598  
   599  }
   600  
   601  func (t *textTemplates) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error {
   602  
   603  	name = strings.TrimPrefix(name, textTmplNamePrefix)
   604  	masterTpl := t.lookup(masterFilename)
   605  
   606  	if masterTpl == nil {
   607  		templ, err := onMissing(masterFilename)
   608  		if err != nil {
   609  			return err
   610  		}
   611  
   612  		masterTpl, err = t.t.New(overlayFilename).Parse(templ)
   613  		if err != nil {
   614  			return err
   615  		}
   616  	}
   617  
   618  	templ, err := onMissing(overlayFilename)
   619  	if err != nil {
   620  		return err
   621  	}
   622  
   623  	overlayTpl, err := texttemplate.Must(masterTpl.Clone()).Parse(templ)
   624  	if err != nil {
   625  		return err
   626  	}
   627  
   628  	overlayTpl = overlayTpl.Lookup(overlayTpl.Name())
   629  	if err := applyTemplateTransformersToTextTemplate(overlayTpl); err != nil {
   630  		return err
   631  	}
   632  	t.overlays[name] = overlayTpl
   633  
   634  	return err
   635  
   636  }
   637  
   638  func (t *templateHandler) addTemplateFile(name, baseTemplatePath, path string) error {
   639  	t.checkState()
   640  
   641  	t.Log.DEBUG.Printf("Add template file: name %q, baseTemplatePath %q, path %q", name, baseTemplatePath, path)
   642  
   643  	getTemplate := func(filename string) (string, error) {
   644  		b, err := afero.ReadFile(t.Layouts.Fs, filename)
   645  		if err != nil {
   646  			return "", err
   647  		}
   648  		s := string(b)
   649  
   650  		return s, nil
   651  	}
   652  
   653  	// get the suffix and switch on that
   654  	ext := filepath.Ext(path)
   655  	switch ext {
   656  	case ".amber":
   657  		//	Only HTML support for Amber
   658  		withoutExt := strings.TrimSuffix(name, filepath.Ext(name))
   659  		templateName := withoutExt + ".html"
   660  		b, err := afero.ReadFile(t.Layouts.Fs, path)
   661  
   662  		if err != nil {
   663  			return err
   664  		}
   665  
   666  		amberMu.Lock()
   667  		templ, err := t.compileAmberWithTemplate(b, path, t.html.t.New(templateName))
   668  		amberMu.Unlock()
   669  		if err != nil {
   670  			return err
   671  		}
   672  
   673  		if err := applyTemplateTransformersToHMLTTemplate(templ); err != nil {
   674  			return err
   675  		}
   676  
   677  		if strings.Contains(templateName, "shortcodes") {
   678  			// We need to keep track of one ot the output format's shortcode template
   679  			// without knowing the rendering context.
   680  			clone := template.Must(templ.Clone())
   681  			t.html.t.AddParseTree(withoutExt, clone.Tree)
   682  		}
   683  
   684  		return nil
   685  
   686  	case ".ace":
   687  		//	Only HTML support for Ace
   688  		var innerContent, baseContent []byte
   689  		innerContent, err := afero.ReadFile(t.Layouts.Fs, path)
   690  
   691  		if err != nil {
   692  			return err
   693  		}
   694  
   695  		if baseTemplatePath != "" {
   696  			baseContent, err = afero.ReadFile(t.Layouts.Fs, baseTemplatePath)
   697  			if err != nil {
   698  				return err
   699  			}
   700  		}
   701  
   702  		return t.addAceTemplate(name, baseTemplatePath, path, baseContent, innerContent)
   703  	default:
   704  
   705  		if baseTemplatePath != "" {
   706  			return t.handleMaster(name, path, baseTemplatePath, getTemplate)
   707  		}
   708  
   709  		templ, err := getTemplate(path)
   710  
   711  		if err != nil {
   712  			return err
   713  		}
   714  
   715  		return t.AddTemplate(name, templ)
   716  	}
   717  }
   718  
   719  var embeddedTemplatesAliases = map[string][]string{
   720  	"shortcodes/twitter.html": []string{"shortcodes/tweet.html"},
   721  }
   722  
   723  func (t *templateHandler) loadEmbedded() {
   724  	for _, kv := range embedded.EmbeddedTemplates {
   725  		// TODO(bep) error handling
   726  		name, templ := kv[0], kv[1]
   727  		t.addInternalTemplate(name, templ)
   728  		if aliases, found := embeddedTemplatesAliases[name]; found {
   729  			for _, alias := range aliases {
   730  				t.addInternalTemplate(alias, templ)
   731  			}
   732  
   733  		}
   734  	}
   735  
   736  }
   737  
   738  func (t *templateHandler) addInternalTemplate(name, tpl string) error {
   739  	return t.AddTemplate("_internal/"+name, tpl)
   740  }
   741  
   742  func (t *templateHandler) checkState() {
   743  	if t.html.clone != nil || t.text.clone != nil {
   744  		panic("template is cloned and cannot be modfified")
   745  	}
   746  }
   747  
   748  func isDotFile(path string) bool {
   749  	return filepath.Base(path)[0] == '.'
   750  }
   751  
   752  func isBackupFile(path string) bool {
   753  	return path[len(path)-1] == '~'
   754  }
   755  
   756  const baseFileBase = "baseof"
   757  
   758  func isBaseTemplate(path string) bool {
   759  	return strings.Contains(filepath.Base(path), baseFileBase)
   760  }