github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/tpl/tplimpl/template_funcs.go (about)

     1  // Copyright 2017-present The Hugo Authors. All rights reserved.
     2  //
     3  // Portions Copyright The Go Authors.
     4  
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  // http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package tplimpl
    17  
    18  import (
    19  	"context"
    20  	"reflect"
    21  	"strings"
    22  
    23  	"github.com/gohugoio/hugo/common/hreflect"
    24  	"github.com/gohugoio/hugo/common/maps"
    25  	"github.com/gohugoio/hugo/tpl"
    26  
    27  	template "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"
    28  	texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
    29  
    30  	"github.com/gohugoio/hugo/deps"
    31  
    32  	"github.com/gohugoio/hugo/tpl/internal"
    33  
    34  	// Init the namespaces
    35  	_ "github.com/gohugoio/hugo/tpl/cast"
    36  	_ "github.com/gohugoio/hugo/tpl/collections"
    37  	_ "github.com/gohugoio/hugo/tpl/compare"
    38  	_ "github.com/gohugoio/hugo/tpl/crypto"
    39  	_ "github.com/gohugoio/hugo/tpl/css"
    40  	_ "github.com/gohugoio/hugo/tpl/data"
    41  	_ "github.com/gohugoio/hugo/tpl/debug"
    42  	_ "github.com/gohugoio/hugo/tpl/diagrams"
    43  	_ "github.com/gohugoio/hugo/tpl/encoding"
    44  	_ "github.com/gohugoio/hugo/tpl/fmt"
    45  	_ "github.com/gohugoio/hugo/tpl/hugo"
    46  	_ "github.com/gohugoio/hugo/tpl/images"
    47  	_ "github.com/gohugoio/hugo/tpl/inflect"
    48  	_ "github.com/gohugoio/hugo/tpl/js"
    49  	_ "github.com/gohugoio/hugo/tpl/lang"
    50  	_ "github.com/gohugoio/hugo/tpl/math"
    51  	_ "github.com/gohugoio/hugo/tpl/openapi/openapi3"
    52  	_ "github.com/gohugoio/hugo/tpl/os"
    53  	_ "github.com/gohugoio/hugo/tpl/page"
    54  	_ "github.com/gohugoio/hugo/tpl/partials"
    55  	_ "github.com/gohugoio/hugo/tpl/path"
    56  	_ "github.com/gohugoio/hugo/tpl/reflect"
    57  	_ "github.com/gohugoio/hugo/tpl/resources"
    58  	_ "github.com/gohugoio/hugo/tpl/safe"
    59  	_ "github.com/gohugoio/hugo/tpl/site"
    60  	_ "github.com/gohugoio/hugo/tpl/strings"
    61  	_ "github.com/gohugoio/hugo/tpl/templates"
    62  	_ "github.com/gohugoio/hugo/tpl/time"
    63  	_ "github.com/gohugoio/hugo/tpl/transform"
    64  	_ "github.com/gohugoio/hugo/tpl/urls"
    65  )
    66  
    67  var (
    68  	_                texttemplate.ExecHelper = (*templateExecHelper)(nil)
    69  	zero             reflect.Value
    70  	contextInterface = reflect.TypeOf((*context.Context)(nil)).Elem()
    71  )
    72  
    73  type templateExecHelper struct {
    74  	running    bool // whether we're in server mode.
    75  	site       reflect.Value
    76  	siteParams reflect.Value
    77  	funcs      map[string]reflect.Value
    78  }
    79  
    80  func (t *templateExecHelper) GetFunc(ctx context.Context, tmpl texttemplate.Preparer, name string) (fn reflect.Value, firstArg reflect.Value, found bool) {
    81  	if fn, found := t.funcs[name]; found {
    82  		if fn.Type().NumIn() > 0 {
    83  			first := fn.Type().In(0)
    84  			if first.Implements(contextInterface) {
    85  				// TODO(bep) check if we can void this conversion every time -- and if that matters.
    86  				// The first argument may be context.Context. This is never provided by the end user, but it's used to pass down
    87  				// contextual information, e.g. the top level data context (e.g. Page).
    88  				return fn, reflect.ValueOf(ctx), true
    89  			}
    90  		}
    91  
    92  		return fn, zero, true
    93  	}
    94  	return zero, zero, false
    95  }
    96  
    97  func (t *templateExecHelper) Init(ctx context.Context, tmpl texttemplate.Preparer) {
    98  }
    99  
   100  func (t *templateExecHelper) GetMapValue(ctx context.Context, tmpl texttemplate.Preparer, receiver, key reflect.Value) (reflect.Value, bool) {
   101  	if params, ok := receiver.Interface().(maps.Params); ok {
   102  		// Case insensitive.
   103  		keystr := strings.ToLower(key.String())
   104  		v, found := params[keystr]
   105  		if !found {
   106  			return zero, false
   107  		}
   108  		return reflect.ValueOf(v), true
   109  	}
   110  
   111  	v := receiver.MapIndex(key)
   112  
   113  	return v, v.IsValid()
   114  }
   115  
   116  var typeParams = reflect.TypeOf(maps.Params{})
   117  
   118  func (t *templateExecHelper) GetMethod(ctx context.Context, tmpl texttemplate.Preparer, receiver reflect.Value, name string) (method reflect.Value, firstArg reflect.Value) {
   119  	if t.running {
   120  		switch name {
   121  		case "GetPage", "Render":
   122  			if info, ok := tmpl.(tpl.Info); ok {
   123  				if m := receiver.MethodByName(name + "WithTemplateInfo"); m.IsValid() {
   124  					return m, reflect.ValueOf(info)
   125  				}
   126  			}
   127  		}
   128  	}
   129  
   130  	if strings.EqualFold(name, "mainsections") && receiver.Type() == typeParams && receiver.Pointer() == t.siteParams.Pointer() {
   131  		// MOved to site.MainSections in Hugo 0.112.0.
   132  		receiver = t.site
   133  		name = "MainSections"
   134  
   135  	}
   136  
   137  	fn := hreflect.GetMethodByName(receiver, name)
   138  	if !fn.IsValid() {
   139  		return zero, zero
   140  	}
   141  
   142  	if fn.Type().NumIn() > 0 {
   143  		first := fn.Type().In(0)
   144  		if first.Implements(contextInterface) {
   145  			// The first argument may be context.Context. This is never provided by the end user, but it's used to pass down
   146  			// contextual information, e.g. the top level data context (e.g. Page).
   147  			return fn, reflect.ValueOf(ctx)
   148  		}
   149  	}
   150  
   151  	return fn, zero
   152  }
   153  
   154  func newTemplateExecuter(d *deps.Deps) (texttemplate.Executer, map[string]reflect.Value) {
   155  	funcs := createFuncMap(d)
   156  	funcsv := make(map[string]reflect.Value)
   157  
   158  	for k, v := range funcs {
   159  		vv := reflect.ValueOf(v)
   160  		funcsv[k] = vv
   161  	}
   162  
   163  	// Duplicate Go's internal funcs here for faster lookups.
   164  	for k, v := range template.GoFuncs {
   165  		if _, exists := funcsv[k]; !exists {
   166  			vv, ok := v.(reflect.Value)
   167  			if !ok {
   168  				vv = reflect.ValueOf(v)
   169  			}
   170  			funcsv[k] = vv
   171  		}
   172  	}
   173  
   174  	for k, v := range texttemplate.GoFuncs {
   175  		if _, exists := funcsv[k]; !exists {
   176  			funcsv[k] = v
   177  		}
   178  	}
   179  
   180  	exeHelper := &templateExecHelper{
   181  		running:    d.Conf.Running(),
   182  		funcs:      funcsv,
   183  		site:       reflect.ValueOf(d.Site),
   184  		siteParams: reflect.ValueOf(d.Site.Params()),
   185  	}
   186  
   187  	return texttemplate.NewExecuter(
   188  		exeHelper,
   189  	), funcsv
   190  }
   191  
   192  func createFuncMap(d *deps.Deps) map[string]any {
   193  	funcMap := template.FuncMap{}
   194  
   195  	// Merge the namespace funcs
   196  	for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
   197  		ns := nsf(d)
   198  		if _, exists := funcMap[ns.Name]; exists {
   199  			panic(ns.Name + " is a duplicate template func")
   200  		}
   201  		funcMap[ns.Name] = ns.Context
   202  		for _, mm := range ns.MethodMappings {
   203  			for _, alias := range mm.Aliases {
   204  				if _, exists := funcMap[alias]; exists {
   205  					panic(alias + " is a duplicate template func")
   206  				}
   207  				funcMap[alias] = mm.Method
   208  			}
   209  		}
   210  	}
   211  
   212  	if d.OverloadedTemplateFuncs != nil {
   213  		for k, v := range d.OverloadedTemplateFuncs {
   214  			funcMap[k] = v
   215  		}
   216  	}
   217  
   218  	return funcMap
   219  }