diff options
Diffstat (limited to 'output')
-rw-r--r-- | output/layout.go | 30 | ||||
-rw-r--r-- | output/layout_base.go | 182 | ||||
-rw-r--r-- | output/layout_base_test.go | 163 | ||||
-rw-r--r-- | output/layout_test.go | 21 |
4 files changed, 29 insertions, 367 deletions
diff --git a/output/layout.go b/output/layout.go index 091684bee..0421e6f3d 100644 --- a/output/layout.go +++ b/output/layout.go @@ -39,6 +39,7 @@ type LayoutDescriptor struct { LayoutOverride bool RenderingHook bool + Baseof bool } func (d LayoutDescriptor) isList() bool { @@ -76,7 +77,6 @@ func (l *LayoutHandler) For(d LayoutDescriptor, f Format) ([]string, error) { layouts := resolvePageTemplate(d, f) - layouts = prependTextPrefixIfNeeded(f, layouts...) layouts = helpers.UniqueStringsReuse(layouts) l.mu.Lock() @@ -95,7 +95,11 @@ type layoutBuilder struct { func (l *layoutBuilder) addLayoutVariations(vars ...string) { for _, layoutVar := range vars { - if !l.d.RenderingHook && l.d.LayoutOverride && layoutVar != l.d.Layout { + if l.d.Baseof && layoutVar != "baseof" { + l.layoutVariations = append(l.layoutVariations, layoutVar+"-baseof") + continue + } + if !l.d.RenderingHook && !l.d.Baseof && l.d.LayoutOverride && layoutVar != l.d.Layout { continue } l.layoutVariations = append(l.layoutVariations, layoutVar) @@ -173,7 +177,7 @@ func resolvePageTemplate(d LayoutDescriptor, f Format) []string { } isRSS := f.Name == RSSFormat.Name - if !d.RenderingHook && isRSS { + if !d.RenderingHook && !d.Baseof && isRSS { // The historic and common rss.xml case b.addLayoutVariations("") } @@ -186,9 +190,13 @@ func resolvePageTemplate(d LayoutDescriptor, f Format) []string { b.addLayoutVariations("list") } + if d.Baseof { + b.addLayoutVariations("baseof") + } + layouts := b.resolveVariations() - if !d.RenderingHook && isRSS { + if !d.RenderingHook && !d.Baseof && isRSS { layouts = append(layouts, "_internal/_default/rss.xml") } @@ -266,20 +274,6 @@ func filterDotLess(layouts []string) []string { return filteredLayouts } -func prependTextPrefixIfNeeded(f Format, layouts ...string) []string { - if !f.IsPlainText { - return layouts - } - - newLayouts := make([]string, len(layouts)) - - for i, l := range layouts { - newLayouts[i] = "_text/" + l - } - - return newLayouts -} - func replaceKeyValues(s string, oldNew ...string) string { replacer := strings.NewReplacer(oldNew...) return replacer.Replace(s) diff --git a/output/layout_base.go b/output/layout_base.go deleted file mode 100644 index 772002e68..000000000 --- a/output/layout_base.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2017-present 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 output - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/gohugoio/hugo/helpers" -) - -const ( - baseFileBase = "baseof" -) - -var ( - goTemplateInnerMarkers = [][]byte{[]byte("{{define"), []byte("{{ define"), []byte("{{- define"), []byte("{{-define")} -) - -// TemplateNames represents a template naming scheme. -type TemplateNames struct { - // The name used as key in the template map. Note that this will be - // prefixed with "_text/" if it should be parsed with text/template. - Name string - - OverlayFilename string - MasterFilename string -} - -// TemplateLookupDescriptor describes the template lookup configuration. -type TemplateLookupDescriptor struct { - // The full path to the site root. - WorkingDir string - - // The path to the template relative the the base. - // I.e. shortcodes/youtube.html - RelPath string - - // The template name prefix to look for. - Prefix string - - // All the output formats in play. This is used to decide if text/template or - // html/template. - OutputFormats Formats - - FileExists func(filename string) (bool, error) - ContainsAny func(filename string, subslices [][]byte) (bool, error) -} - -func isShorthCodeOrPartial(name string) bool { - return strings.HasPrefix(name, "shortcodes/") || strings.HasPrefix(name, "partials/") -} - -// CreateTemplateNames returns a TemplateNames object for a given template. -func CreateTemplateNames(d TemplateLookupDescriptor) (TemplateNames, error) { - - name := filepath.ToSlash(d.RelPath) - name = strings.TrimPrefix(name, "/") - - if d.Prefix != "" { - name = strings.Trim(d.Prefix, "/") + "/" + name - } - - var ( - id TemplateNames - ) - - // The filename will have a suffix with an optional type indicator. - // Examples: - // index.html - // index.amp.html - // index.json - filename := filepath.Base(d.RelPath) - isPlainText := false - outputFormat, found := d.OutputFormats.FromFilename(filename) - - if found && outputFormat.IsPlainText { - isPlainText = true - } - - var ext, outFormat string - - parts := strings.Split(filename, ".") - if len(parts) > 2 { - outFormat = parts[1] - ext = parts[2] - } else if len(parts) > 1 { - ext = parts[1] - } - - filenameNoSuffix := parts[0] - - id.OverlayFilename = d.RelPath - id.Name = name - - if isPlainText { - id.Name = "_text/" + id.Name - } - - // Go templates may have both a base and inner template. - if isShorthCodeOrPartial(name) { - // No base template support - return id, nil - } - - pathDir := filepath.Dir(d.RelPath) - - innerMarkers := goTemplateInnerMarkers - - var baseFilename string - - if outFormat != "" { - baseFilename = fmt.Sprintf("%s.%s.%s", baseFileBase, outFormat, ext) - } else { - baseFilename = fmt.Sprintf("%s.%s", baseFileBase, ext) - } - - // This may be a view that shouldn't have base template - // Have to look inside it to make sure - needsBase, err := d.ContainsAny(d.RelPath, innerMarkers) - if err != nil { - return id, err - } - - if needsBase { - currBaseFilename := fmt.Sprintf("%s-%s", filenameNoSuffix, baseFilename) - - // Look for base template in the follwing order: - // 1. <current-path>/<template-name>-baseof.<outputFormat>(optional).<suffix>, e.g. list-baseof.<outputFormat>(optional).<suffix>. - // 2. <current-path>/baseof.<outputFormat>(optional).<suffix> - // 3. _default/<template-name>-baseof.<outputFormat>(optional).<suffix>, e.g. list-baseof.<outputFormat>(optional).<suffix>. - // 4. _default/baseof.<outputFormat>(optional).<suffix> - // - // The filesystem it looks in a a composite of the project and potential theme(s). - pathsToCheck := createPathsToCheck(pathDir, baseFilename, currBaseFilename) - - // We may have language code and/or "terms" in the template name. We want the most specific, - // but need to fall back to the baseof.html if needed. - // E.g. list-baseof.en.html and list-baseof.terms.en.html - // See #3893, #3856. - baseBaseFilename, currBaseBaseFilename := helpers.Filename(baseFilename), helpers.Filename(currBaseFilename) - p1, p2 := strings.Split(baseBaseFilename, "."), strings.Split(currBaseBaseFilename, ".") - if len(p1) > 0 && len(p1) == len(p2) { - for i := len(p1); i > 0; i-- { - v1, v2 := strings.Join(p1[:i], ".")+"."+ext, strings.Join(p2[:i], ".")+"."+ext - pathsToCheck = append(pathsToCheck, createPathsToCheck(pathDir, v1, v2)...) - - } - } - - for _, p := range pathsToCheck { - if ok, err := d.FileExists(p); err == nil && ok { - id.MasterFilename = p - break - } - } - } - - return id, nil - -} - -func createPathsToCheck(baseTemplatedDir, baseFilename, currBaseFilename string) []string { - return []string{ - filepath.Join(baseTemplatedDir, currBaseFilename), - filepath.Join(baseTemplatedDir, baseFilename), - filepath.Join("_default", currBaseFilename), - filepath.Join("_default", baseFilename), - } -} diff --git a/output/layout_base_test.go b/output/layout_base_test.go deleted file mode 100644 index 8eea9e61e..000000000 --- a/output/layout_base_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2017-present 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 output - -import ( - "path/filepath" - "strings" - "testing" - - qt "github.com/frankban/quicktest" -) - -func TestLayoutBase(t *testing.T) { - c := qt.New(t) - - var ( - workingDir = "/sites/mysite/" - layoutPath1 = "_default/single.html" - layoutPathAmp = "_default/single.amp.html" - layoutPathJSON = "_default/single.json" - ) - - for _, this := range []struct { - name string - d TemplateLookupDescriptor - needsBase bool - basePathMatchStrings string - expect TemplateNames - }{ - {"No base", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: layoutPath1}, false, "", - TemplateNames{ - Name: "_default/single.html", - OverlayFilename: "_default/single.html", - }}, - {"Base", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: layoutPath1}, true, "", - TemplateNames{ - Name: "_default/single.html", - OverlayFilename: "_default/single.html", - MasterFilename: "_default/single-baseof.html", - }}, - // Issue #3893 - {"Base Lang, Default Base", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: "_default/list.en.html"}, true, "_default/baseof.html", - TemplateNames{ - Name: "_default/list.en.html", - OverlayFilename: "_default/list.en.html", - MasterFilename: "_default/baseof.html", - }}, - {"Base Lang, Lang Base", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: "_default/list.en.html"}, true, "_default/baseof.html|_default/baseof.en.html", - TemplateNames{ - Name: "_default/list.en.html", - OverlayFilename: "_default/list.en.html", - MasterFilename: "_default/baseof.en.html", - }}, - // Issue #3856 - {"Base Taxonomy Term", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: "taxonomy/tag.terms.html"}, true, "_default/baseof.html", - TemplateNames{ - Name: "taxonomy/tag.terms.html", - OverlayFilename: "taxonomy/tag.terms.html", - MasterFilename: "_default/baseof.html", - }}, - - {"Partial", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: "partials/menu.html"}, true, - "mytheme/layouts/_default/baseof.html", - TemplateNames{ - Name: "partials/menu.html", - OverlayFilename: "partials/menu.html", - }}, - {"Partial in subfolder", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: "/partials/sub/menu.html"}, true, - "_default/baseof.html", - TemplateNames{ - Name: "partials/sub/menu.html", - OverlayFilename: "/partials/sub/menu.html", - }}, - {"Shortcode in subfolder", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: "shortcodes/sub/menu.html"}, true, - "_default/baseof.html", - TemplateNames{ - Name: "shortcodes/sub/menu.html", - OverlayFilename: "shortcodes/sub/menu.html", - }}, - {"AMP, no base", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: layoutPathAmp}, false, "", - TemplateNames{ - Name: "_default/single.amp.html", - OverlayFilename: "_default/single.amp.html", - }}, - {"JSON, no base", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: layoutPathJSON}, false, "", - TemplateNames{ - Name: "_default/single.json", - OverlayFilename: "_default/single.json", - }}, - {"AMP with base", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: layoutPathAmp}, true, "single-baseof.html|single-baseof.amp.html", - TemplateNames{ - Name: "_default/single.amp.html", - OverlayFilename: "_default/single.amp.html", - MasterFilename: "_default/single-baseof.amp.html", - }}, - {"AMP with no AMP base", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: layoutPathAmp}, true, "single-baseof.html", - TemplateNames{ - Name: "_default/single.amp.html", - OverlayFilename: "_default/single.amp.html", - MasterFilename: "_default/single-baseof.html", - }}, - - {"JSON with base", TemplateLookupDescriptor{WorkingDir: workingDir, RelPath: layoutPathJSON}, true, "single-baseof.json", - TemplateNames{ - Name: "_default/single.json", - OverlayFilename: "_default/single.json", - MasterFilename: "_default/single-baseof.json", - }}, - } { - c.Run(this.name, func(c *qt.C) { - - this.basePathMatchStrings = filepath.FromSlash(this.basePathMatchStrings) - - fileExists := func(filename string) (bool, error) { - stringsToMatch := strings.Split(this.basePathMatchStrings, "|") - for _, s := range stringsToMatch { - if strings.Contains(filename, s) { - return true, nil - } - - } - return false, nil - } - - needsBase := func(filename string, subslices [][]byte) (bool, error) { - return this.needsBase, nil - } - - this.d.OutputFormats = Formats{AMPFormat, HTMLFormat, RSSFormat, JSONFormat} - this.d.WorkingDir = filepath.FromSlash(this.d.WorkingDir) - this.d.RelPath = filepath.FromSlash(this.d.RelPath) - this.d.ContainsAny = needsBase - this.d.FileExists = fileExists - - this.expect.MasterFilename = filepath.FromSlash(this.expect.MasterFilename) - this.expect.OverlayFilename = filepath.FromSlash(this.expect.OverlayFilename) - - if strings.Contains(this.d.RelPath, "json") { - // currently the only plain text templates in this test. - this.expect.Name = "_text/" + this.expect.Name - } - - id, err := CreateTemplateNames(this.d) - - c.Assert(err, qt.IsNil) - msg := qt.Commentf(this.name) - c.Assert(id, qt.Equals, this.expect, msg) - - }) - } - -} diff --git a/output/layout_test.go b/output/layout_test.go index cff275929..7efa5675f 100644 --- a/output/layout_test.go +++ b/output/layout_test.go @@ -66,9 +66,13 @@ func TestLayout(t *testing.T) { }{ {"Home", LayoutDescriptor{Kind: "home"}, "", ampType, []string{"index.amp.html", "home.amp.html", "list.amp.html", "index.html", "home.html", "list.html", "_default/index.amp.html"}, 12}, + {"Home baseof", LayoutDescriptor{Kind: "home", Baseof: true}, "", ampType, + []string{"index-baseof.amp.html", "home-baseof.amp.html", "list-baseof.amp.html", "baseof.amp.html", "index-baseof.html"}, 16}, {"Home, HTML", LayoutDescriptor{Kind: "home"}, "", htmlFormat, // We will eventually get to index.html. This looks stuttery, but makes the lookup logic easy to understand. []string{"index.html.html", "home.html.html"}, 12}, + {"Home, HTML, baseof", LayoutDescriptor{Kind: "home", Baseof: true}, "", htmlFormat, + []string{"index-baseof.html.html", "home-baseof.html.html", "list-baseof.html.html", "baseof.html.html"}, 16}, {"Home, french language", LayoutDescriptor{Kind: "home", Lang: "fr"}, "", ampType, []string{"index.fr.amp.html"}, 24}, @@ -80,6 +84,8 @@ func TestLayout(t *testing.T) { []string{"_default/single.nem"}, 1}, {"Section", LayoutDescriptor{Kind: "section", Section: "sect1"}, "", ampType, []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, baseof", LayoutDescriptor{Kind: "section", Section: "sect1", Baseof: true}, "", ampType, + []string{"sect1/sect1-baseof.amp.html", "sect1/section-baseof.amp.html", "sect1/list-baseof.amp.html", "sect1/baseof.amp.html", "sect1/sect1-baseof.html", "sect1/section-baseof.html", "sect1/list-baseof.html", "sect1/baseof.html"}, 24}, {"Section with layout", LayoutDescriptor{Kind: "section", Section: "sect1", Layout: "mylayout"}, "", 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"}, "", ampType, @@ -88,8 +94,12 @@ func TestLayout(t *testing.T) { []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"}, "", ampType, []string{"_default/single.amp.html", "_default/single.html"}, 2}, + {"Page, baseof", LayoutDescriptor{Kind: "page", Baseof: true}, "", ampType, + []string{"_default/single-baseof.amp.html", "_default/baseof.amp.html", "_default/single-baseof.html", "_default/baseof.html"}, 4}, {"Page with layout", LayoutDescriptor{Kind: "page", Layout: "mylayout"}, "", ampType, []string{"_default/mylayout.amp.html", "_default/single.amp.html", "_default/mylayout.html", "_default/single.html"}, 4}, + {"Page with layout, baseof", LayoutDescriptor{Kind: "page", Layout: "mylayout", Baseof: true}, "", ampType, + []string{"_default/mylayout-baseof.amp.html", "_default/single-baseof.amp.html", "_default/baseof.amp.html", "_default/mylayout-baseof.html", "_default/single-baseof.html", "_default/baseof.html"}, 6}, {"Page with layout and type", LayoutDescriptor{Kind: "page", Layout: "mylayout", Type: "myttype"}, "", ampType, []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"}, "", ampType, @@ -97,6 +107,8 @@ func TestLayout(t *testing.T) { // RSS {"RSS Home", LayoutDescriptor{Kind: "home"}, "", RSSFormat, []string{"index.rss.xml", "home.rss.xml", "rss.xml"}, 15}, + {"RSS Home, baseof", LayoutDescriptor{Kind: "home", Baseof: true}, "", RSSFormat, + []string{"index-baseof.rss.xml", "home-baseof.rss.xml", "list-baseof.rss.xml", "baseof.rss.xml"}, 16}, {"RSS Section", LayoutDescriptor{Kind: "section", Section: "sect1"}, "", RSSFormat, []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"}, "", RSSFormat, @@ -104,13 +116,14 @@ func TestLayout(t *testing.T) { {"RSS Taxonomy term", LayoutDescriptor{Kind: "taxonomyTerm", Section: "tag"}, "", RSSFormat, []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"}, "", JSONFormat, - []string{"_text/index.json.json", "_text/home.json.json"}, 12}, + []string{"index.json.json", "home.json.json"}, 12}, {"Page plain text", LayoutDescriptor{Kind: "page"}, "", JSONFormat, - []string{"_text/_default/single.json.json", "_text/_default/single.json"}, 2}, + []string{"_default/single.json.json", "_default/single.json"}, 2}, {"Reserved section, shortcodes", LayoutDescriptor{Kind: "section", Section: "shortcodes", Type: "shortcodes"}, "", ampType, []string{"section/shortcodes.amp.html"}, 12}, {"Reserved section, partials", LayoutDescriptor{Kind: "section", Section: "partials", Type: "partials"}, "", ampType, []string{"section/partials.amp.html"}, 12}, + // We may add type support ... later. {"Content hook", LayoutDescriptor{Kind: "render-link", RenderingHook: true, Layout: "mylayout", Section: "blog"}, "", ampType, []string{"_default/_markup/render-link.amp.html", "_default/_markup/render-link.html"}, 2}, @@ -122,7 +135,7 @@ func TestLayout(t *testing.T) { c.Assert(err, qt.IsNil) c.Assert(layouts, qt.Not(qt.IsNil)) - c.Assert(len(layouts) >= len(this.expect), qt.Equals, true) + c.Assert(len(layouts) >= len(this.expect), qt.Equals, true, qt.Commentf("%d vs %d", len(layouts), len(this.expect))) // Not checking the complete list for now ... got := layouts[:len(this.expect)] if len(layouts) != this.expectCount || !reflect.DeepEqual(got, this.expect) { @@ -130,7 +143,7 @@ func TestLayout(t *testing.T) { 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) + c.Fatalf("Got %d/%d:\n%v\nExpected:\n%v\nAll:\n%v\nFormatted:\n%s", len(layouts), this.expectCount, got, this.expect, layouts, formatted) } |