diff --git a/hugolib/template_test.go b/hugolib/template_test.go index e75bda790d6..44566f5a750 100644 --- a/hugolib/template_test.go +++ b/hugolib/template_test.go @@ -361,6 +361,43 @@ Base %d: {{ block "main" . }}FOO{{ end }} } +// https://github.com/gohugoio/hugo/issues/6790 +func TestTemplateNoBasePlease(t *testing.T) { + t.Parallel() + b := newTestSitesBuilder(t).WithSimpleConfigFile() + + b.WithTemplates("_default/list.html", ` + {{ define "main" }} + Bonjour + {{ end }} + + {{ printf "list" }} + + + `) + + b.WithTemplates( + "_default/single.html", ` +{{ printf "single" }} +{{ define "main" }} + Bonjour +{{ end }} + + +`) + + b.WithContent("blog/p1.md", `--- +title: The Page +--- +`) + + b.Build(BuildCfg{}) + + b.AssertFileContent("public/blog/p1/index.html", `single`) + b.AssertFileContent("public/blog/index.html", `list`) + +} + func TestTemplateLookupSite(t *testing.T) { t.Run("basic", func(t *testing.T) { t.Parallel() diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go index d0c656a2efb..a87cdde344d 100644 --- a/tpl/tplimpl/template.go +++ b/tpl/tplimpl/template.go @@ -72,7 +72,14 @@ var ( _ tpl.Info = (*templateState)(nil) ) -var defineRe = regexp.MustCompile(`{{-?\s?define`) +// A template needing a base template is a template with only define sections, +// but we check only for the start. +// If a base template does not exist, we will handle that when it's used. +var baseTemplateDefineRe = regexp.MustCompile(`^\s*{{-?\s*define`) + +func needsBaseTemplate(templ string) bool { + return baseTemplateDefineRe.MatchString(templ) +} func newIdentity(name string) identity.Manager { return identity.NewManager(identity.NewPathIdentity(files.ComponentFolderLayouts, name)) @@ -379,20 +386,19 @@ func (t *templateHandler) findLayout(d output.LayoutDescriptor, f output.Format) } } - if !found { - return nil, false, errors.Errorf("no baseof layout found for %q:", name) - } - templ, err := t.applyBaseTemplate(overlay, base) if err != nil { return nil, false, err } ts := newTemplateState(templ, overlay) - ts.baseInfo = base - // Add the base identity to detect changes - ts.Add(identity.NewPathIdentity(files.ComponentFolderLayouts, base.name)) + if found { + ts.baseInfo = base + + // Add the base identity to detect changes + ts.Add(identity.NewPathIdentity(files.ComponentFolderLayouts, base.name)) + } t.applyTemplateTransformers(t.main, ts) @@ -537,13 +543,13 @@ func (t *templateHandler) addTemplateFile(name, path string) error { return err } - if isBaseTemplate(name) { + if isBaseTemplatePath(name) { // Store it for later. t.baseof[name] = tinfo return nil } - needsBaseof := !t.noBaseNeeded(name) && defineRe.MatchString(tinfo.template) + needsBaseof := !t.noBaseNeeded(name) && baseTemplateDefineRe.MatchString(tinfo.template) if needsBaseof { t.needsBaseof[name] = tinfo return nil @@ -565,10 +571,18 @@ func (t *templateHandler) addTemplateTo(info templateInfo, to *templateNamespace func (t *templateHandler) applyBaseTemplate(overlay, base templateInfo) (tpl.Template, error) { if overlay.isText { - templ, err := t.main.prototypeTextClone.New(overlay.name).Parse(base.template) - if err != nil { - return nil, base.errWithFileContext("parse failed", err) + var ( + templ = t.main.prototypeTextClone.New(overlay.name) + err error + ) + + if !base.IsZero() { + templ, err = templ.Parse(base.template) + if err != nil { + return nil, base.errWithFileContext("parse failed", err) + } } + templ, err = templ.Parse(overlay.template) if err != nil { return nil, overlay.errWithFileContext("parse failed", err) @@ -576,9 +590,16 @@ func (t *templateHandler) applyBaseTemplate(overlay, base templateInfo) (tpl.Tem return templ, nil } - templ, err := t.main.prototypeHTMLClone.New(overlay.name).Parse(base.template) - if err != nil { - return nil, base.errWithFileContext("parse failed", err) + var ( + templ = t.main.prototypeHTMLClone.New(overlay.name) + err error + ) + + if !base.IsZero() { + templ, err = templ.Parse(base.template) + if err != nil { + return nil, base.errWithFileContext("parse failed", err) + } } templ, err = htmltemplate.Must(templ.Clone()).Parse(overlay.template) @@ -890,7 +911,7 @@ func isBackupFile(path string) bool { return path[len(path)-1] == '~' } -func isBaseTemplate(path string) bool { +func isBaseTemplatePath(path string) bool { return strings.Contains(filepath.Base(path), baseFileBase) } diff --git a/tpl/tplimpl/template_errors.go b/tpl/tplimpl/template_errors.go index 48818cb60bf..df80726f5d6 100644 --- a/tpl/tplimpl/template_errors.go +++ b/tpl/tplimpl/template_errors.go @@ -34,6 +34,10 @@ type templateInfo struct { realFilename string } +func (t templateInfo) IsZero() bool { + return t.name == "" +} + func (t templateInfo) resolveType() templateType { return resolveTemplateType(t.name) } diff --git a/tpl/tplimpl/template_test.go b/tpl/tplimpl/template_test.go new file mode 100644 index 00000000000..05be5bbb765 --- /dev/null +++ b/tpl/tplimpl/template_test.go @@ -0,0 +1,33 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package tplimpl + +import ( + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestNeedsBaseTemplate(t *testing.T) { + c := qt.New(t) + + c.Assert(needsBaseTemplate(`{{ define "main" }}`), qt.Equals, true) + c.Assert(needsBaseTemplate(`{{define "main" }}`), qt.Equals, true) + c.Assert(needsBaseTemplate(`{{- define "main" }}`), qt.Equals, true) + c.Assert(needsBaseTemplate(`{{-define "main" }}`), qt.Equals, true) + c.Assert(needsBaseTemplate(` {{ define "main" }}`), qt.Equals, true) + c.Assert(needsBaseTemplate(` +{{ define "main" }}`), qt.Equals, true) + c.Assert(needsBaseTemplate(` A {{ define "main" }}`), qt.Equals, false) + +}