diff options
Diffstat (limited to 'tpl/tplimpl/template_funcs.go')
-rw-r--r-- | tpl/tplimpl/template_funcs.go | 70 |
1 files changed, 53 insertions, 17 deletions
diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go index 97d1b40dd..8997c83d6 100644 --- a/tpl/tplimpl/template_funcs.go +++ b/tpl/tplimpl/template_funcs.go @@ -22,6 +22,7 @@ import ( "github.com/gohugoio/hugo/common/hreflect" "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/tpl" template "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate" @@ -65,9 +66,8 @@ import ( ) var ( - _ texttemplate.ExecHelper = (*templateExecHelper)(nil) - zero reflect.Value - contextInterface = reflect.TypeOf((*context.Context)(nil)).Elem() + _ texttemplate.ExecHelper = (*templateExecHelper)(nil) + zero reflect.Value ) type templateExecHelper struct { @@ -81,7 +81,7 @@ func (t *templateExecHelper) GetFunc(ctx context.Context, tmpl texttemplate.Prep if fn, found := t.funcs[name]; found { if fn.Type().NumIn() > 0 { first := fn.Type().In(0) - if first.Implements(contextInterface) { + if hreflect.IsContextType(first) { // TODO(bep) check if we can void this conversion every time -- and if that matters. // The first argument may be context.Context. This is never provided by the end user, but it's used to pass down // contextual information, e.g. the top level data context (e.g. Page). @@ -95,6 +95,13 @@ func (t *templateExecHelper) GetFunc(ctx context.Context, tmpl texttemplate.Prep } func (t *templateExecHelper) Init(ctx context.Context, tmpl texttemplate.Preparer) { + if t.running { + _, ok := tmpl.(identity.IdentityProvider) + if ok { + t.trackDependencies(ctx, tmpl, "", reflect.Value{}) + } + + } } func (t *templateExecHelper) GetMapValue(ctx context.Context, tmpl texttemplate.Preparer, receiver, key reflect.Value) (reflect.Value, bool) { @@ -116,22 +123,14 @@ func (t *templateExecHelper) GetMapValue(ctx context.Context, tmpl texttemplate. var typeParams = reflect.TypeOf(maps.Params{}) func (t *templateExecHelper) GetMethod(ctx context.Context, tmpl texttemplate.Preparer, receiver reflect.Value, name string) (method reflect.Value, firstArg reflect.Value) { - if t.running { - switch name { - case "GetPage", "Render": - if info, ok := tmpl.(tpl.Info); ok { - if m := receiver.MethodByName(name + "WithTemplateInfo"); m.IsValid() { - return m, reflect.ValueOf(info) - } - } - } - } - if strings.EqualFold(name, "mainsections") && receiver.Type() == typeParams && receiver.Pointer() == t.siteParams.Pointer() { - // MOved to site.MainSections in Hugo 0.112.0. + // Moved to site.MainSections in Hugo 0.112.0. receiver = t.site name = "MainSections" + } + if t.running { + ctx = t.trackDependencies(ctx, tmpl, name, receiver) } fn := hreflect.GetMethodByName(receiver, name) @@ -141,7 +140,7 @@ func (t *templateExecHelper) GetMethod(ctx context.Context, tmpl texttemplate.Pr if fn.Type().NumIn() > 0 { first := fn.Type().In(0) - if first.Implements(contextInterface) { + if hreflect.IsContextType(first) { // The first argument may be context.Context. This is never provided by the end user, but it's used to pass down // contextual information, e.g. the top level data context (e.g. Page). return fn, reflect.ValueOf(ctx) @@ -151,6 +150,43 @@ func (t *templateExecHelper) GetMethod(ctx context.Context, tmpl texttemplate.Pr return fn, zero } +func (t *templateExecHelper) trackDependencies(ctx context.Context, tmpl texttemplate.Preparer, name string, receiver reflect.Value) context.Context { + if tmpl == nil { + panic("must provide a template") + } + + idm := tpl.Context.GetDependencyManagerInCurrentScope(ctx) + if idm == nil { + return ctx + } + + if info, ok := tmpl.(identity.IdentityProvider); ok { + idm.AddIdentity(info.GetIdentity()) + } + + // The receive is the "." in the method execution or map lookup, e.g. the Page in .Resources. + if hreflect.IsValid(receiver) { + in := receiver.Interface() + + if idlp, ok := in.(identity.ForEeachIdentityByNameProvider); ok { + // This will skip repeated .RelPermalink usage on transformed resources + // which is not fingerprinted, e.g. to + // prevent all HTML pages to be re-rendered on a small CSS change. + idlp.ForEeachIdentityByName(name, func(id identity.Identity) bool { + idm.AddIdentity(id) + return false + }) + } else { + identity.WalkIdentitiesShallow(in, func(level int, id identity.Identity) bool { + idm.AddIdentity(id) + return false + }) + } + } + + return ctx +} + func newTemplateExecuter(d *deps.Deps) (texttemplate.Executer, map[string]reflect.Value) { funcs := createFuncMap(d) funcsv := make(map[string]reflect.Value) |