summaryrefslogtreecommitdiffstats
path: root/output
diff options
context:
space:
mode:
Diffstat (limited to 'output')
-rw-r--r--output/layout.go30
-rw-r--r--output/layout_base.go182
-rw-r--r--output/layout_base_test.go163
-rw-r--r--output/layout_test.go21
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)
}