diff --git a/hugolib/page_output.go b/hugolib/page_output.go index 993dcb7a2..9844b150a 100644 --- a/hugolib/page_output.go +++ b/hugolib/page_output.go @@ -100,22 +100,19 @@ func (p *PageOutput) layouts(layouts ...string) ([]string, error) { return []string{p.selfLayout}, nil } - layoutOverride := "" + layoutDescriptor := p.layoutDescriptor + if len(layouts) > 0 { - layoutOverride = layouts[0] + layoutDescriptor.Layout = layouts[0] + layoutDescriptor.LayoutOverride = true } return p.s.layoutHandler.For( - p.layoutDescriptor, - layoutOverride, + layoutDescriptor, p.outputFormat) } func (p *PageOutput) Render(layout ...string) template.HTML { - if !p.checkRender() { - return "" - } - l, err := p.layouts(layout...) if err != nil { helpers.DistinctErrorLog.Printf("in .Render: Failed to resolve layout %q for page %q", layout, p.pathOrTitle()) @@ -145,10 +142,6 @@ func (p *PageOutput) Render(layout ...string) template.HTML { } func (p *Page) Render(layout ...string) template.HTML { - if !p.checkRender() { - return "" - } - p.pageOutputInit.Do(func() { if p.mainPageOutput != nil { return @@ -170,16 +163,6 @@ func (p *Page) Render(layout ...string) template.HTML { return p.mainPageOutput.Render(layout...) } -// We may fix this in the future, but the layout handling in Render isn't built -// for list pages. -func (p *Page) checkRender() bool { - if p.Kind != KindPage { - helpers.DistinctWarnLog.Printf(".Render only available for regular pages, not for of kind %q. You probably meant .Site.RegularPages and not.Site.Pages.", p.Kind) - return false - } - return true -} - // OutputFormats holds a list of the relevant output formats for a given resource. type OutputFormats []*OutputFormat diff --git a/hugolib/site.go b/hugolib/site.go index 771ee5914..4ac76f0b5 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -1541,7 +1541,7 @@ func (s *Site) kindFromSections(sections []string) string { } func (s *Site) layouts(p *PageOutput) ([]string, error) { - return s.layoutHandler.For(p.layoutDescriptor, "", p.outputFormat) + return s.layoutHandler.For(p.layoutDescriptor, p.outputFormat) } func (s *Site) preparePages() error { diff --git a/hugolib/site_render.go b/hugolib/site_render.go index 497433ff6..8f46e33ba 100644 --- a/hugolib/site_render.go +++ b/hugolib/site_render.go @@ -242,7 +242,6 @@ func (s *Site) renderRSS(p *PageOutput) error { layouts, err := s.layoutHandler.For( p.layoutDescriptor, - "", p.outputFormat) if err != nil { return err diff --git a/output/docshelper.go b/output/docshelper.go index 9ed94b09b..5ccf0f57d 100644 --- a/output/docshelper.go +++ b/output/docshelper.go @@ -37,27 +37,26 @@ func createLayoutExamples() interface{} { ) for _, example := range []struct { - name string - d LayoutDescriptor - hasTheme bool - layoutOverride string - f Format + name string + d LayoutDescriptor + hasTheme bool + f Format }{ - {`AMP home, with theme "demoTheme".`, LayoutDescriptor{Kind: "home"}, true, "", AMPFormat}, - {`AMP home, French language".`, LayoutDescriptor{Kind: "home", Lang: "fr"}, false, "", AMPFormat}, - {"RSS home, no theme.", LayoutDescriptor{Kind: "home"}, false, "", RSSFormat}, - {"JSON home, no theme.", LayoutDescriptor{Kind: "home"}, false, "", JSONFormat}, - {fmt.Sprintf(`CSV regular, "layout: %s" in front matter.`, demoLayout), LayoutDescriptor{Kind: "page", Layout: demoLayout}, false, "", CSVFormat}, - {fmt.Sprintf(`JSON regular, "type: %s" in front matter.`, demoType), LayoutDescriptor{Kind: "page", Type: demoType}, false, "", JSONFormat}, - {"HTML regular.", LayoutDescriptor{Kind: "page"}, false, "", HTMLFormat}, - {"AMP regular.", LayoutDescriptor{Kind: "page"}, false, "", AMPFormat}, - {"Calendar blog section.", LayoutDescriptor{Kind: "section", Section: "blog"}, false, "", CalendarFormat}, - {"Calendar taxonomy list.", LayoutDescriptor{Kind: "taxonomy", Section: "tag"}, false, "", CalendarFormat}, - {"Calendar taxonomy term.", LayoutDescriptor{Kind: "taxonomyTerm", Section: "tag"}, false, "", CalendarFormat}, + {`AMP home, with theme "demoTheme".`, LayoutDescriptor{Kind: "home"}, true, AMPFormat}, + {`AMP home, French language".`, LayoutDescriptor{Kind: "home", Lang: "fr"}, false, AMPFormat}, + {"RSS home, no theme.", LayoutDescriptor{Kind: "home"}, false, RSSFormat}, + {"JSON home, no theme.", LayoutDescriptor{Kind: "home"}, false, JSONFormat}, + {fmt.Sprintf(`CSV regular, "layout: %s" in front matter.`, demoLayout), LayoutDescriptor{Kind: "page", Layout: demoLayout}, false, CSVFormat}, + {fmt.Sprintf(`JSON regular, "type: %s" in front matter.`, demoType), LayoutDescriptor{Kind: "page", Type: demoType}, false, JSONFormat}, + {"HTML regular.", LayoutDescriptor{Kind: "page"}, false, HTMLFormat}, + {"AMP regular.", LayoutDescriptor{Kind: "page"}, false, AMPFormat}, + {"Calendar blog section.", LayoutDescriptor{Kind: "section", Section: "blog"}, false, CalendarFormat}, + {"Calendar taxonomy list.", LayoutDescriptor{Kind: "taxonomy", Section: "tag"}, false, CalendarFormat}, + {"Calendar taxonomy term.", LayoutDescriptor{Kind: "taxonomyTerm", Section: "tag"}, false, CalendarFormat}, } { l := NewLayoutHandler(example.hasTheme) - layouts, _ := l.For(example.d, example.layoutOverride, example.f) + layouts, _ := l.For(example.d, example.f) basicExamples = append(basicExamples, Example{ Example: example.name, diff --git a/output/layout.go b/output/layout.go index ee3305758..5ffc19130 100644 --- a/output/layout.go +++ b/output/layout.go @@ -15,11 +15,16 @@ package output import ( "fmt" - "path" "strings" "sync" ) +// These may be used as content sections with potential conflicts. Avoid that. +var reservedSections = map[string]bool{ + "shortcodes": true, + "partials": true, +} + // LayoutDescriptor describes how a layout should be chosen. This is // typically built from a Page. type LayoutDescriptor struct { @@ -28,6 +33,8 @@ type LayoutDescriptor struct { Kind string Lang string Layout string + // LayoutOverride indicates what we should only look for the above layout. + LayoutOverride bool } // LayoutHandler calculates the layout template to use to render a given output type. @@ -39,9 +46,8 @@ type LayoutHandler struct { } type layoutCacheKey struct { - d LayoutDescriptor - layoutOverride string - f Format + d LayoutDescriptor + f Format } // NewLayoutHandler creates a new LayoutHandler. @@ -49,50 +55,12 @@ func NewLayoutHandler(hasTheme bool) *LayoutHandler { return &LayoutHandler{hasTheme: hasTheme, cache: make(map[layoutCacheKey][]string)} } -// RSS: -// Home:"rss.xml", "_default/rss.xml", "_internal/_default/rss.xml" -// Section: "section/" + section + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml" -// Taxonomy "taxonomy/" + singular + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml" -// Tax term: taxonomy/" + singular + ".terms.rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml" - -const ( - - // TODO(bep) variations reduce to 1 "." - - // The RSS templates doesn't map easily into the regular pages. - layoutsRSSHome = `VARIATIONS _default/VARIATIONS _internal/_default/rss.xml` - layoutsRSSSection = `section/SECTION.VARIATIONS _default/VARIATIONS VARIATIONS _internal/_default/rss.xml` - layoutsRSSTaxonomy = `taxonomy/SECTION.VARIATIONS _default/VARIATIONS VARIATIONS _internal/_default/rss.xml` - layoutsRSSTaxonomyTerm = `taxonomy/SECTION.terms.VARIATIONS _default/VARIATIONS VARIATIONS _internal/_default/rss.xml` - - layoutsHome = "index.VARIATIONS _default/list.VARIATIONS" - layoutsSection = ` -section/SECTION.VARIATIONS -SECTION/list.VARIATIONS -_default/section.VARIATIONS -_default/list.VARIATIONS -indexes/SECTION.VARIATIONS -_default/indexes.VARIATIONS -` - layoutsTaxonomy = ` -taxonomy/SECTION.VARIATIONS -indexes/SECTION.VARIATIONS -_default/taxonomy.VARIATIONS -_default/list.VARIATIONS -` - layoutsTaxonomyTerm = ` -taxonomy/SECTION.terms.VARIATIONS -_default/terms.VARIATIONS -indexes/indexes.VARIATIONS -` -) - // For returns a layout for the given LayoutDescriptor and options. // Layouts are rendered and cached internally. -func (l *LayoutHandler) For(d LayoutDescriptor, layoutOverride string, f Format) ([]string, error) { +func (l *LayoutHandler) For(d LayoutDescriptor, f Format) ([]string, error) { // We will get lots of requests for the same layouts, so avoid recalculations. - key := layoutCacheKey{d, layoutOverride, f} + key := layoutCacheKey{d, f} l.mu.RLock() if cacheVal, found := l.cache[key]; found { l.mu.RUnlock() @@ -100,53 +68,18 @@ func (l *LayoutHandler) For(d LayoutDescriptor, layoutOverride string, f Format) } l.mu.RUnlock() - var layouts []string - - if layoutOverride != "" && d.Kind != "page" { - return layouts, fmt.Errorf("Custom layout (%q) only supported for regular pages, not kind %q", layoutOverride, d.Kind) - } - - layout := d.Layout - - if layoutOverride != "" { - layout = layoutOverride - } - - isRSS := f.Name == RSSFormat.Name - - if d.Kind == "page" { - if isRSS { - return []string{}, nil - } - layouts = regularPageLayouts(d.Type, layout, f) - } else { - if isRSS { - layouts = resolveListTemplate(d, f, - layoutsRSSHome, - layoutsRSSSection, - layoutsRSSTaxonomy, - layoutsRSSTaxonomyTerm) - } else { - layouts = resolveListTemplate(d, f, - layoutsHome, - layoutsSection, - layoutsTaxonomy, - layoutsTaxonomyTerm) - } - } + layouts := resolvePageTemplate(d, f) if l.hasTheme { + // From Hugo 0.33 we interleave the project/theme templates. This was kind of a fundamental change, but the + // previous behaviour was surprising. + // As an example, an `index.html` in theme for the home page will now win over a `_default/list.html` in the project. layoutsWithThemeLayouts := []string{} + // First place all non internal templates for _, t := range layouts { if !strings.HasPrefix(t, "_internal/") { layoutsWithThemeLayouts = append(layoutsWithThemeLayouts, t) - } - } - - // Then place theme templates with the same names - for _, t := range layouts { - if !strings.HasPrefix(t, "_internal/") { layoutsWithThemeLayouts = append(layoutsWithThemeLayouts, "theme/"+t) } } @@ -170,70 +103,167 @@ func (l *LayoutHandler) For(d LayoutDescriptor, layoutOverride string, f Format) return layouts, nil } -func resolveListTemplate(d LayoutDescriptor, f Format, - homeLayouts, - sectionLayouts, - taxonomyLayouts, - taxonomyTermLayouts string) []string { - var layouts []string - - switch d.Kind { - case "home": - layouts = resolveTemplate(homeLayouts, d, f) - case "section": - layouts = resolveTemplate(sectionLayouts, d, f) - case "taxonomy": - layouts = resolveTemplate(taxonomyLayouts, d, f) - case "taxonomyTerm": - layouts = resolveTemplate(taxonomyTermLayouts, d, f) - } - return layouts +type layoutBuilder struct { + layoutVariations []string + typeVariations []string + d LayoutDescriptor + f Format } -func resolveTemplate(templ string, d LayoutDescriptor, f Format) []string { +func (l *layoutBuilder) addLayoutVariations(vars ...string) { + for _, layoutVar := range vars { + if l.d.LayoutOverride && layoutVar != l.d.Layout { + continue + } + l.layoutVariations = append(l.layoutVariations, layoutVar) + } +} - // VARIATIONS will be replaced with - // .lang.name.suffix - // .name.suffix - // .lang.suffix - // .suffix - var replacementValues []string +func (l *layoutBuilder) addTypeVariations(vars ...string) { + for _, typeVar := range vars { + if !reservedSections[typeVar] { + l.typeVariations = append(l.typeVariations, typeVar) + } + } +} - name := strings.ToLower(f.Name) +func (l *layoutBuilder) addSectionType() { + if l.d.Section != "" { + l.addTypeVariations(l.d.Section) + } +} - if d.Lang != "" { - replacementValues = append(replacementValues, fmt.Sprintf("%s.%s.%s", d.Lang, name, f.MediaType.Suffix)) +func (l *layoutBuilder) addKind() { + l.addLayoutVariations(l.d.Kind) + l.addTypeVariations(l.d.Kind) +} + +func resolvePageTemplate(d LayoutDescriptor, f Format) []string { + + b := &layoutBuilder{d: d, f: f} + + if d.Layout != "" { + b.addLayoutVariations(d.Layout) } - replacementValues = append(replacementValues, fmt.Sprintf("%s.%s", name, f.MediaType.Suffix)) + if d.Type != "" { + b.addTypeVariations(d.Type) + } + + switch d.Kind { + case "page": + b.addLayoutVariations("single") + b.addSectionType() + case "home": + b.addLayoutVariations("index", "home") + // Also look in the root + b.addTypeVariations("") + case "section": + if d.Section != "" { + b.addLayoutVariations(d.Section) + } + b.addSectionType() + b.addKind() + case "taxonomy": + if d.Section != "" { + b.addLayoutVariations(d.Section) + } + b.addKind() + b.addSectionType() + + case "taxonomyTerm": + if d.Section != "" { + b.addLayoutVariations(d.Section + ".terms") + } + b.addTypeVariations("taxonomy") + b.addSectionType() + b.addLayoutVariations("terms") - if d.Lang != "" { - replacementValues = append(replacementValues, fmt.Sprintf("%s.%s", d.Lang, f.MediaType.Suffix)) } isRSS := f.Name == RSSFormat.Name - - if !isRSS { - replacementValues = append(replacementValues, f.MediaType.Suffix) + if isRSS { + // The historic and common rss.xml case + b.addLayoutVariations("") } + // All have _default in their lookup path + b.addTypeVariations("_default") + + if d.Kind != "page" { + // Add the common list type + b.addLayoutVariations("list") + } + + layouts := b.resolveVariations() + + if isRSS { + layouts = append(layouts, "_internal/_default/rss.xml") + } + + return layouts + +} + +func (l *layoutBuilder) resolveVariations() []string { + var layouts []string - templFields := strings.Fields(templ) + var variations []string + name := strings.ToLower(l.f.Name) - for _, field := range templFields { - for _, replacements := range replacementValues { - layouts = append(layouts, replaceKeyValues(field, "VARIATIONS", replacements, "SECTION", d.Section)) + if l.d.Lang != "" { + // We prefer the most specific type before language. + variations = append(variations, []string{fmt.Sprintf("%s.%s", l.d.Lang, name), name, l.d.Lang}...) + } else { + variations = append(variations, name) + } + + variations = append(variations, "") + + for _, typeVar := range l.typeVariations { + for _, variation := range variations { + for _, layoutVar := range l.layoutVariations { + if variation == "" && layoutVar == "" { + continue + } + template := layoutTemplate(typeVar, layoutVar) + layouts = append(layouts, replaceKeyValues(template, + "TYPE", typeVar, + "LAYOUT", layoutVar, + "VARIATIONS", variation, + "EXTENSION", l.f.MediaType.Suffix, + )) + } } + } return filterDotLess(layouts) } +func layoutTemplate(typeVar, layoutVar string) string { + + var l string + + if typeVar != "" { + l = "TYPE/" + } + + if layoutVar != "" { + l += "LAYOUT.VARIATIONS.EXTENSION" + } else { + l += "VARIATIONS.EXTENSION" + } + + return l +} + func filterDotLess(layouts []string) []string { var filteredLayouts []string for _, l := range layouts { + l = strings.Replace(l, "..", ".", -1) l = strings.Trim(l, ".") // If media type has no suffix, we have "index" type of layouts in this list, which // doesn't make much sense. @@ -263,37 +293,3 @@ func replaceKeyValues(s string, oldNew ...string) string { replacer := strings.NewReplacer(oldNew...) return replacer.Replace(s) } - -func regularPageLayouts(types string, layout string, f Format) []string { - var layouts []string - - if layout == "" { - layout = "single" - } - - delimiter := "." - if f.MediaType.Delimiter == "" { - delimiter = "" - } - - suffix := delimiter + f.MediaType.Suffix - name := strings.ToLower(f.Name) - - if types != "" { - t := strings.Split(types, "/") - - // Add type/layout.html - for i := range t { - search := t[:len(t)-i] - layouts = append(layouts, fmt.Sprintf("%s/%s.%s%s", strings.ToLower(path.Join(search...)), layout, name, suffix)) - layouts = append(layouts, fmt.Sprintf("%s/%s%s", strings.ToLower(path.Join(search...)), layout, suffix)) - - } - } - - // Add _default/layout.html - layouts = append(layouts, fmt.Sprintf("_default/%s.%s%s", layout, name, suffix)) - layouts = append(layouts, fmt.Sprintf("_default/%s%s", layout, suffix)) - - return filterDotLess(layouts) -} diff --git a/output/layout_test.go b/output/layout_test.go index 6fb958c9d..3c7fde41a 100644 --- a/output/layout_test.go +++ b/output/layout_test.go @@ -14,6 +14,9 @@ package output import ( + "fmt" + "reflect" + "strings" "testing" "github.com/gohugoio/hugo/media" @@ -37,6 +40,8 @@ func TestLayout(t *testing.T) { BaseName: "index", } + htmlFormat = HTMLFormat + noExtDelimFormat = Format{ Name: "NEM", MediaType: noExtNoDelimMediaType, @@ -56,57 +61,74 @@ func TestLayout(t *testing.T) { layoutOverride string tp Format expect []string + expectCount int }{ {"Home", LayoutDescriptor{Kind: "home"}, true, "", ampType, - []string{"index.amp.html", "index.html", "_default/list.amp.html", "_default/list.html", "theme/index.amp.html", "theme/index.html"}}, + []string{"index.amp.html", "theme/index.amp.html", "home.amp.html", "theme/home.amp.html", "list.amp.html", "theme/list.amp.html", "index.html", "theme/index.html", "home.html", "theme/home.html", "list.html", "theme/list.html", "_default/index.amp.html"}, 24}, + {"Home, HTML", LayoutDescriptor{Kind: "home"}, true, "", htmlFormat, + // We will eventually get to index.html. This looks stuttery, but makes the lookup logic easy to understand. + []string{"index.html.html", "theme/index.html.html", "home.html.html"}, 24}, {"Home, french language", LayoutDescriptor{Kind: "home", Lang: "fr"}, true, "", ampType, - []string{"index.fr.amp.html", "index.amp.html", "index.fr.html", "index.html", "_default/list.fr.amp.html", "_default/list.amp.html", "_default/list.fr.html", "_default/list.html", "theme/index.fr.amp.html", "theme/index.amp.html", "theme/index.fr.html"}}, + []string{"index.fr.amp.html", "theme/index.fr.amp.html"}, + 48}, {"Home, no ext or delim", LayoutDescriptor{Kind: "home"}, true, "", noExtDelimFormat, - []string{"index.nem", "_default/list.nem"}}, + []string{"index.nem", "theme/index.nem", "home.nem", "theme/home.nem", "list.nem"}, 12}, {"Home, no ext", LayoutDescriptor{Kind: "home"}, true, "", noExt, - []string{"index.nex", "_default/list.nex"}}, + []string{"index.nex", "theme/index.nex", "home.nex", "theme/home.nex", "list.nex"}, 12}, {"Page, no ext or delim", LayoutDescriptor{Kind: "page"}, true, "", noExtDelimFormat, - []string{"_default/single.nem", "theme/_default/single.nem"}}, + []string{"_default/single.nem", "theme/_default/single.nem"}, 2}, {"Section", LayoutDescriptor{Kind: "section", Section: "sect1"}, false, "", ampType, - []string{"section/sect1.amp.html", "section/sect1.html"}}, + []string{"sect1/sect1.amp.html", "sect1/section.amp.html", "sect1/list.amp.html", "sect1/sect1.html", "sect1/section.html", "sect1/list.html", "section/sect1.amp.html", "section/section.amp.html"}, 18}, + {"Section with layout", LayoutDescriptor{Kind: "section", Section: "sect1", Layout: "mylayout"}, false, "", ampType, + []string{"sect1/mylayout.amp.html", "sect1/sect1.amp.html", "sect1/section.amp.html", "sect1/list.amp.html", "sect1/mylayout.html", "sect1/sect1.html"}, 24}, {"Taxonomy", LayoutDescriptor{Kind: "taxonomy", Section: "tag"}, false, "", ampType, - []string{"taxonomy/tag.amp.html", "taxonomy/tag.html"}}, + []string{"taxonomy/tag.amp.html", "taxonomy/taxonomy.amp.html", "taxonomy/list.amp.html", "taxonomy/tag.html", "taxonomy/taxonomy.html"}, 18}, {"Taxonomy term", LayoutDescriptor{Kind: "taxonomyTerm", Section: "categories"}, false, "", ampType, - []string{"taxonomy/categories.terms.amp.html", "taxonomy/categories.terms.html", "_default/terms.amp.html"}}, + []string{"taxonomy/categories.terms.amp.html", "taxonomy/terms.amp.html", "taxonomy/list.amp.html", "taxonomy/categories.terms.html", "taxonomy/terms.html"}, 18}, {"Page", LayoutDescriptor{Kind: "page"}, true, "", ampType, - []string{"_default/single.amp.html", "_default/single.html", "theme/_default/single.amp.html"}}, + []string{"_default/single.amp.html", "theme/_default/single.amp.html", "_default/single.html", "theme/_default/single.html"}, 4}, {"Page with layout", LayoutDescriptor{Kind: "page", Layout: "mylayout"}, false, "", ampType, - []string{"_default/mylayout.amp.html", "_default/mylayout.html"}}, + []string{"_default/mylayout.amp.html", "_default/single.amp.html", "_default/mylayout.html", "_default/single.html"}, 4}, {"Page with layout and type", LayoutDescriptor{Kind: "page", Layout: "mylayout", Type: "myttype"}, false, "", ampType, - []string{"myttype/mylayout.amp.html", "myttype/mylayout.html", "_default/mylayout.amp.html"}}, + []string{"myttype/mylayout.amp.html", "myttype/single.amp.html", "myttype/mylayout.html"}, 8}, {"Page with layout and type with subtype", LayoutDescriptor{Kind: "page", Layout: "mylayout", Type: "myttype/mysubtype"}, false, "", ampType, - []string{"myttype/mysubtype/mylayout.amp.html", "myttype/mysubtype/mylayout.html", "myttype/mylayout.amp.html"}}, - {"Page with overridden layout", LayoutDescriptor{Kind: "page", Layout: "mylayout", Type: "myttype"}, false, "myotherlayout", ampType, - []string{"myttype/myotherlayout.amp.html", "myttype/myotherlayout.html"}}, + []string{"myttype/mysubtype/mylayout.amp.html", "myttype/mysubtype/single.amp.html", "myttype/mysubtype/mylayout.html"}, 8}, // RSS {"RSS Home with theme", LayoutDescriptor{Kind: "home"}, true, "", RSSFormat, - []string{"rss.xml", "_default/rss.xml", "theme/rss.xml", "theme/_default/rss.xml", "_internal/_default/rss.xml"}}, + []string{"index.rss.xml", "theme/index.rss.xml", "home.rss.xml", "theme/home.rss.xml", "rss.xml"}, 29}, {"RSS Section", LayoutDescriptor{Kind: "section", Section: "sect1"}, false, "", RSSFormat, - []string{"section/sect1.rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}}, + []string{"sect1/sect1.rss.xml", "sect1/section.rss.xml", "sect1/rss.xml", "sect1/list.rss.xml", "sect1/sect1.xml", "sect1/section.xml"}, 22}, {"RSS Taxonomy", LayoutDescriptor{Kind: "taxonomy", Section: "tag"}, false, "", RSSFormat, - []string{"taxonomy/tag.rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}}, + []string{"taxonomy/tag.rss.xml", "taxonomy/taxonomy.rss.xml", "taxonomy/rss.xml", "taxonomy/list.rss.xml", "taxonomy/tag.xml", "taxonomy/taxonomy.xml"}, 22}, {"RSS Taxonomy term", LayoutDescriptor{Kind: "taxonomyTerm", Section: "tag"}, false, "", RSSFormat, - []string{"taxonomy/tag.terms.rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}}, + []string{"taxonomy/tag.terms.rss.xml", "taxonomy/terms.rss.xml", "taxonomy/rss.xml", "taxonomy/list.rss.xml", "taxonomy/tag.terms.xml"}, 22}, {"Home plain text", LayoutDescriptor{Kind: "home"}, true, "", JSONFormat, - []string{"_text/index.json.json", "_text/index.json", "_text/_default/list.json.json", "_text/_default/list.json", "_text/theme/index.json.json", "_text/theme/index.json"}}, + []string{"_text/index.json.json", "_text/theme/index.json.json", "_text/home.json.json", "_text/theme/home.json.json"}, 24}, {"Page plain text", LayoutDescriptor{Kind: "page"}, true, "", JSONFormat, - []string{"_text/_default/single.json.json", "_text/_default/single.json", "_text/theme/_default/single.json.json"}}, + []string{"_text/_default/single.json.json", "_text/theme/_default/single.json.json", "_text/_default/single.json", "_text/theme/_default/single.json"}, 4}, + {"Reserved section, shortcodes", LayoutDescriptor{Kind: "section", Section: "shortcodes", Type: "shortcodes"}, true, "", ampType, + []string{"section/shortcodes.amp.html", "theme/section/shortcodes.amp.html"}, 24}, + {"Reserved section, partials", LayoutDescriptor{Kind: "section", Section: "partials", Type: "partials"}, true, "", ampType, + []string{"section/partials.amp.html", "theme/section/partials.amp.html"}, 24}, } { t.Run(this.name, func(t *testing.T) { l := NewLayoutHandler(this.hasTheme) - layouts, err := l.For(this.d, this.layoutOverride, this.tp) + layouts, err := l.For(this.d, this.tp) require.NoError(t, err) require.NotNil(t, layouts) - require.True(t, len(layouts) >= len(this.expect)) + require.True(t, len(layouts) >= len(this.expect), fmt.Sprint(layouts)) // Not checking the complete list for now ... - require.Equal(t, this.expect, layouts[:len(this.expect)]) + got := layouts[:len(this.expect)] + if len(layouts) != this.expectCount || !reflect.DeepEqual(got, this.expect) { + formatted := strings.Replace(fmt.Sprintf("%v", layouts), "[", "\"", 1) + formatted = strings.Replace(formatted, "]", "\"", 1) + formatted = strings.Replace(formatted, " ", "\", \"", -1) + + t.Fatalf("Got %d/%d:\n%v\nExpected:\n%v\nAll:\n%v\nFormatted:\n%s", len(layouts), this.expectCount, got, this.expect, layouts, formatted) + + } if !this.hasTheme { for _, layout := range layouts { @@ -116,10 +138,6 @@ func TestLayout(t *testing.T) { }) } - l := NewLayoutHandler(false) - _, err := l.For(LayoutDescriptor{Kind: "taxonomyTerm", Section: "tag"}, "override", RSSFormat) - require.Error(t, err) - } func BenchmarkLayout(b *testing.B) { @@ -127,7 +145,7 @@ func BenchmarkLayout(b *testing.B) { l := NewLayoutHandler(false) for i := 0; i < b.N; i++ { - layouts, err := l.For(descriptor, "", HTMLFormat) + layouts, err := l.For(descriptor, HTMLFormat) require.NoError(b, err) require.NotEmpty(b, layouts) }