ref: 4a3efea7efe59cd3de7d0eb352836ab395a2b6b3
parent: c66dc6c74fa3bbe308ccaade8c76071b49908129
author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
date: Wed Jul 1 06:43:17 EDT 2020
Add support for inline partials Fixes #7444
--- a/docs/content/en/templates/partials.md
+++ b/docs/content/en/templates/partials.md
@@ -81,6 +81,21 @@
In addition to outputting markup, partials can be used to return a value of any type. In order to return a value, a partial must include a lone `return` statement.
+## Inline partials
+
+{{< new-in "0.74.0" >}}
+
+You can also define partials inline in the template. But remember that template namespace is global, so you need to make sure that the names are unique to avoid conflicts.
+
+```go-html-template
+Value: {{ partial "my-inline-partial" . }}
+
+{{ define "partials/my-inline-partial" }}
+{{ $value := 32 }}
+{{ return $value }}
+{{ end }}
+```
+
### Example GetFeatured
```go-html-template
{{/* layouts/partials/GetFeatured.html */}}
--- a/hugolib/hugo_sites_build_errors_test.go
+++ b/hugolib/hugo_sites_build_errors_test.go
@@ -65,8 +65,7 @@
fileFixer: func(content string) string {
return strings.Replace(content, ".Title }}", ".Title }", 1)
},
- // Base templates gets parsed at build time.
- assertBuildError: func(a testSiteBuildErrorAsserter, err error) {
+ assertCreateError: func(a testSiteBuildErrorAsserter, err error) {
a.assertLineNumber(4, err)
},
},
@@ -91,7 +90,7 @@
a.c.Assert(fe.Position().LineNumber, qt.Equals, 5)
a.c.Assert(fe.Position().ColumnNumber, qt.Equals, 1)
a.c.Assert(fe.ChromaLexer, qt.Equals, "go-html-template")
- a.assertErrorMessage("\"layouts/foo/single.html:5:1\": parse failed: template: foo/single.html:5: unexpected \"}\" in operand", fe.Error())
+ a.assertErrorMessage("\"layouts/_default/single.html:5:1\": parse failed: template: _default/single.html.___b:5: unexpected \"}\" in operand", fe.Error())
},
},
--- a/hugolib/template_test.go
+++ b/hugolib/template_test.go
@@ -597,3 +597,83 @@
func ident(level int) string {
return strings.Repeat(" ", level)
}
+
+func TestPartialInline(t *testing.T) {
+
+ b := newTestSitesBuilder(t)
+
+ b.WithContent("p1.md", "")
+
+ b.WithTemplates(
+ "index.html", `
+
+{{ $p1 := partial "p1" . }}
+{{ $p2 := partial "p2" . }}
+
+P1: {{ $p1 }}
+P2: {{ $p2 }}
+
+{{ define "partials/p1" }}Inline: p1{{ end }}
+
+{{ define "partials/p2" }}
+{{ $value := 32 }}
+{{ return $value }}
+{{ end }}
+
+
+`,
+ )
+
+ b.CreateSites().Build(BuildCfg{})
+
+ b.AssertFileContent("public/index.html",
+ `
+P1: Inline: p1
+P2: 32`,
+ )
+
+}
+
+func TestPartialInlineBase(t *testing.T) {
+
+ b := newTestSitesBuilder(t)
+
+ b.WithContent("p1.md", "")
+
+ b.WithTemplates(
+ "baseof.html", `{{ $p3 := partial "p3" . }}P3: {{ $p3 }}
+{{ block "main" . }}{{ end }}{{ define "partials/p3" }}Inline: p3{{ end }}`,
+ "index.html", `
+{{ define "main" }}
+
+{{ $p1 := partial "p1" . }}
+{{ $p2 := partial "p2" . }}
+
+P1: {{ $p1 }}
+P2: {{ $p2 }}
+
+{{ end }}
+
+
+{{ define "partials/p1" }}Inline: p1{{ end }}
+
+{{ define "partials/p2" }}
+{{ $value := 32 }}
+{{ return $value }}
+{{ end }}
+
+
+`,
+ )
+
+ b.CreateSites().Build(BuildCfg{})
+
+ b.AssertFileContent("public/index.html",
+ `
+P1: Inline: p1
+P2: 32
+P3: Inline: p3
+`,
+ )
+
+}
--- a/tpl/tplimpl/template.go
+++ b/tpl/tplimpl/template.go
@@ -553,6 +553,12 @@
if isBaseTemplatePath(name) {
// Store it for later.
t.baseof[name] = tinfo
+ // Also parse and add it on its own to make sure we reach the inline partials.
+ tinfo.name = name + ".___b"
+ _, err := t.addTemplateTo(tinfo, t.main)
+ if err != nil {
+ return tinfo.errWithFileContext("parse failed", err)
+ }
return nil
}
@@ -559,6 +565,12 @@
needsBaseof := !t.noBaseNeeded(name) && needsBaseTemplate(tinfo.template)
if needsBaseof {
t.needsBaseof[name] = tinfo
+ // Also parse and add it on its own to make sure we reach the inline partials.
+ tinfo.name = name + ".___b"
+ _, err := t.addTemplateTo(tinfo, t.main)
+ if err != nil {
+ return tinfo.errWithFileContext("parse failed", err)
+ }
return nil
}
@@ -720,10 +732,51 @@
}
func (t *templateHandler) postTransform() error {
+ defineCheckedHTML := false
+ defineCheckedText := false
+
for _, v := range t.main.templates {
if v.typ == templateShortcode {
t.addShortcodeVariant(v)
}
+
+ if defineCheckedHTML && defineCheckedText {
+ continue
+ }
+
+ isText := isText(v.Template)
+ if isText {
+ if defineCheckedText {
+ continue
+ }
+ defineCheckedText = true
+ } else {
+ if defineCheckedHTML {
+ continue
+ }
+ defineCheckedHTML = true
+ }
+
+ templs := templates(v.Template)
+ for _, templ := range templs {
+ if templ.Name() == "" || !strings.HasPrefix(templ.Name(), "partials/") {
+ continue
+ }
+
+ ts := newTemplateState(templ, templateInfo{name: templ.Name()})
+ ts.typ = templatePartial
+
+ if _, found := t.main.templates[templ.Name()]; !found {
+ // This is a template defined inline.
+
+ _, err := applyTemplateTransformers(ts, t.main.newTemplateLookup(ts))
+ if err != nil {
+ return err
+ }
+ t.main.templates[templ.Name()] = ts
+
+ }
+ }
}
for name, source := range t.transformNotFound {
@@ -872,8 +925,13 @@
}
func (t *templateState) isText() bool {
- _, isText := t.Template.(*texttemplate.Template)
+ return isText(t.Template)
+}
+
+func isText(templ tpl.Template) bool {
+ _, isText := templ.(*texttemplate.Template)
return isText
+
}
type templateStateMap struct {
@@ -959,4 +1017,23 @@
return ts.Template
}
return templ
+}
+
+func templates(in tpl.Template) []tpl.Template {
+ var templs []tpl.Template
+ in = unwrap(in)
+ if textt, ok := in.(*texttemplate.Template); ok {
+ for _, t := range textt.Templates() {
+ templs = append(templs, t)
+ }
+ }
+
+ if htmlt, ok := in.(*htmltemplate.Template); ok {
+ for _, t := range htmlt.Templates() {
+ templs = append(templs, t)
+ }
+ }
+
+ return templs
+
}