summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--commands/server.go2
-rw-r--r--common/hreflect/helpers.go17
-rw-r--r--hugolib/alias.go5
-rw-r--r--hugolib/hugo_sites.go5
-rw-r--r--hugolib/page__per_output.go3
-rw-r--r--hugolib/site.go25
-rw-r--r--hugolib/site_render.go6
-rw-r--r--markup/converter/converter.go4
-rw-r--r--markup/converter/hooks/hooks.go7
-rw-r--r--markup/goldmark/codeblocks/render.go1
-rw-r--r--markup/goldmark/render_hooks.go4
-rw-r--r--markup/highlight/highlight.go3
-rw-r--r--resources/errorResource.go4
-rw-r--r--resources/page/pagegroup.go7
-rw-r--r--resources/resource.go1
-rw-r--r--resources/resource_transformers/templates/execute_as_template.go7
-rw-r--r--resources/transform.go15
-rw-r--r--tpl/cast/init.go4
-rw-r--r--tpl/collections/apply.go10
-rw-r--r--tpl/collections/init.go4
-rw-r--r--tpl/compare/init.go4
-rw-r--r--tpl/crypto/init.go4
-rw-r--r--tpl/css/css.go4
-rw-r--r--tpl/data/init.go4
-rw-r--r--tpl/debug/init.go4
-rw-r--r--tpl/diagrams/init.go4
-rw-r--r--tpl/encoding/init.go4
-rw-r--r--tpl/fmt/init.go4
-rw-r--r--tpl/hugo/init.go4
-rw-r--r--tpl/images/init.go4
-rw-r--r--tpl/inflect/init.go4
-rw-r--r--tpl/internal/go_templates/texttemplate/hugo_template.go36
-rw-r--r--tpl/internal/templatefuncsRegistry.go11
-rw-r--r--tpl/js/init.go4
-rw-r--r--tpl/lang/init.go4
-rw-r--r--tpl/math/init.go4
-rw-r--r--tpl/openapi/openapi3/init.go4
-rw-r--r--tpl/os/init.go4
-rw-r--r--tpl/page/init.go49
-rw-r--r--tpl/page/integration_test.go179
-rw-r--r--tpl/partials/init.go4
-rw-r--r--tpl/path/init.go3
-rw-r--r--tpl/reflect/init.go4
-rw-r--r--tpl/resources/init.go4
-rw-r--r--tpl/resources/resources.go8
-rw-r--r--tpl/safe/init.go4
-rw-r--r--tpl/site/init.go4
-rw-r--r--tpl/strings/init.go4
-rw-r--r--tpl/template.go17
-rw-r--r--tpl/templates/init.go4
-rw-r--r--tpl/time/init.go3
-rw-r--r--tpl/tplimpl/template.go4
-rw-r--r--tpl/transform/init.go4
-rw-r--r--tpl/urls/init.go4
54 files changed, 436 insertions, 108 deletions
diff --git a/commands/server.go b/commands/server.go
index 332e35c01..4f7d4db8f 100644
--- a/commands/server.go
+++ b/commands/server.go
@@ -577,7 +577,7 @@ func (c *commandeer) serve(s *serverCmd) error {
// to cached values if nil.
templ, handler := getErrorTemplateAndHandler(c.hugoTry())
b := &bytes.Buffer{}
- err := handler.Execute(templ, b, ctx)
+ err := handler.ExecuteWithContext(context.Background(), templ, b, ctx)
return b, err
},
}
diff --git a/common/hreflect/helpers.go b/common/hreflect/helpers.go
index 1b7e5acf7..17afbf912 100644
--- a/common/hreflect/helpers.go
+++ b/common/hreflect/helpers.go
@@ -208,6 +208,23 @@ func AsTime(v reflect.Value, loc *time.Location) (time.Time, bool) {
return time.Time{}, false
}
+func CallMethodByName(cxt context.Context, name string, v reflect.Value) []reflect.Value {
+ fn := v.MethodByName(name)
+ var args []reflect.Value
+ tp := fn.Type()
+ if tp.NumIn() > 0 {
+ if tp.NumIn() > 1 {
+ panic("not supported")
+ }
+ first := tp.In(0)
+ if first.Implements(ContextInterface) {
+ args = append(args, reflect.ValueOf(cxt))
+ }
+ }
+
+ return fn.Call(args)
+}
+
// Based on: https://github.com/golang/go/blob/178a2c42254166cffed1b25fb1d3c7a5727cada6/src/text/template/exec.go#L931
func indirectInterface(v reflect.Value) reflect.Value {
if v.Kind() != reflect.Interface {
diff --git a/hugolib/alias.go b/hugolib/alias.go
index 2609cd6bb..071f73d41 100644
--- a/hugolib/alias.go
+++ b/hugolib/alias.go
@@ -15,6 +15,7 @@ package hugolib
import (
"bytes"
+ "context"
"errors"
"fmt"
"io"
@@ -64,8 +65,10 @@ func (a aliasHandler) renderAlias(permalink string, p page.Page) (io.Reader, err
p,
}
+ ctx := tpl.SetPageInContext(context.Background(), p)
+
buffer := new(bytes.Buffer)
- err := a.t.Execute(templ, buffer, data)
+ err := a.t.ExecuteWithContext(ctx, templ, buffer, data)
if err != nil {
return nil, err
}
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
index cdc5d97fb..65cc23971 100644
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -767,10 +767,11 @@ func (h *HugoSites) renderCrossSitesSitemap() error {
}
s := h.Sites[0]
+ // We don't have any page context to pass in here.
+ ctx := context.Background()
templ := s.lookupLayouts("sitemapindex.xml", "_default/sitemapindex.xml", "_internal/_default/sitemapindex.xml")
-
- return s.renderAndWriteXML(&s.PathSpec.ProcessingStats.Sitemaps, "sitemapindex",
+ return s.renderAndWriteXML(ctx, &s.PathSpec.ProcessingStats.Sitemaps, "sitemapindex",
s.siteCfg.sitemap.Filename, h.toSiteInfos(), templ)
}
diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go
index ce3498e0e..be65ad9e7 100644
--- a/hugolib/page__per_output.go
+++ b/hugolib/page__per_output.go
@@ -740,6 +740,7 @@ func (cp *pageContentOutput) ParseContent(ctx context.Context, content []byte) (
return nil, ok, nil
}
rctx := converter.RenderContext{
+ Ctx: ctx,
Src: content,
RenderTOC: true,
GetRenderer: cp.renderHooks.getRenderer,
@@ -758,6 +759,7 @@ func (cp *pageContentOutput) RenderContent(ctx context.Context, content []byte,
return nil, ok, nil
}
rctx := converter.RenderContext{
+ Ctx: ctx,
Src: content,
RenderTOC: true,
GetRenderer: cp.renderHooks.getRenderer,
@@ -777,6 +779,7 @@ func (cp *pageContentOutput) RenderContent(ctx context.Context, content []byte,
func (cp *pageContentOutput) renderContentWithConverter(ctx context.Context, c converter.Converter, content []byte, renderTOC bool) (converter.ResultRender, error) {
r, err := c.Convert(
converter.RenderContext{
+ Ctx: ctx,
Src: content,
RenderTOC: renderTOC,
GetRenderer: cp.renderHooks.getRenderer,
diff --git a/hugolib/site.go b/hugolib/site.go
index e90fa41ff..45b4643f0 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -1710,12 +1710,12 @@ func (s *Site) lookupLayouts(layouts ...string) tpl.Template {
return nil
}
-func (s *Site) renderAndWriteXML(statCounter *uint64, name string, targetPath string, d any, templ tpl.Template) error {
+func (s *Site) renderAndWriteXML(ctx context.Context, statCounter *uint64, name string, targetPath string, d any, templ tpl.Template) error {
s.Log.Debugf("Render XML for %q to %q", name, targetPath)
renderBuffer := bp.GetBuffer()
defer bp.PutBuffer(renderBuffer)
- if err := s.renderForTemplate(name, "", d, renderBuffer, templ); err != nil {
+ if err := s.renderForTemplate(ctx, name, "", d, renderBuffer, templ); err != nil {
return err
}
@@ -1739,8 +1739,9 @@ func (s *Site) renderAndWritePage(statCounter *uint64, name string, targetPath s
defer bp.PutBuffer(renderBuffer)
of := p.outputFormat()
+ ctx := tpl.SetPageInContext(context.Background(), p)
- if err := s.renderForTemplate(p.Kind(), of.Name, p, renderBuffer, templ); err != nil {
+ if err := s.renderForTemplate(ctx, p.Kind(), of.Name, p, renderBuffer, templ); err != nil {
return err
}
@@ -1797,16 +1798,16 @@ type hookRendererTemplate struct {
resolvePosition func(ctx any) text.Position
}
-func (hr hookRendererTemplate) RenderLink(w io.Writer, ctx hooks.LinkContext) error {
- return hr.templateHandler.Execute(hr.templ, w, ctx)
+func (hr hookRendererTemplate) RenderLink(cctx context.Context, w io.Writer, ctx hooks.LinkContext) error {
+ return hr.templateHandler.ExecuteWithContext(cctx, hr.templ, w, ctx)
}
-func (hr hookRendererTemplate) RenderHeading(w io.Writer, ctx hooks.HeadingContext) error {
- return hr.templateHandler.Execute(hr.templ, w, ctx)
+func (hr hookRendererTemplate) RenderHeading(cctx context.Context, w io.Writer, ctx hooks.HeadingContext) error {
+ return hr.templateHandler.ExecuteWithContext(cctx, hr.templ, w, ctx)
}
-func (hr hookRendererTemplate) RenderCodeblock(w hugio.FlexiWriter, ctx hooks.CodeblockContext) error {
- return hr.templateHandler.Execute(hr.templ, w, ctx)
+func (hr hookRendererTemplate) RenderCodeblock(cctx context.Context, w hugio.FlexiWriter, ctx hooks.CodeblockContext) error {
+ return hr.templateHandler.ExecuteWithContext(cctx, hr.templ, w, ctx)
}
func (hr hookRendererTemplate) ResolvePosition(ctx any) text.Position {
@@ -1817,13 +1818,15 @@ func (hr hookRendererTemplate) IsDefaultCodeBlockRenderer() bool {
return false
}
-func (s *Site) renderForTemplate(name, outputFormat string, d any, w io.Writer, templ tpl.Template) (err error) {
+func (s *Site) renderForTemplate(ctx context.Context, name, outputFormat string, d any, w io.Writer, templ tpl.Template) (err error) {
if templ == nil {
s.logMissingLayout(name, "", "", outputFormat)
return nil
}
- ctx := context.Background()
+ if ctx == nil {
+ panic("nil context")
+ }
if err = s.Tmpl().ExecuteWithContext(ctx, templ, w, d); err != nil {
return fmt.Errorf("render of %q failed: %w", name, err)
diff --git a/hugolib/site_render.go b/hugolib/site_render.go
index 51d638dde..f105a1ae4 100644
--- a/hugolib/site_render.go
+++ b/hugolib/site_render.go
@@ -14,6 +14,7 @@
package hugolib
import (
+ "context"
"fmt"
"path"
"strings"
@@ -197,7 +198,7 @@ func (s *Site) renderPaginator(p *pageState, templ tpl.Template) error {
d.Addends = fmt.Sprintf("/%s/%d", paginatePath, 1)
targetPaths := page.CreateTargetPaths(d)
- if err := s.writeDestAlias(targetPaths.TargetFilename, p.Permalink(), f, nil); err != nil {
+ if err := s.writeDestAlias(targetPaths.TargetFilename, p.Permalink(), f, p); err != nil {
return err
}
}
@@ -278,6 +279,7 @@ func (s *Site) renderSitemap() error {
}
targetPath := p.targetPaths().TargetFilename
+ ctx := tpl.SetPageInContext(context.Background(), p)
if targetPath == "" {
return errors.New("failed to create targetPath for sitemap")
@@ -285,7 +287,7 @@ func (s *Site) renderSitemap() error {
templ := s.lookupLayouts("sitemap.xml", "_default/sitemap.xml", "_internal/_default/sitemap.xml")
- return s.renderAndWriteXML(&s.PathSpec.ProcessingStats.Sitemaps, "sitemap", targetPath, p, templ)
+ return s.renderAndWriteXML(ctx, &s.PathSpec.ProcessingStats.Sitemaps, "sitemap", targetPath, p, templ)
}
func (s *Site) renderRobotsTXT() error {
diff --git a/markup/converter/converter.go b/markup/converter/converter.go
index e5a07f1a1..544d4841a 100644
--- a/markup/converter/converter.go
+++ b/markup/converter/converter.go
@@ -15,6 +15,7 @@ package converter
import (
"bytes"
+ "context"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
@@ -141,6 +142,9 @@ type DocumentContext struct {
// RenderContext holds contextual information about the content to render.
type RenderContext struct {
+ // Ctx is the context.Context for the current Page render.
+ Ctx context.Context
+
// Src is the content to render.
Src []byte
diff --git a/markup/converter/hooks/hooks.go b/markup/converter/hooks/hooks.go
index 7eede0710..55d7c1127 100644
--- a/markup/converter/hooks/hooks.go
+++ b/markup/converter/hooks/hooks.go
@@ -14,6 +14,7 @@
package hooks
import (
+ "context"
"io"
"github.com/gohugoio/hugo/common/hugio"
@@ -85,12 +86,12 @@ type AttributesOptionsSliceProvider interface {
}
type LinkRenderer interface {
- RenderLink(w io.Writer, ctx LinkContext) error
+ RenderLink(cctx context.Context, w io.Writer, ctx LinkContext) error
identity.Provider
}
type CodeBlockRenderer interface {
- RenderCodeblock(w hugio.FlexiWriter, ctx CodeblockContext) error
+ RenderCodeblock(cctx context.Context, w hugio.FlexiWriter, ctx CodeblockContext) error
identity.Provider
}
@@ -119,7 +120,7 @@ type HeadingContext interface {
// HeadingRenderer describes a uniquely identifiable rendering hook.
type HeadingRenderer interface {
// Render writes the rendered content to w using the data in w.
- RenderHeading(w io.Writer, ctx HeadingContext) error
+ RenderHeading(cctx context.Context, w io.Writer, ctx HeadingContext) error
identity.Provider
}
diff --git a/markup/goldmark/codeblocks/render.go b/markup/goldmark/codeblocks/render.go
index 739781de1..cf5a0f296 100644
--- a/markup/goldmark/codeblocks/render.go
+++ b/markup/goldmark/codeblocks/render.go
@@ -124,6 +124,7 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No
cr := renderer.(hooks.CodeBlockRenderer)
err := cr.RenderCodeblock(
+ ctx.RenderContext().Ctx,
w,
cbctx,
)
diff --git a/markup/goldmark/render_hooks.go b/markup/goldmark/render_hooks.go
index f36f9f4e6..0bd800dc0 100644
--- a/markup/goldmark/render_hooks.go
+++ b/markup/goldmark/render_hooks.go
@@ -181,6 +181,7 @@ func (r *hookedRenderer) renderImage(w util.BufWriter, source []byte, node ast.N
attrs := r.filterInternalAttributes(n.Attributes())
err := lr.RenderLink(
+ ctx.RenderContext().Ctx,
w,
imageLinkContext{
linkContext: linkContext{
@@ -271,6 +272,7 @@ func (r *hookedRenderer) renderLink(w util.BufWriter, source []byte, node ast.No
ctx.Buffer.Truncate(pos)
err := lr.RenderLink(
+ ctx.RenderContext().Ctx,
w,
linkContext{
page: ctx.DocumentContext().Document,
@@ -340,6 +342,7 @@ func (r *hookedRenderer) renderAutoLink(w util.BufWriter, source []byte, node as
}
err := lr.RenderLink(
+ ctx.RenderContext().Ctx,
w,
linkContext{
page: ctx.DocumentContext().Document,
@@ -428,6 +431,7 @@ func (r *hookedRenderer) renderHeading(w util.BufWriter, source []byte, node ast
anchor := anchori.([]byte)
err := hr.RenderHeading(
+ ctx.RenderContext().Ctx,
w,
headingContext{
page: ctx.DocumentContext().Document,
diff --git a/markup/highlight/highlight.go b/markup/highlight/highlight.go
index b74997700..410beb740 100644
--- a/markup/highlight/highlight.go
+++ b/markup/highlight/highlight.go
@@ -14,6 +14,7 @@
package highlight
import (
+ "context"
"fmt"
gohtml "html"
"html/template"
@@ -122,7 +123,7 @@ func (h chromaHighlighter) HighlightCodeBlock(ctx hooks.CodeblockContext, opts a
}, nil
}
-func (h chromaHighlighter) RenderCodeblock(w hugio.FlexiWriter, ctx hooks.CodeblockContext) error {
+func (h chromaHighlighter) RenderCodeblock(cctx context.Context, w hugio.FlexiWriter, ctx hooks.CodeblockContext) error {
cfg := h.cfg
attributes := ctx.(hooks.AttributesOptionsSliceProvider).AttributesSlice()
diff --git a/resources/errorResource.go b/resources/errorResource.go
index 42edb0bd0..c8c32dfc3 100644
--- a/resources/errorResource.go
+++ b/resources/errorResource.go
@@ -135,3 +135,7 @@ func (e *errorResource) DecodeImage() (image.Image, error) {
func (e *errorResource) Transform(...ResourceTransformation) (ResourceTransformer, error) {
panic(e.ResourceError)
}
+
+func (e *errorResource) TransformWithContext(context.Context, ...ResourceTransformation) (ResourceTransformer, error) {
+ panic(e.ResourceError)
+}
diff --git a/resources/page/pagegroup.go b/resources/page/pagegroup.go
index bac5d8327..99f1af3ff 100644
--- a/resources/page/pagegroup.go
+++ b/resources/page/pagegroup.go
@@ -159,12 +159,7 @@ func (p Pages) GroupBy(ctx context.Context, key string, order ...string) (PagesG
case reflect.StructField:
fv = ppv.Elem().FieldByName(key)
case reflect.Method:
- var args []reflect.Value
- fn := hreflect.GetMethodByName(ppv, key)
- if fn.Type().NumIn() > 0 && fn.Type().In(0).Implements(hreflect.ContextInterface) {
- args = []reflect.Value{reflect.ValueOf(ctx)}
- }
- fv = fn.Call(args)[0]
+ fv = hreflect.CallMethodByName(ctx, key, ppv)[0]
}
if !fv.IsValid() {
continue
diff --git a/resources/resource.go b/resources/resource.go
index 8a524247a..7ccc5da39 100644
--- a/resources/resource.go
+++ b/resources/resource.go
@@ -104,6 +104,7 @@ type ResourceTransformer interface {
type Transformer interface {
Transform(...ResourceTransformation) (ResourceTransformer, error)
+ TransformWithContext(context.Context, ...ResourceTransformation) (ResourceTransformer, error)
}
func NewFeatureNotAvailableTransformer(key string, elements ...any) ResourceTransformation {
diff --git a/resources/resource_transformers/templates/execute_as_template.go b/resources/resource_transformers/templates/execute_as_template.go
index 5fe4230f1..4d4aba396 100644
--- a/resources/resource_transformers/templates/execute_as_template.go
+++ b/resources/resource_transformers/templates/execute_as_template.go
@@ -15,6 +15,7 @@
package templates
import (
+ "context"
"fmt"
"github.com/gohugoio/hugo/helpers"
@@ -61,11 +62,11 @@ func (t *executeAsTemplateTransform) Transform(ctx *resources.ResourceTransforma
ctx.OutPath = t.targetPath
- return t.t.Tmpl().Execute(templ, ctx.To, t.data)
+ return t.t.Tmpl().ExecuteWithContext(ctx.Ctx, templ, ctx.To, t.data)
}
-func (c *Client) ExecuteAsTemplate(res resources.ResourceTransformer, targetPath string, data any) (resource.Resource, error) {
- return res.Transform(&executeAsTemplateTransform{
+func (c *Client) ExecuteAsTemplate(ctx context.Context, res resources.ResourceTransformer, targetPath string, data any) (resource.Resource, error) {
+ return res.TransformWithContext(ctx, &executeAsTemplateTransform{
rs: c.rs,
targetPath: helpers.ToSlashTrimLeading(targetPath),
t: c.t,
diff --git a/resources/transform.go b/resources/transform.go
index 3477c710f..4ab51485e 100644
--- a/resources/transform.go
+++ b/resources/transform.go
@@ -69,6 +69,7 @@ func newResourceAdapter(spec *Spec, lazyPublish bool, target transformableResour
return &resourceAdapter{
resourceTransformations: &resourceTransformations{},
resourceAdapterInner: &resourceAdapterInner{
+ ctx: context.TODO(),
spec: spec,
publishOnce: po,
target: target,
@@ -84,6 +85,9 @@ type ResourceTransformation interface {
}
type ResourceTransformationCtx struct {
+ // The context that started the transformation.
+ Ctx context.Context
+
// The content to transform.
From io.Reader
@@ -180,6 +184,7 @@ func (r *resourceAdapter) Data() any {
func (r resourceAdapter) cloneTo(targetPath string) resource.Resource {
newtTarget := r.target.cloneTo(targetPath)
newInner := &resourceAdapterInner{
+ ctx: r.ctx,
spec: r.spec,
target: newtTarget.(transformableResource),
}
@@ -278,11 +283,17 @@ func (r *resourceAdapter) Title() string {
}
func (r resourceAdapter) Transform(t ...ResourceTransformation) (ResourceTransformer, error) {
+ return r.TransformWithContext(context.Background(), t...)
+}
+
+func (r resourceAdapter) TransformWithContext(ctx context.Context, t ...ResourceTransform