hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

template.go (27465B)

    1 // Copyright 2019 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 	"bytes"
   18 	"context"
   19 	"embed"
   20 	"fmt"
   21 	"io"
   22 	"io/fs"
   23 	"os"
   24 	"path/filepath"
   25 	"reflect"
   26 	"regexp"
   27 	"sort"
   28 	"strings"
   29 	"sync"
   30 	"time"
   31 	"unicode"
   32 	"unicode/utf8"
   33 
   34 	"github.com/gohugoio/hugo/common/types"
   35 
   36 	"github.com/gohugoio/hugo/helpers"
   37 
   38 	"github.com/gohugoio/hugo/output"
   39 
   40 	"github.com/gohugoio/hugo/deps"
   41 	"github.com/spf13/afero"
   42 
   43 	"github.com/gohugoio/hugo/common/herrors"
   44 	"github.com/gohugoio/hugo/hugofs"
   45 	"github.com/gohugoio/hugo/hugofs/files"
   46 
   47 	htmltemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"
   48 	texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
   49 
   50 	"github.com/gohugoio/hugo/identity"
   51 	"github.com/gohugoio/hugo/tpl"
   52 )
   53 
   54 const (
   55 	textTmplNamePrefix = "_text/"
   56 
   57 	shortcodesPathPrefix = "shortcodes/"
   58 	internalPathPrefix   = "_internal/"
   59 	baseFileBase         = "baseof"
   60 )
   61 
   62 // The identifiers may be truncated in the log, e.g.
   63 // "executing "main" at <$scaled.SRelPermalin...>: can't evaluate field SRelPermalink in type *resource.Image"
   64 // We need this to identify position in templates with base templates applied.
   65 var identifiersRe = regexp.MustCompile(`at \<(.*?)(\.{3})?\>:`)
   66 
   67 var embeddedTemplatesAliases = map[string][]string{
   68 	"shortcodes/twitter.html": {"shortcodes/tweet.html"},
   69 }
   70 
   71 var (
   72 	_ tpl.TemplateManager         = (*templateExec)(nil)
   73 	_ tpl.TemplateHandler         = (*templateExec)(nil)
   74 	_ tpl.TemplateFuncGetter      = (*templateExec)(nil)
   75 	_ tpl.TemplateFinder          = (*templateExec)(nil)
   76 	_ tpl.UnusedTemplatesProvider = (*templateExec)(nil)
   77 
   78 	_ tpl.Template = (*templateState)(nil)
   79 	_ tpl.Info     = (*templateState)(nil)
   80 )
   81 
   82 var baseTemplateDefineRe = regexp.MustCompile(`^{{-?\s*define`)
   83 
   84 // needsBaseTemplate returns true if the first non-comment template block is a
   85 // define block.
   86 // If a base template does not exist, we will handle that when it's used.
   87 func needsBaseTemplate(templ string) bool {
   88 	idx := -1
   89 	inComment := false
   90 	for i := 0; i < len(templ); {
   91 		if !inComment && strings.HasPrefix(templ[i:], "{{/*") {
   92 			inComment = true
   93 			i += 4
   94 		} else if inComment && strings.HasPrefix(templ[i:], "*/}}") {
   95 			inComment = false
   96 			i += 4
   97 		} else {
   98 			r, size := utf8.DecodeRuneInString(templ[i:])
   99 			if !inComment {
  100 				if strings.HasPrefix(templ[i:], "{{") {
  101 					idx = i
  102 					break
  103 				} else if !unicode.IsSpace(r) {
  104 					break
  105 				}
  106 			}
  107 			i += size
  108 		}
  109 	}
  110 
  111 	if idx == -1 {
  112 		return false
  113 	}
  114 
  115 	return baseTemplateDefineRe.MatchString(templ[idx:])
  116 }
  117 
  118 func newIdentity(name string) identity.Manager {
  119 	return identity.NewManager(identity.NewPathIdentity(files.ComponentFolderLayouts, name))
  120 }
  121 
  122 func newStandaloneTextTemplate(funcs map[string]any) tpl.TemplateParseFinder {
  123 	return &textTemplateWrapperWithLock{
  124 		RWMutex:  &sync.RWMutex{},
  125 		Template: texttemplate.New("").Funcs(funcs),
  126 	}
  127 }
  128 
  129 func newTemplateExec(d *deps.Deps) (*templateExec, error) {
  130 	exec, funcs := newTemplateExecuter(d)
  131 	funcMap := make(map[string]any)
  132 	for k, v := range funcs {
  133 		funcMap[k] = v.Interface()
  134 	}
  135 
  136 	var templateUsageTracker map[string]templateInfo
  137 	if d.Cfg.GetBool("printUnusedTemplates") {
  138 		templateUsageTracker = make(map[string]templateInfo)
  139 	}
  140 
  141 	h := &templateHandler{
  142 		nameBaseTemplateName: make(map[string]string),
  143 		transformNotFound:    make(map[string]*templateState),
  144 		identityNotFound:     make(map[string][]identity.Manager),
  145 
  146 		shortcodes:   make(map[string]*shortcodeTemplates),
  147 		templateInfo: make(map[string]tpl.Info),
  148 		baseof:       make(map[string]templateInfo),
  149 		needsBaseof:  make(map[string]templateInfo),
  150 
  151 		main: newTemplateNamespace(funcMap),
  152 
  153 		Deps:                d,
  154 		layoutHandler:       output.NewLayoutHandler(),
  155 		layoutsFs:           d.BaseFs.Layouts.Fs,
  156 		layoutTemplateCache: make(map[layoutCacheKey]tpl.Template),
  157 
  158 		templateUsageTracker: templateUsageTracker,
  159 	}
  160 
  161 	if err := h.loadEmbedded(); err != nil {
  162 		return nil, err
  163 	}
  164 
  165 	if err := h.loadTemplates(); err != nil {
  166 		return nil, err
  167 	}
  168 
  169 	e := &templateExec{
  170 		d:               d,
  171 		executor:        exec,
  172 		funcs:           funcs,
  173 		templateHandler: h,
  174 	}
  175 
  176 	d.SetTmpl(e)
  177 	d.SetTextTmpl(newStandaloneTextTemplate(funcMap))
  178 
  179 	if d.WithTemplate != nil {
  180 		if err := d.WithTemplate(e); err != nil {
  181 			return nil, err
  182 		}
  183 	}
  184 
  185 	return e, nil
  186 }
  187 
  188 func newTemplateNamespace(funcs map[string]any) *templateNamespace {
  189 	return &templateNamespace{
  190 		prototypeHTML: htmltemplate.New("").Funcs(funcs),
  191 		prototypeText: texttemplate.New("").Funcs(funcs),
  192 		templateStateMap: &templateStateMap{
  193 			templates: make(map[string]*templateState),
  194 		},
  195 	}
  196 }
  197 
  198 func newTemplateState(templ tpl.Template, info templateInfo) *templateState {
  199 	return &templateState{
  200 		info:      info,
  201 		typ:       info.resolveType(),
  202 		Template:  templ,
  203 		Manager:   newIdentity(info.name),
  204 		parseInfo: tpl.DefaultParseInfo,
  205 	}
  206 }
  207 
  208 type layoutCacheKey struct {
  209 	d output.LayoutDescriptor
  210 	f string
  211 }
  212 
  213 type templateExec struct {
  214 	d        *deps.Deps
  215 	executor texttemplate.Executer
  216 	funcs    map[string]reflect.Value
  217 
  218 	*templateHandler
  219 }
  220 
  221 func (t templateExec) Clone(d *deps.Deps) *templateExec {
  222 	exec, funcs := newTemplateExecuter(d)
  223 	t.executor = exec
  224 	t.funcs = funcs
  225 	t.d = d
  226 	return &t
  227 }
  228 
  229 func (t *templateExec) Execute(templ tpl.Template, wr io.Writer, data any) error {
  230 	return t.ExecuteWithContext(context.Background(), templ, wr, data)
  231 }
  232 
  233 func (t *templateExec) ExecuteWithContext(ctx context.Context, templ tpl.Template, wr io.Writer, data any) error {
  234 	if rlocker, ok := templ.(types.RLocker); ok {
  235 		rlocker.RLock()
  236 		defer rlocker.RUnlock()
  237 	}
  238 	if t.Metrics != nil {
  239 		defer t.Metrics.MeasureSince(templ.Name(), time.Now())
  240 	}
  241 
  242 	if t.templateUsageTracker != nil {
  243 		if ts, ok := templ.(*templateState); ok {
  244 			t.templateUsageTrackerMu.Lock()
  245 			if _, found := t.templateUsageTracker[ts.Name()]; !found {
  246 				t.templateUsageTracker[ts.Name()] = ts.info
  247 			}
  248 
  249 			if !ts.baseInfo.IsZero() {
  250 				if _, found := t.templateUsageTracker[ts.baseInfo.name]; !found {
  251 					t.templateUsageTracker[ts.baseInfo.name] = ts.baseInfo
  252 				}
  253 			}
  254 			t.templateUsageTrackerMu.Unlock()
  255 		}
  256 	}
  257 
  258 	execErr := t.executor.ExecuteWithContext(ctx, templ, wr, data)
  259 	if execErr != nil {
  260 		execErr = t.addFileContext(templ, execErr)
  261 	}
  262 	return execErr
  263 }
  264 
  265 func (t *templateExec) UnusedTemplates() []tpl.FileInfo {
  266 	if t.templateUsageTracker == nil {
  267 		return nil
  268 	}
  269 	var unused []tpl.FileInfo
  270 
  271 	for _, ti := range t.needsBaseof {
  272 		if _, found := t.templateUsageTracker[ti.name]; !found {
  273 			unused = append(unused, ti)
  274 		}
  275 	}
  276 
  277 	for _, ti := range t.baseof {
  278 		if _, found := t.templateUsageTracker[ti.name]; !found {
  279 			unused = append(unused, ti)
  280 		}
  281 	}
  282 
  283 	for _, ts := range t.main.templates {
  284 		ti := ts.info
  285 		if strings.HasPrefix(ti.name, "_internal/") || ti.realFilename == "" {
  286 			continue
  287 		}
  288 
  289 		if _, found := t.templateUsageTracker[ti.name]; !found {
  290 			unused = append(unused, ti)
  291 		}
  292 	}
  293 
  294 	sort.Slice(unused, func(i, j int) bool {
  295 		return unused[i].Name() < unused[j].Name()
  296 	})
  297 
  298 	return unused
  299 }
  300 
  301 func (t *templateExec) GetFunc(name string) (reflect.Value, bool) {
  302 	v, found := t.funcs[name]
  303 	return v, found
  304 }
  305 
  306 func (t *templateExec) MarkReady() error {
  307 	var err error
  308 	t.readyInit.Do(func() {
  309 		// We only need the clones if base templates are in use.
  310 		if len(t.needsBaseof) > 0 {
  311 			err = t.main.createPrototypes()
  312 		}
  313 	})
  314 
  315 	return err
  316 }
  317 
  318 type templateHandler struct {
  319 	main        *templateNamespace
  320 	needsBaseof map[string]templateInfo
  321 	baseof      map[string]templateInfo
  322 
  323 	readyInit sync.Once
  324 
  325 	// This is the filesystem to load the templates from. All the templates are
  326 	// stored in the root of this filesystem.
  327 	layoutsFs afero.Fs
  328 
  329 	layoutHandler *output.LayoutHandler
  330 
  331 	layoutTemplateCache   map[layoutCacheKey]tpl.Template
  332 	layoutTemplateCacheMu sync.RWMutex
  333 
  334 	*deps.Deps
  335 
  336 	// Used to get proper filenames in errors
  337 	nameBaseTemplateName map[string]string
  338 
  339 	// Holds name and source of template definitions not found during the first
  340 	// AST transformation pass.
  341 	transformNotFound map[string]*templateState
  342 
  343 	// Holds identities of templates not found during first pass.
  344 	identityNotFound map[string][]identity.Manager
  345 
  346 	// shortcodes maps shortcode name to template variants
  347 	// (language, output format etc.) of that shortcode.
  348 	shortcodes map[string]*shortcodeTemplates
  349 
  350 	// templateInfo maps template name to some additional information about that template.
  351 	// Note that for shortcodes that same information is embedded in the
  352 	// shortcodeTemplates type.
  353 	templateInfo map[string]tpl.Info
  354 
  355 	// May be nil.
  356 	templateUsageTracker   map[string]templateInfo
  357 	templateUsageTrackerMu sync.Mutex
  358 }
  359 
  360 // AddTemplate parses and adds a template to the collection.
  361 // Templates with name prefixed with "_text" will be handled as plain
  362 // text templates.
  363 func (t *templateHandler) AddTemplate(name, tpl string) error {
  364 	templ, err := t.addTemplateTo(t.newTemplateInfo(name, tpl), t.main)
  365 	if err == nil {
  366 		t.applyTemplateTransformers(t.main, templ)
  367 	}
  368 	return err
  369 }
  370 
  371 func (t *templateHandler) Lookup(name string) (tpl.Template, bool) {
  372 	templ, found := t.main.Lookup(name)
  373 	if found {
  374 		return templ, true
  375 	}
  376 
  377 	return nil, false
  378 }
  379 
  380 func (t *templateHandler) LookupLayout(d output.LayoutDescriptor, f output.Format) (tpl.Template, bool, error) {
  381 	key := layoutCacheKey{d, f.Name}
  382 	t.layoutTemplateCacheMu.RLock()
  383 	if cacheVal, found := t.layoutTemplateCache[key]; found {
  384 		t.layoutTemplateCacheMu.RUnlock()
  385 		return cacheVal, true, nil
  386 	}
  387 	t.layoutTemplateCacheMu.RUnlock()
  388 
  389 	t.layoutTemplateCacheMu.Lock()
  390 	defer t.layoutTemplateCacheMu.Unlock()
  391 
  392 	templ, found, err := t.findLayout(d, f)
  393 	if err == nil && found {
  394 		t.layoutTemplateCache[key] = templ
  395 		return templ, true, nil
  396 	}
  397 
  398 	return nil, false, err
  399 }
  400 
  401 // This currently only applies to shortcodes and what we get here is the
  402 // shortcode name.
  403 func (t *templateHandler) LookupVariant(name string, variants tpl.TemplateVariants) (tpl.Template, bool, bool) {
  404 	name = templateBaseName(templateShortcode, name)
  405 	s, found := t.shortcodes[name]
  406 	if !found {
  407 		return nil, false, false
  408 	}
  409 
  410 	sv, found := s.fromVariants(variants)
  411 	if !found {
  412 		return nil, false, false
  413 	}
  414 
  415 	more := len(s.variants) > 1
  416 
  417 	return sv.ts, true, more
  418 }
  419 
  420 // LookupVariants returns all variants of name, nil if none found.
  421 func (t *templateHandler) LookupVariants(name string) []tpl.Template {
  422 	name = templateBaseName(templateShortcode, name)
  423 	s, found := t.shortcodes[name]
  424 	if !found {
  425 		return nil
  426 	}
  427 
  428 	variants := make([]tpl.Template, len(s.variants))
  429 	for i := 0; i < len(variants); i++ {
  430 		variants[i] = s.variants[i].ts
  431 	}
  432 
  433 	return variants
  434 }
  435 
  436 func (t *templateHandler) HasTemplate(name string) bool {
  437 	if _, found := t.baseof[name]; found {
  438 		return true
  439 	}
  440 
  441 	if _, found := t.needsBaseof[name]; found {
  442 		return true
  443 	}
  444 
  445 	_, found := t.Lookup(name)
  446 	return found
  447 }
  448 
  449 func (t *templateHandler) findLayout(d output.LayoutDescriptor, f output.Format) (tpl.Template, bool, error) {
  450 	layouts, _ := t.layoutHandler.For(d, f)
  451 	for _, name := range layouts {
  452 		templ, found := t.main.Lookup(name)
  453 		if found {
  454 			return templ, true, nil
  455 		}
  456 
  457 		overlay, found := t.needsBaseof[name]
  458 
  459 		if !found {
  460 			continue
  461 		}
  462 
  463 		d.Baseof = true
  464 		baseLayouts, _ := t.layoutHandler.For(d, f)
  465 		var base templateInfo
  466 		found = false
  467 		for _, l := range baseLayouts {
  468 			base, found = t.baseof[l]
  469 			if found {
  470 				break
  471 			}
  472 		}
  473 
  474 		templ, err := t.applyBaseTemplate(overlay, base)
  475 		if err != nil {
  476 			return nil, false, err
  477 		}
  478 
  479 		ts := newTemplateState(templ, overlay)
  480 
  481 		if found {
  482 			ts.baseInfo = base
  483 
  484 			// Add the base identity to detect changes
  485 			ts.Add(identity.NewPathIdentity(files.ComponentFolderLayouts, base.name))
  486 		}
  487 
  488 		t.applyTemplateTransformers(t.main, ts)
  489 
  490 		if err := t.extractPartials(ts.Template); err != nil {
  491 			return nil, false, err
  492 		}
  493 
  494 		return ts, true, nil
  495 
  496 	}
  497 
  498 	return nil, false, nil
  499 }
  500 
  501 func (t *templateHandler) findTemplate(name string) *templateState {
  502 	if templ, found := t.Lookup(name); found {
  503 		return templ.(*templateState)
  504 	}
  505 	return nil
  506 }
  507 
  508 func (t *templateHandler) newTemplateInfo(name, tpl string) templateInfo {
  509 	var isText bool
  510 	name, isText = t.nameIsText(name)
  511 	return templateInfo{
  512 		name:     name,
  513 		isText:   isText,
  514 		template: tpl,
  515 	}
  516 }
  517 
  518 func (t *templateHandler) addFileContext(templ tpl.Template, inerr error) error {
  519 	if strings.HasPrefix(templ.Name(), "_internal") {
  520 		return inerr
  521 	}
  522 
  523 	ts, ok := templ.(*templateState)
  524 	if !ok {
  525 		return inerr
  526 	}
  527 
  528 	identifiers := t.extractIdentifiers(inerr.Error())
  529 
  530 	//lint:ignore ST1008 the error is the main result
  531 	checkFilename := func(info templateInfo, inErr error) (error, bool) {
  532 		if info.filename == "" {
  533 			return inErr, false
  534 		}
  535 
  536 		lineMatcher := func(m herrors.LineMatcher) int {
  537 			if m.Position.LineNumber != m.LineNumber {
  538 				return -1
  539 			}
  540 
  541 			for _, id := range identifiers {
  542 				if strings.Contains(m.Line, id) {
  543 					// We found the line, but return a 0 to signal to
  544 					// use the column from the error message.
  545 					return 0
  546 				}
  547 			}
  548 			return -1
  549 		}
  550 
  551 		f, err := t.layoutsFs.Open(info.filename)
  552 		if err != nil {
  553 			return inErr, false
  554 		}
  555 		defer f.Close()
  556 
  557 		fe := herrors.NewFileErrorFromName(inErr, info.realFilename)
  558 		fe.UpdateContent(f, lineMatcher)
  559 
  560 		if !fe.ErrorContext().Position.IsValid() {
  561 			return inErr, false
  562 		}
  563 		return fe, true
  564 	}
  565 
  566 	inerr = fmt.Errorf("execute of template failed: %w", inerr)
  567 
  568 	if err, ok := checkFilename(ts.info, inerr); ok {
  569 		return err
  570 	}
  571 
  572 	err, _ := checkFilename(ts.baseInfo, inerr)
  573 
  574 	return err
  575 }
  576 
  577 func (t *templateHandler) extractIdentifiers(line string) []string {
  578 	m := identifiersRe.FindAllStringSubmatch(line, -1)
  579 	identifiers := make([]string, len(m))
  580 	for i := 0; i < len(m); i++ {
  581 		identifiers[i] = m[i][1]
  582 	}
  583 	return identifiers
  584 }
  585 
  586 func (t *templateHandler) addShortcodeVariant(ts *templateState) {
  587 	name := ts.Name()
  588 	base := templateBaseName(templateShortcode, name)
  589 
  590 	shortcodename, variants := templateNameAndVariants(base)
  591 
  592 	templs, found := t.shortcodes[shortcodename]
  593 	if !found {
  594 		templs = &shortcodeTemplates{}
  595 		t.shortcodes[shortcodename] = templs
  596 	}
  597 
  598 	sv := shortcodeVariant{variants: variants, ts: ts}
  599 
  600 	i := templs.indexOf(variants)
  601 
  602 	if i != -1 {
  603 		// Only replace if it's an override of an internal template.
  604 		if !isInternal(name) {
  605 			templs.variants[i] = sv
  606 		}
  607 	} else {
  608 		templs.variants = append(templs.variants, sv)
  609 	}
  610 }
  611 
  612 func (t *templateHandler) addTemplateFile(name, path string) error {
  613 	getTemplate := func(filename string) (templateInfo, error) {
  614 		fs := t.Layouts.Fs
  615 		b, err := afero.ReadFile(fs, filename)
  616 		if err != nil {
  617 			return templateInfo{filename: filename, fs: fs}, err
  618 		}
  619 
  620 		s := removeLeadingBOM(string(b))
  621 
  622 		realFilename := filename
  623 		if fi, err := fs.Stat(filename); err == nil {
  624 			if fim, ok := fi.(hugofs.FileMetaInfo); ok {
  625 				realFilename = fim.Meta().Filename
  626 			}
  627 		}
  628 
  629 		var isText bool
  630 		name, isText = t.nameIsText(name)
  631 
  632 		return templateInfo{
  633 			name:         name,
  634 			isText:       isText,
  635 			template:     s,
  636 			filename:     filename,
  637 			realFilename: realFilename,
  638 			fs:           fs,
  639 		}, nil
  640 	}
  641 
  642 	tinfo, err := getTemplate(path)
  643 	if err != nil {
  644 		return err
  645 	}
  646 
  647 	if isBaseTemplatePath(name) {
  648 		// Store it for later.
  649 		t.baseof[name] = tinfo
  650 		return nil
  651 	}
  652 
  653 	needsBaseof := !t.noBaseNeeded(name) && needsBaseTemplate(tinfo.template)
  654 	if needsBaseof {
  655 		t.needsBaseof[name] = tinfo
  656 		return nil
  657 	}
  658 
  659 	templ, err := t.addTemplateTo(tinfo, t.main)
  660 	if err != nil {
  661 		return tinfo.errWithFileContext("parse failed", err)
  662 	}
  663 	t.applyTemplateTransformers(t.main, templ)
  664 
  665 	return nil
  666 }
  667 
  668 func (t *templateHandler) addTemplateTo(info templateInfo, to *templateNamespace) (*templateState, error) {
  669 	return to.parse(info)
  670 }
  671 
  672 func (t *templateHandler) applyBaseTemplate(overlay, base templateInfo) (tpl.Template, error) {
  673 	if overlay.isText {
  674 		var (
  675 			templ = t.main.prototypeTextClone.New(overlay.name)
  676 			err   error
  677 		)
  678 
  679 		if !base.IsZero() {
  680 			templ, err = templ.Parse(base.template)
  681 			if err != nil {
  682 				return nil, base.errWithFileContext("parse failed", err)
  683 			}
  684 		}
  685 
  686 		templ, err = texttemplate.Must(templ.Clone()).Parse(overlay.template)
  687 		if err != nil {
  688 			return nil, overlay.errWithFileContext("parse failed", err)
  689 		}
  690 
  691 		// The extra lookup is a workaround, see
  692 		// * https://github.com/golang/go/issues/16101
  693 		// * https://github.com/gohugoio/hugo/issues/2549
  694 		// templ = templ.Lookup(templ.Name())
  695 
  696 		return templ, nil
  697 	}
  698 
  699 	var (
  700 		templ = t.main.prototypeHTMLClone.New(overlay.name)
  701 		err   error
  702 	)
  703 
  704 	if !base.IsZero() {
  705 		templ, err = templ.Parse(base.template)
  706 		if err != nil {
  707 			return nil, base.errWithFileContext("parse failed", err)
  708 		}
  709 	}
  710 
  711 	templ, err = htmltemplate.Must(templ.Clone()).Parse(overlay.template)
  712 	if err != nil {
  713 		return nil, overlay.errWithFileContext("parse failed", err)
  714 	}
  715 
  716 	// The extra lookup is a workaround, see
  717 	// * https://github.com/golang/go/issues/16101
  718 	// * https://github.com/gohugoio/hugo/issues/2549
  719 	templ = templ.Lookup(templ.Name())
  720 
  721 	return templ, err
  722 }
  723 
  724 func (t *templateHandler) applyTemplateTransformers(ns *templateNamespace, ts *templateState) (*templateContext, error) {
  725 	c, err := applyTemplateTransformers(ts, ns.newTemplateLookup(ts))
  726 	if err != nil {
  727 		return nil, err
  728 	}
  729 
  730 	for k := range c.templateNotFound {
  731 		t.transformNotFound[k] = ts
  732 		t.identityNotFound[k] = append(t.identityNotFound[k], c.t)
  733 	}
  734 
  735 	for k := range c.identityNotFound {
  736 		t.identityNotFound[k] = append(t.identityNotFound[k], c.t)
  737 	}
  738 
  739 	return c, err
  740 }
  741 
  742 //go:embed embedded/templates/*
  743 //go:embed embedded/templates/_default/*
  744 //go:embed embedded/templates/_server/*
  745 var embededTemplatesFs embed.FS
  746 
  747 func (t *templateHandler) loadEmbedded() error {
  748 	return fs.WalkDir(embededTemplatesFs, ".", func(path string, d fs.DirEntry, err error) error {
  749 		if d == nil || d.IsDir() {
  750 			return nil
  751 		}
  752 
  753 		templb, err := embededTemplatesFs.ReadFile(path)
  754 		if err != nil {
  755 			return err
  756 		}
  757 
  758 		// Get the newlines on Windows in line with how we had it back when we used Go Generate
  759 		// to write the templates to Go files.
  760 		templ := string(bytes.ReplaceAll(templb, []byte("\r\n"), []byte("\n")))
  761 		name := strings.TrimPrefix(filepath.ToSlash(path), "embedded/templates/")
  762 		templateName := name
  763 
  764 		// For the render hooks and the server templates it does not make sense to preseve the
  765 		// double _indternal double book-keeping,
  766 		// just add it if its now provided by the user.
  767 		if !strings.Contains(path, "_default/_markup") && !strings.HasPrefix(name, "_server/") {
  768 			templateName = internalPathPrefix + name
  769 		}
  770 
  771 		if _, found := t.Lookup(templateName); !found {
  772 			if err := t.AddTemplate(templateName, templ); err != nil {
  773 				return err
  774 			}
  775 		}
  776 
  777 		if aliases, found := embeddedTemplatesAliases[name]; found {
  778 			// TODO(bep) avoid reparsing these aliases
  779 			for _, alias := range aliases {
  780 				alias = internalPathPrefix + alias
  781 				if err := t.AddTemplate(alias, templ); err != nil {
  782 					return err
  783 				}
  784 			}
  785 		}
  786 
  787 		return nil
  788 	})
  789 }
  790 
  791 func (t *templateHandler) loadTemplates() error {
  792 	walker := func(path string, fi hugofs.FileMetaInfo, err error) error {
  793 		if err != nil || fi.IsDir() {
  794 			return err
  795 		}
  796 
  797 		if isDotFile(path) || isBackupFile(path) {
  798 			return nil
  799 		}
  800 
  801 		name := strings.TrimPrefix(filepath.ToSlash(path), "/")
  802 		filename := filepath.Base(path)
  803 		outputFormat, found := t.OutputFormatsConfig.FromFilename(filename)
  804 
  805 		if found && outputFormat.IsPlainText {
  806 			name = textTmplNamePrefix + name
  807 		}
  808 
  809 		if err := t.addTemplateFile(name, path); err != nil {
  810 			return err
  811 		}
  812 
  813 		return nil
  814 	}
  815 
  816 	if err := helpers.SymbolicWalk(t.Layouts.Fs, "", walker); err != nil {
  817 		if !os.IsNotExist(err) {
  818 			return err
  819 		}
  820 		return nil
  821 	}
  822 
  823 	return nil
  824 }
  825 
  826 func (t *templateHandler) nameIsText(name string) (string, bool) {
  827 	isText := strings.HasPrefix(name, textTmplNamePrefix)
  828 	if isText {
  829 		name = strings.TrimPrefix(name, textTmplNamePrefix)
  830 	}
  831 	return name, isText
  832 }
  833 
  834 func (t *templateHandler) noBaseNeeded(name string) bool {
  835 	if strings.HasPrefix(name, "shortcodes/") || strings.HasPrefix(name, "partials/") {
  836 		return true
  837 	}
  838 	return strings.Contains(name, "_markup/")
  839 }
  840 
  841 func (t *templateHandler) extractPartials(templ tpl.Template) error {
  842 	templs := templates(templ)
  843 	for _, templ := range templs {
  844 		if templ.Name() == "" || !strings.HasPrefix(templ.Name(), "partials/") {
  845 			continue
  846 		}
  847 
  848 		ts := newTemplateState(templ, templateInfo{name: templ.Name()})
  849 		ts.typ = templatePartial
  850 
  851 		t.main.mu.RLock()
  852 		_, found := t.main.templates[templ.Name()]
  853 		t.main.mu.RUnlock()
  854 
  855 		if !found {
  856 			t.main.mu.Lock()
  857 			// This is a template defined inline.
  858 			_, err := applyTemplateTransformers(ts, t.main.newTemplateLookup(ts))
  859 			if err != nil {
  860 				t.main.mu.Unlock()
  861 				return err
  862 			}
  863 			t.main.templates[templ.Name()] = ts
  864 			t.main.mu.Unlock()
  865 
  866 		}
  867 	}
  868 
  869 	return nil
  870 }
  871 
  872 func (t *templateHandler) postTransform() error {
  873 	defineCheckedHTML := false
  874 	defineCheckedText := false
  875 
  876 	for _, v := range t.main.templates {
  877 		if v.typ == templateShortcode {
  878 			t.addShortcodeVariant(v)
  879 		}
  880 
  881 		if defineCheckedHTML && defineCheckedText {
  882 			continue
  883 		}
  884 
  885 		isText := isText(v.Template)
  886 		if isText {
  887 			if defineCheckedText {
  888 				continue
  889 			}
  890 			defineCheckedText = true
  891 		} else {
  892 			if defineCheckedHTML {
  893 				continue
  894 			}
  895 			defineCheckedHTML = true
  896 		}
  897 
  898 		if err := t.extractPartials(v.Template); err != nil {
  899 			return err
  900 		}
  901 	}
  902 
  903 	for name, source := range t.transformNotFound {
  904 		lookup := t.main.newTemplateLookup(source)
  905 		templ := lookup(name)
  906 		if templ != nil {
  907 			_, err := applyTemplateTransformers(templ, lookup)
  908 			if err != nil {
  909 				return err
  910 			}
  911 		}
  912 	}
  913 
  914 	for k, v := range t.identityNotFound {
  915 		ts := t.findTemplate(k)
  916 		if ts != nil {
  917 			for _, im := range v {
  918 				im.Add(ts)
  919 			}
  920 		}
  921 	}
  922 
  923 	for _, v := range t.shortcodes {
  924 		sort.Slice(v.variants, func(i, j int) bool {
  925 			v1, v2 := v.variants[i], v.variants[j]
  926 			name1, name2 := v1.ts.Name(), v2.ts.Name()
  927 			isHTMl1, isHTML2 := strings.HasSuffix(name1, "html"), strings.HasSuffix(name2, "html")
  928 
  929 			// There will be a weighted selection later, but make
  930 			// sure these are sorted to get a stable selection for
  931 			// output formats missing specific templates.
  932 			// Prefer HTML.
  933 			if isHTMl1 || isHTML2 && !(isHTMl1 && isHTML2) {
  934 				return isHTMl1
  935 			}
  936 
  937 			return name1 < name2
  938 		})
  939 	}
  940 
  941 	return nil
  942 }
  943 
  944 type templateNamespace struct {
  945 	prototypeText      *texttemplate.Template
  946 	prototypeHTML      *htmltemplate.Template
  947 	prototypeTextClone *texttemplate.Template
  948 	prototypeHTMLClone *htmltemplate.Template
  949 
  950 	*templateStateMap
  951 }
  952 
  953 func (t templateNamespace) Clone() *templateNamespace {
  954 	t.mu.Lock()
  955 	defer t.mu.Unlock()
  956 
  957 	t.templateStateMap = &templateStateMap{
  958 		templates: make(map[string]*templateState),
  959 	}
  960 
  961 	t.prototypeText = texttemplate.Must(t.prototypeText.Clone())
  962 	t.prototypeHTML = htmltemplate.Must(t.prototypeHTML.Clone())
  963 
  964 	return &t
  965 }
  966 
  967 func (t *templateNamespace) Lookup(name string) (tpl.Template, bool) {
  968 	t.mu.RLock()
  969 	defer t.mu.RUnlock()
  970 
  971 	templ, found := t.templates[name]
  972 	if !found {
  973 		return nil, false
  974 	}
  975 
  976 	return templ, found
  977 }
  978 
  979 func (t *templateNamespace) createPrototypes() error {
  980 	t.prototypeTextClone = texttemplate.Must(t.prototypeText.Clone())
  981 	t.prototypeHTMLClone = htmltemplate.Must(t.prototypeHTML.Clone())
  982 
  983 	return nil
  984 }
  985 
  986 func (t *templateNamespace) newTemplateLookup(in *templateState) func(name string) *templateState {
  987 	return func(name string) *templateState {
  988 		if templ, found := t.templates[name]; found {
  989 			if templ.isText() != in.isText() {
  990 				return nil
  991 			}
  992 			return templ
  993 		}
  994 		if templ, found := findTemplateIn(name, in); found {
  995 			return newTemplateState(templ, templateInfo{name: templ.Name()})
  996 		}
  997 		return nil
  998 	}
  999 }
 1000 
 1001 func (t *templateNamespace) parse(info templateInfo) (*templateState, error) {
 1002 	t.mu.Lock()
 1003 	defer t.mu.Unlock()
 1004 
 1005 	if info.isText {
 1006 		prototype := t.prototypeText
 1007 
 1008 		templ, err := prototype.New(info.name).Parse(info.template)
 1009 		if err != nil {
 1010 			return nil, err
 1011 		}
 1012 
 1013 		ts := newTemplateState(templ, info)
 1014 
 1015 		t.templates[info.name] = ts
 1016 
 1017 		return ts, nil
 1018 	}
 1019 
 1020 	prototype := t.prototypeHTML
 1021 
 1022 	templ, err := prototype.New(info.name).Parse(info.template)
 1023 	if err != nil {
 1024 		return nil, err
 1025 	}
 1026 
 1027 	ts := newTemplateState(templ, info)
 1028 
 1029 	t.templates[info.name] = ts
 1030 
 1031 	return ts, nil
 1032 }
 1033 
 1034 type templateState struct {
 1035 	tpl.Template
 1036 
 1037 	typ       templateType
 1038 	parseInfo tpl.ParseInfo
 1039 	identity.Manager
 1040 
 1041 	info     templateInfo
 1042 	baseInfo templateInfo // Set when a base template is used.
 1043 }
 1044 
 1045 func (t *templateState) ParseInfo() tpl.ParseInfo {
 1046 	return t.parseInfo
 1047 }
 1048 
 1049 func (t *templateState) isText() bool {
 1050 	return isText(t.Template)
 1051 }
 1052 
 1053 func isText(templ tpl.Template) bool {
 1054 	_, isText := templ.(*texttemplate.Template)
 1055 	return isText
 1056 }
 1057 
 1058 type templateStateMap struct {
 1059 	mu        sync.RWMutex
 1060 	templates map[string]*templateState
 1061 }
 1062 
 1063 type templateWrapperWithLock struct {
 1064 	*sync.RWMutex
 1065 	tpl.Template
 1066 }
 1067 
 1068 type textTemplateWrapperWithLock struct {
 1069 	*sync.RWMutex
 1070 	*texttemplate.Template
 1071 }
 1072 
 1073 func (t *textTemplateWrapperWithLock) Lookup(name string) (tpl.Template, bool) {
 1074 	t.RLock()
 1075 	templ := t.Template.Lookup(name)
 1076 	t.RUnlock()
 1077 	if templ == nil {
 1078 		return nil, false
 1079 	}
 1080 	return &textTemplateWrapperWithLock{
 1081 		RWMutex:  t.RWMutex,
 1082 		Template: templ,
 1083 	}, true
 1084 }
 1085 
 1086 func (t *textTemplateWrapperWithLock) LookupVariant(name string, variants tpl.TemplateVariants) (tpl.Template, bool, bool) {
 1087 	panic("not supported")
 1088 }
 1089 
 1090 func (t *textTemplateWrapperWithLock) LookupVariants(name string) []tpl.Template {
 1091 	panic("not supported")
 1092 }
 1093 
 1094 func (t *textTemplateWrapperWithLock) Parse(name, tpl string) (tpl.Template, error) {
 1095 	t.Lock()
 1096 	defer t.Unlock()
 1097 	return t.Template.New(name).Parse(tpl)
 1098 }
 1099 
 1100 func isBackupFile(path string) bool {
 1101 	return path[len(path)-1] == '~'
 1102 }
 1103 
 1104 func isBaseTemplatePath(path string) bool {
 1105 	return strings.Contains(filepath.Base(path), baseFileBase)
 1106 }
 1107 
 1108 func isDotFile(path string) bool {
 1109 	return filepath.Base(path)[0] == '.'
 1110 }
 1111 
 1112 func removeLeadingBOM(s string) string {
 1113 	const bom = '\ufeff'
 1114 
 1115 	for i, r := range s {
 1116 		if i == 0 && r != bom {
 1117 			return s
 1118 		}
 1119 		if i > 0 {
 1120 			return s[i:]
 1121 		}
 1122 	}
 1123 
 1124 	return s
 1125 }
 1126 
 1127 // resolves _internal/shortcodes/param.html => param.html etc.
 1128 func templateBaseName(typ templateType, name string) string {
 1129 	name = strings.TrimPrefix(name, internalPathPrefix)
 1130 	switch typ {
 1131 	case templateShortcode:
 1132 		return strings.TrimPrefix(name, shortcodesPathPrefix)
 1133 	default:
 1134 		panic("not implemented")
 1135 	}
 1136 }
 1137 
 1138 func unwrap(templ tpl.Template) tpl.Template {
 1139 	if ts, ok := templ.(*templateState); ok {
 1140 		return ts.Template
 1141 	}
 1142 	return templ
 1143 }
 1144 
 1145 func templates(in tpl.Template) []tpl.Template {
 1146 	var templs []tpl.Template
 1147 	in = unwrap(in)
 1148 	if textt, ok := in.(*texttemplate.Template); ok {
 1149 		for _, t := range textt.Templates() {
 1150 			templs = append(templs, t)
 1151 		}
 1152 	}
 1153 
 1154 	if htmlt, ok := in.(*htmltemplate.Template); ok {
 1155 		for _, t := range htmlt.Templates() {
 1156 			templs = append(templs, t)
 1157 		}
 1158 	}
 1159 
 1160 	return templs
 1161 }