Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ If there is sufficient interest, [create a proposal]. Do not submit a pull reque

For a complete guide to contributing to Hugo, see the [Contribution Guide](CONTRIBUTING.md).

## License

For the Hugo source code, see [LICENSE](/LICENSE).

We also bundle some libraries in binary/WASM form:

* [libwebp](https://github.com/webmproject/libwebp), [BSD-3-Clause license](https://github.com/webmproject/libwebp?tab=BSD-3-Clause-1-ov-file#readme)
* [Katex](https://github.com/KaTeX/KaTeX), [MIT license](https://github.com/KaTeX/KaTeX?tab=MIT-1-ov-file#readme)
* [QuickJS](https://github.com/bellard/quickjs?tab=License-1-ov-file#readme), [License](https://github.com/bellard/quickjs?tab=License-1-ov-file#readme)

## Dependencies

Hugo stands on the shoulders of great open source libraries. Run `hugo env --logLevel info` to display a list of dependencies.
Expand Down
5 changes: 3 additions & 2 deletions hugoreleaser.env
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Release env.
# These will be replaced by script before release.
HUGORELEASER_TAG=v0.154.0
HUGORELEASER_COMMITISH=0b71db299a2bd89be876d7dc972ded03a222f560
HUGORELEASER_TAG=v0.154.1
HUGORELEASER_COMMITISH=e2fd6764be86d0cde988a7de6334fda0f43de871




Expand Down
86 changes: 86 additions & 0 deletions tpl/templates/decorator_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,89 @@ This construct creates a loop: {{PLACEHOLDER . }}
b.Assert(err.Error(), qt.Contains, "inner cannot be used inside a with block that wraps a partial decorator")
}
}

func TestPartialDecoratorInParens(t *testing.T) {
t.Parallel()

files := `
-- hugo.toml --
-- layouts/home.html --
Home.
{{ define "_partials/b.html" }}
<b>{{ inner . }}</b>
{{ end }}
{{ with (partial "b.html" "Important!") }}Notice: {{ . }}{{ end }}
`

b := hugolib.Test(t, files)

b.AssertFileContent("public/index.html", "<b>Notice: Important!</b>")
}

func TestPartialDecoratorBreakInWith(t *testing.T) {
t.Parallel()

filesTemplate := `
-- hugo.toml --
-- layouts/home.html --
Home.
{{ define "_partials/b.html" }}
<b>{{ inner . }}</b>
{{ end }}
{{ with (partial "b.html" "Important!") }}
{{ range seq 1 5 }}
{{ if eq . 3 }}
PLACEHOLDER
{{ end }}
Notice: {{ . }}
{{ end }}
{{ end }}
`

for _, placeholder := range []string{"{{ break }}", "{{ continue }}"} {
files := strings.ReplaceAll(filesTemplate, "PLACEHOLDER", placeholder)
b := hugolib.Test(t, files)
b.AssertFileContent("public/index.html", "Notice: 1", "Notice: 2", "! Notice: 3")
if strings.Contains(placeholder, "continue") {
b.AssertFileContent("public/index.html", "Notice: 4", "Notice: 5")
} else {
b.AssertFileContent("public/index.html", "! Notice: 4", "! Notice: 5")
}
}
}

func TestPartialWithBreakOutsideRange14333(t *testing.T) {
t.Parallel()

filesTemplate := `
-- hugo.toml --
-- layouts/home.html --
Home. {{ partial "a" }}:Done.
{{- define "_partials/a" }}
{{- $items := slice "a" "b" "c" }}
{{- range $items }}
{{- with partial "b" . -}}
PLACEHOLDER
{{- else }}
else: {{ . -}}
{{- end }}
{{- end }}
{{- end }}
{{- define "_partials/b" }}
{{ $b := true }}
{{ if ne . "b" }}
{{ $b = false }}
{{ end }}
{{ return $b }}
{{ end }}
`
for _, placeholder := range []string{"{{ break }}", "{{ continue }}", "{{- break }}"} {
files := strings.ReplaceAll(filesTemplate, "PLACEHOLDER", placeholder)
b := hugolib.Test(t, files)
if strings.Contains(placeholder, "continue") {
b.AssertFileContent("public/index.html", "else: c:Done.")
} else {
b.AssertFileContent("public/index.html", "else: a:Done.")
}
}
}
56 changes: 54 additions & 2 deletions tpl/tplimpl/templatetransform.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,20 @@ func (c *templateTransformContext) isWithPartial(args []parse.Node) bool {
return false
}

if id1, ok := args[0].(*parse.IdentifierNode); ok && (id1.Ident == "partial" || id1.Ident == "partialCached") {
first := args[0]

if pn, ok := first.(*parse.PipeNode); ok {
if len(pn.Cmds) == 0 || pn.Cmds[0] == nil {
return false
}
return c.isWithPartial(pn.Cmds[0].Args)
}

if id1, ok := first.(*parse.IdentifierNode); ok && (id1.Ident == "partial" || id1.Ident == "partialCached") {
return true
}

if chain, ok := args[0].(*parse.ChainNode); ok {
if chain, ok := first.(*parse.ChainNode); ok {
if id2, ok := chain.Node.(*parse.IdentifierNode); !ok || (id2.Ident != "partials") {
return false
}
Expand Down Expand Up @@ -266,12 +275,52 @@ const PartialDecoratorPrefix = "_internal/decorator_"

var templatesInnerRe = regexp.MustCompile(`{{\s*(templates\.Inner\b|inner\b)`)

// hasBreakOrContinueOutsideRange returns true if the given list node contains a break or continue statement without being nested in a range.
func (c *templateTransformContext) hasBreakOrContinueOutsideRange(n *parse.ListNode) bool {
if n == nil {
return false
}
for _, node := range n.Nodes {
switch x := node.(type) {
case *parse.ListNode:
if c.hasBreakOrContinueOutsideRange(x) {
return true
}
case *parse.RangeNode:
// skip
case *parse.IfNode:
if c.hasBreakOrContinueOutsideRange(x.List) {
return true
}
if c.hasBreakOrContinueOutsideRange(x.ElseList) {
return true
}
case *parse.WithNode:
if c.hasBreakOrContinueOutsideRange(x.List) {
return true
}
if c.hasBreakOrContinueOutsideRange(x.ElseList) {
return true
}
case *parse.BreakNode, *parse.ContinueNode:
return true

}
}
return false
}

func (c *templateTransformContext) handleWithPartial(withNode *parse.WithNode) {
withNodeInnerString := withNode.List.String()
if templatesInnerRe.MatchString(withNodeInnerString) {
c.err = fmt.Errorf("inner cannot be used inside a with block that wraps a partial decorator")
return
}

// See #14333. That is a very odd construct, but we need to guard against it.
if c.hasBreakOrContinueOutsideRange(withNode.List) {
return
}
innerHash := hashing.XxHashFromStringHexEncoded(c.t.Name() + withNodeInnerString)
internalPartialName := fmt.Sprintf("_partials/%s%s", PartialDecoratorPrefix, innerHash)

Expand Down Expand Up @@ -321,6 +370,9 @@ func (c *templateTransformContext) handleWithPartial(withNode *parse.WithNode) {
sn2 := setContext.(*parse.PipeNode).Cmds[0].Args[1].(*parse.PipeNode).Cmds[0].Args[0].(*parse.StringNode)
sn2.Text = innerHash
sn2.Quoted = fmt.Sprintf("%q", sn2.Text)
if pn, ok := withNode.Pipe.Cmds[0].Args[0].(*parse.PipeNode); ok {
withNode.Pipe.Cmds[0].Args = pn.Cmds[0].Args
}
withNode.Pipe.Cmds = append(orNode.Pipe.Cmds, withNode.Pipe.Cmds...)

withNode.List = newInner
Expand Down
Loading