diff --git a/tpl/templates/templates_integration_test.go b/tpl/templates/templates_integration_test.go index 4150ec367..93922b4c4 100644 --- a/tpl/templates/templates_integration_test.go +++ b/tpl/templates/templates_integration_test.go @@ -214,3 +214,18 @@ All. b.AssertFileContent("public/index.html", "All.") b.AssertFileContent("public/amp/index.html", "All.") } + +// Issue #13584. +func TestLegacySectionSection(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +-- content/mysection/_index.md -- +-- layouts/section/section.html -- +layouts/section/section.html + +` + b := hugolib.Test(t, files) + b.AssertFileContent("public/mysection/index.html", "layouts/section/section.html") +} diff --git a/tpl/tplimpl/templates.go b/tpl/tplimpl/templates.go index 1f911b9a5..4c3ad3be1 100644 --- a/tpl/tplimpl/templates.go +++ b/tpl/tplimpl/templates.go @@ -3,7 +3,9 @@ package tplimpl import ( "io" "regexp" + "strconv" "strings" + "sync/atomic" "unicode" "unicode/utf8" @@ -49,11 +51,6 @@ func (t *templateNamespace) parseTemplate(ti *TemplInfo) error { } pi := ti.PathInfo name := pi.PathNoLeadingSlash() - if ti.isLegacyMapped { - // When mapping the old taxonomy structure to the new one, we may map the same path to multiple templates per kind. - // Append the kind here to make the name unique. - name += ("-" + ti.D.Kind) - } var ( templ tpl.Template @@ -62,12 +59,18 @@ func (t *templateNamespace) parseTemplate(ti *TemplInfo) error { if ti.D.IsPlainText { prototype := t.parseText + if prototype.Lookup(name) != nil { + name += "-" + strconv.FormatUint(t.nameCounter.Add(1), 10) + } templ, err = prototype.New(name).Parse(ti.content) if err != nil { return err } } else { prototype := t.parseHTML + if prototype.Lookup(name) != nil { + name += "-" + strconv.FormatUint(t.nameCounter.Add(1), 10) + } templ, err = prototype.New(name).Parse(ti.content) if err != nil { return err @@ -296,6 +299,8 @@ type templateNamespace struct { prototypeText *texttemplate.Template prototypeHTML *htmltemplate.Template + nameCounter atomic.Uint64 + standaloneText *texttemplate.Template baseofTextClones []*texttemplate.Template diff --git a/tpl/tplimpl/templatestore.go b/tpl/tplimpl/templatestore.go index 3c8c4abf2..8fe89586d 100644 --- a/tpl/tplimpl/templatestore.go +++ b/tpl/tplimpl/templatestore.go @@ -690,7 +690,7 @@ func (t *TemplateStore) UnusedTemplates() []*TemplInfo { var unused []*TemplInfo for vv := range t.templates() { - if vv.subCategory != SubCategoryMain { + if vv.subCategory != SubCategoryMain || vv.isLegacyMapped { // Skip inline partials and internal templates. continue } @@ -1169,8 +1169,8 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo case containerPartials, containerShortcodes, containerMarkup: // OK. default: - applyLegacyMapping = true pi = fromLegacyPath(pi) + applyLegacyMapping = strings.Count(pi.Path(), "/") <= 2 } if applyLegacyMapping { @@ -1183,6 +1183,7 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo ext: pi.Ext(), outputFormat: pi.OutputFormat(), } + if m2, ok := legacyOrdinalMappings[key]; ok { if m1.ordinal < m2.m.ordinal { // Higher up == better match. @@ -1208,26 +1209,74 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo base := piOrig.PathBeforeLangAndOutputFormatAndExt() identifiers := pi.IdentifiersUnknown() - - // Tokens on e.g. form /SECTIONKIND/THESECTION - insertSectionTokens := func(section string, kindOnly bool) string { - s := base - if !kindOnly { - s = strings.Replace(s, section, sectionToken, 1) - } - s = strings.Replace(s, kinds.KindSection, sectionKindToken, 1) - return s + if pi.Kind() != "" { + identifiers = append(identifiers, pi.Kind()) } - for _, section := range identifiers { - if section == baseNameBaseof { + shouldIncludeSection := func(section string) bool { + switch section { + case containerShortcodes, containerPartials, containerMarkup: + return false + case "taxonomy", "": + return false + default: + for k, v := range s.opts.TaxonomySingularPlural { + if k == section || v == section { + return false + } + } + return true + } + } + if shouldIncludeSection(pi.Section()) { + identifiers = append(identifiers, pi.Section()) + } + + identifiers = helpers.UniqueStrings(identifiers) + + // Tokens on e.g. form /SECTIONKIND/THESECTION + insertSectionTokens := func(section string) []string { + kindOnly := isLayoutStandard(section) + var ss []string + s1 := base + if !kindOnly { + s1 = strings.ReplaceAll(s1, section, sectionToken) + } + s1 = strings.ReplaceAll(s1, kinds.KindSection, sectionKindToken) + if s1 != base { + ss = append(ss, s1) + } + s1 = strings.ReplaceAll(base, kinds.KindSection, sectionKindToken) + if !kindOnly { + s1 = strings.ReplaceAll(s1, section, sectionToken) + } + if s1 != base { + ss = append(ss, s1) + } + + helpers.UniqueStringsReuse(ss) + + return ss + } + + for _, id := range identifiers { + if id == "" { continue } - kindOnly := isLayoutStandard(section) - p := insertSectionTokens(section, kindOnly) - if m1, ok := s.opts.legacyMappingSection[p]; ok { - m1.mapping.targetPath = strings.Replace(m1.mapping.targetPath, sectionToken, section, 1) - handleMapping(m1) + + p := insertSectionTokens(id) + for _, ss := range p { + if m1, ok := s.opts.legacyMappingSection[ss]; ok { + targetPath := m1.mapping.targetPath + + if targetPath != "" { + targetPath = strings.ReplaceAll(targetPath, sectionToken, id) + targetPath = strings.ReplaceAll(targetPath, sectionKindToken, id) + targetPath = strings.ReplaceAll(targetPath, "//", "/") + } + m1.mapping.targetPath = targetPath + handleMapping(m1) + } } } diff --git a/tpl/tplimpl/templatestore_integration_test.go b/tpl/tplimpl/templatestore_integration_test.go index 4f76626ad..74abe58d0 100644 --- a/tpl/tplimpl/templatestore_integration_test.go +++ b/tpl/tplimpl/templatestore_integration_test.go @@ -441,7 +441,7 @@ title: "P1" {{ define "main" }}FOO{{ end }} -- layouts/_default/single.json -- -- layouts/_default/single.html -- -{{ define "main" }}MAIN{{ end }} +{{ define "main" }}MAIN /_default/single.html{{ end }} -- layouts/post/single.html -- {{ define "main" }}MAIN{{ end }} -- layouts/_partials/usedpartial.html -- @@ -461,6 +461,8 @@ title: "P1" ) b.Build() + b.AssertFileContent("public/p1/index.html", "MAIN /_default/single.html") + unused := b.H.GetTemplateStore().UnusedTemplates() var names []string for _, tmpl := range unused { @@ -468,8 +470,9 @@ title: "P1" names = append(names, fi.Meta().PathInfo.PathNoLeadingSlash()) } } - b.Assert(len(unused), qt.Equals, 5, qt.Commentf("%#v", names)) + b.Assert(names, qt.DeepEquals, []string{"_partials/unusedpartial.html", "shortcodes/unusedshortcode.html", "baseof.json", "post/single.html", "_default/single.json"}) + b.Assert(len(unused), qt.Equals, 5, qt.Commentf("%#v", names)) } func TestCreateManyTemplateStores(t *testing.T) {