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 }