diff options
author | Bjørn Erik Pedersen <[email protected]> | 2020-07-01 10:43:17 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2020-07-01 23:10:21 +0200 |
commit | 4a3efea7efe59cd3de7d0eb352836ab395a2b6b3 (patch) | |
tree | 99185f7c06113125ea474af365dcb32f356d68ed | |
parent | c66dc6c74fa3bbe308ccaade8c76071b49908129 (diff) | |
download | hugo-4a3efea7efe59cd3de7d0eb352836ab395a2b6b3.tar.gz hugo-4a3efea7efe59cd3de7d0eb352836ab395a2b6b3.zip |
Add support for inline partials
Fixes #7444
-rw-r--r-- | docs/content/en/templates/partials.md | 15 | ||||
-rw-r--r-- | hugolib/hugo_sites_build_errors_test.go | 5 | ||||
-rw-r--r-- | hugolib/template_test.go | 80 | ||||
-rw-r--r-- | tpl/tplimpl/template.go | 79 |
4 files changed, 175 insertions, 4 deletions
diff --git a/docs/content/en/templates/partials.md b/docs/content/en/templates/partials.md index 873f5e696..d23622ded 100644 --- a/docs/content/en/templates/partials.md +++ b/docs/content/en/templates/partials.md @@ -81,6 +81,21 @@ This means the partial will *only* be able to access those variables. The partia 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 */}} diff --git a/hugolib/hugo_sites_build_errors_test.go b/hugolib/hugo_sites_build_errors_test.go index d90a8b364..4de98a788 100644 --- a/hugolib/hugo_sites_build_errors_test.go +++ b/hugolib/hugo_sites_build_errors_test.go @@ -65,8 +65,7 @@ func TestSiteBuildErrors(t *testing.T) { 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 @@ func TestSiteBuildErrors(t *testing.T) { 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()) }, }, diff --git a/hugolib/template_test.go b/hugolib/template_test.go index 29993120d..673d91b5c 100644 --- a/hugolib/template_test.go +++ b/hugolib/template_test.go @@ -597,3 +597,83 @@ func collectIdentities(set map[identity.Identity]bool, provider identity.Provide 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 +`, + ) + +} diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go index 81b62b342..1243e6a15 100644 --- a/tpl/tplimpl/template.go +++ b/tpl/tplimpl/template.go @@ -553,12 +553,24 @@ func (t *templateHandler) addTemplateFile(name, path string) error { 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 } 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) noBaseNeeded(name string) bool { } 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) ParseInfo() tpl.ParseInfo { } 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 { @@ -960,3 +1018,22 @@ func unwrap(templ tpl.Template) tpl.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 + +} |