Skip to content

Commit

Permalink
Allow for return partials with falsy arguments
Browse files Browse the repository at this point in the history
Partials with returns values are parsed, then inserted into a
partial return wrapper via wrapInPartialReturnWrapper in order
to assign the return value via *contextWrapper.Set. The
predefined wrapper template for partials inserts a partial's nodes
into a "with" template action in order to set dot to a
*contextWrapper within the partial. However, because "with" is
skipped if its argument is falsy, partials with falsy arguments
were not being evaluated.

This replaces the "with" action in the partial wrapper with a
"range" action that isn't skipped if .Arg is falsy.

Fixes #7528
  • Loading branch information
ptgott committed Dec 16, 2021
1 parent f4389e4 commit cb49424
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 22 deletions.
38 changes: 25 additions & 13 deletions hugolib/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,22 +456,34 @@ complex: 80: 80
`,
)
})
}

c.Run("Zero argument", func(c *qt.C) {
b := newBuilder(c)

b.WithTemplatesAdded(
"index.html", `
Test Partials With Return Values:
add42: fail: {{ partial "add42.tpl" 0 }}
// Issue 7528
func TestPartialWithZeroedArgs(t *testing.T) {

`,
)
b := newTestSitesBuilder(t)
b.WithTemplatesAdded("index.html",
`
X{{ partial "retval" dict }}X
X{{ partial "retval" slice }}X
X{{ partial "retval" "" }}X
X{{ partial "retval" false }}X
X{{ partial "retval" 0 }}X
{{ define "partials/retval" }}
{{ return 123 }}
{{ end }}`)

b.WithContentAdded("p.md", ``)
b.Build(BuildCfg{})
b.AssertFileContent("public/index.html",
`
X123X
X123X
X123X
X123X
X123X
`)

e := b.CreateSites().BuildE(BuildCfg{})
b.Assert(e, qt.Not(qt.IsNil))
})
}

func TestPartialCached(t *testing.T) {
Expand Down
5 changes: 0 additions & 5 deletions tpl/partials/partials.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import (
"strings"
"sync"

"github.com/gohugoio/hugo/common/hreflect"
texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"

"github.com/gohugoio/hugo/helpers"
Expand Down Expand Up @@ -121,10 +120,6 @@ func (ns *Namespace) Include(name string, contextList ...interface{}) (interface
var w io.Writer

if info.HasReturn {
if !hreflect.IsTruthful(context) {
// TODO(bep) we need to fix this, but it is non-trivial.
return nil, errors.New("partial that returns a value needs a non-zero argument.")
}
// Wrap the context sent to the template to capture the return value.
// Note that the template is rewritten to make sure that the dot (".")
// and the $ variable points to Arg.
Expand Down
10 changes: 6 additions & 4 deletions tpl/tplimpl/template_ast_transformers.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func getParseTree(templ tpl.Template) *parse.Tree {
}

const (
partialReturnWrapperTempl = `{{ $_hugo_dot := $ }}{{ $ := .Arg }}{{ with .Arg }}{{ $_hugo_dot.Set ("PLACEHOLDER") }}{{ end }}`
partialReturnWrapperTempl = `{{ $_hugo_dot := $ }}{{ $ := .Arg }}{{ range (slice .Arg) }}{{ $_hugo_dot.Set ("PLACEHOLDER") }}{{ end }}`
)

var partialReturnWrapper *parse.ListNode
Expand All @@ -125,16 +125,18 @@ func init() {
partialReturnWrapper = templ.Tree.Root
}

// wrapInPartialReturnWrapper copies and modifies the parsed nodes of a
// predefined partial return wrapper to insert those of a user-defined partial.
func (c *templateContext) wrapInPartialReturnWrapper(n *parse.ListNode) *parse.ListNode {
wrapper := partialReturnWrapper.CopyList()
withNode := wrapper.Nodes[2].(*parse.WithNode)
retn := withNode.List.Nodes[0]
rangeNode := wrapper.Nodes[2].(*parse.RangeNode)
retn := rangeNode.List.Nodes[0]
setCmd := retn.(*parse.ActionNode).Pipe.Cmds[0]
setPipe := setCmd.Args[1].(*parse.PipeNode)
// Replace PLACEHOLDER with the real return value.
// Note that this is a PipeNode, so it will be wrapped in parens.
setPipe.Cmds = []*parse.CommandNode{c.returnNode}
withNode.List.Nodes = append(n.Nodes, retn)
rangeNode.List.Nodes = append(n.Nodes, retn)

return wrapper
}
Expand Down

0 comments on commit cb49424

Please sign in to comment.