diff --git a/hugolib/hugo_sites_build_errors_test.go b/hugolib/hugo_sites_build_errors_test.go index 4de98a788..d90a8b364 100644 --- a/hugolib/hugo_sites_build_errors_test.go +++ b/hugolib/hugo_sites_build_errors_test.go @@ -65,7 +65,8 @@ func TestSiteBuildErrors(t *testing.T) { fileFixer: func(content string) string { return strings.Replace(content, ".Title }}", ".Title }", 1) }, - assertCreateError: func(a testSiteBuildErrorAsserter, err error) { + // Base templates gets parsed at build time. + assertBuildError: func(a testSiteBuildErrorAsserter, err error) { a.assertLineNumber(4, err) }, }, @@ -90,7 +91,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/_default/single.html:5:1\": parse failed: template: _default/single.html.___b:5: unexpected \"}\" in operand", fe.Error()) + a.assertErrorMessage("\"layouts/foo/single.html:5:1\": parse failed: template: foo/single.html:5: unexpected \"}\" in operand", fe.Error()) }, }, diff --git a/hugolib/template_test.go b/hugolib/template_test.go index 673d91b5c..a215f09dc 100644 --- a/hugolib/template_test.go +++ b/hugolib/template_test.go @@ -677,3 +677,46 @@ P3: Inline: p3 ) } + +// https://github.com/gohugoio/hugo/issues/7478 +func TestBaseWithAndWithoutDefine(t *testing.T) { + + b := newTestSitesBuilder(t) + + b.WithContent("p1.md", "---\ntitle: P\n---\nContent") + + b.WithTemplates( + "_default/baseof.html", ` +::Header Start:{{ block "header" . }}{{ end }}:Header End: +::{{ block "main" . }}Main{{ end }}:: +`, "index.html", ` +{{ define "header" }} +Home Header +{{ end }} +{{ define "main" }} +This is home main +{{ end }} +`, + + "_default/single.html", ` +{{ define "main" }} +This is single main +{{ end }} +`, + ) + + b.CreateSites().Build(BuildCfg{}) + + b.AssertFileContent("public/index.html", ` +Home Header +This is home main +`, + ) + + b.AssertFileContent("public/p1/index.html", ` + ::Header Start::Header End: +This is single main +`, + ) + +} diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go index 6171d167b..e3a4ce090 100644 --- a/tpl/tplimpl/template.go +++ b/tpl/tplimpl/template.go @@ -138,7 +138,7 @@ func newTemplateExec(d *deps.Deps) (*templateExec, error) { baseof: make(map[string]templateInfo), needsBaseof: make(map[string]templateInfo), - main: newTemplateNamespace(funcMap, false), + main: newTemplateNamespace(funcMap), Deps: d, layoutHandler: output.NewLayoutHandler(), @@ -174,17 +174,11 @@ func newTemplateExec(d *deps.Deps) (*templateExec, error) { return e, nil } -func newTemplateNamespace(funcs map[string]interface{}, lock bool) *templateNamespace { - var mu *sync.RWMutex - if lock { - mu = &sync.RWMutex{} - } - +func newTemplateNamespace(funcs map[string]interface{}) *templateNamespace { return &templateNamespace{ prototypeHTML: htmltemplate.New("").Funcs(funcs), prototypeText: texttemplate.New("").Funcs(funcs), templateStateMap: &templateStateMap{ - mu: mu, templates: make(map[string]*templateState), }, } @@ -426,6 +420,10 @@ func (t *templateHandler) findLayout(d output.LayoutDescriptor, f output.Format) t.applyTemplateTransformers(t.main, ts) + if err := t.extractPartials(ts.Template); err != nil { + return nil, false, err + } + return ts, true, nil } @@ -570,24 +568,12 @@ 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 } @@ -748,6 +734,38 @@ func (t *templateHandler) noBaseNeeded(name string) bool { return strings.Contains(name, "_markup/") } +func (t *templateHandler) extractPartials(templ tpl.Template) error { + templs := templates(templ) + for _, templ := range templs { + if templ.Name() == "" || !strings.HasPrefix(templ.Name(), "partials/") { + continue + } + + ts := newTemplateState(templ, templateInfo{name: templ.Name()}) + ts.typ = templatePartial + + t.main.mu.RLock() + _, found := t.main.templates[templ.Name()] + t.main.mu.RUnlock() + + if !found { + t.main.mu.Lock() + // This is a template defined inline. + _, err := applyTemplateTransformers(ts, t.main.newTemplateLookup(ts)) + if err != nil { + t.main.mu.Unlock() + return err + } + t.main.templates[templ.Name()] = ts + t.main.mu.Unlock() + + } + } + + return nil + +} + func (t *templateHandler) postTransform() error { defineCheckedHTML := false defineCheckedText := false @@ -774,25 +792,8 @@ func (t *templateHandler) postTransform() error { 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 - - } + if err := t.extractPartials(v.Template); err != nil { + return err } } @@ -828,20 +829,12 @@ type templateNamespace struct { *templateStateMap } -func (t templateNamespace) Clone(lock bool) *templateNamespace { - if t.mu != nil { - t.mu.Lock() - defer t.mu.Unlock() - } - - var mu *sync.RWMutex - if lock { - mu = &sync.RWMutex{} - } +func (t templateNamespace) Clone() *templateNamespace { + t.mu.Lock() + defer t.mu.Unlock() t.templateStateMap = &templateStateMap{ templates: make(map[string]*templateState), - mu: mu, } t.prototypeText = texttemplate.Must(t.prototypeText.Clone()) @@ -851,20 +844,14 @@ func (t templateNamespace) Clone(lock bool) *templateNamespace { } func (t *templateNamespace) Lookup(name string) (tpl.Template, bool) { - if t.mu != nil { - t.mu.RLock() - defer t.mu.RUnlock() - } + t.mu.RLock() + defer t.mu.RUnlock() templ, found := t.templates[name] if !found { return nil, false } - if t.mu != nil { - return &templateWrapperWithLock{RWMutex: t.mu, Template: templ}, true - } - return templ, found } @@ -892,10 +879,8 @@ func (t *templateNamespace) newTemplateLookup(in *templateState) func(name strin } func (t *templateNamespace) parse(info templateInfo) (*templateState, error) { - if t.mu != nil { - t.mu.Lock() - defer t.mu.Unlock() - } + t.mu.Lock() + defer t.mu.Unlock() if info.isText { prototype := t.prototypeText @@ -952,7 +937,7 @@ func isText(templ tpl.Template) bool { } type templateStateMap struct { - mu *sync.RWMutex // May be nil + mu sync.RWMutex templates map[string]*templateState }