diff --git a/output/layout_base.go b/output/layout_base.go index a0d2bc4eb..876746556 100644 --- a/output/layout_base.go +++ b/output/layout_base.go @@ -38,7 +38,11 @@ type TemplateNames struct { } type TemplateLookupDescriptor struct { - // The full path to the site or theme root. + // TemplateDir is the project or theme root of the current template. + // This will be the same as WorkingDir for non-theme templates. + TemplateDir string + + // The full path to the site root. WorkingDir string // Main project layout dir, defaults to "layouts" @@ -51,8 +55,8 @@ type TemplateLookupDescriptor struct { // The template name prefix to look for, i.e. "theme". Prefix string - // The theme name if active. - Theme string + // The theme dir if theme active. + ThemeDir string // All the output formats in play. This is used to decide if text/template or // html/template. @@ -64,16 +68,29 @@ type TemplateLookupDescriptor struct { func CreateTemplateNames(d TemplateLookupDescriptor) (TemplateNames, error) { - var id TemplateNames - name := filepath.ToSlash(d.RelPath) if d.Prefix != "" { name = strings.Trim(d.Prefix, "/") + "/" + name } - baseLayoutDir := filepath.Join(d.WorkingDir, d.LayoutDir) - fullPath := filepath.Join(baseLayoutDir, d.RelPath) + var ( + id TemplateNames + + // This is the path to the actual template in process. This may + // be in the theme's or the project's /layouts. + baseLayoutDir = filepath.Join(d.TemplateDir, d.LayoutDir) + fullPath = filepath.Join(baseLayoutDir, d.RelPath) + + // This is always the project's layout dir. + baseWorkLayoutDir = filepath.Join(d.WorkingDir, d.LayoutDir) + + baseThemeLayoutDir string + ) + + if d.ThemeDir != "" { + baseThemeLayoutDir = filepath.Join(d.ThemeDir, "layouts") + } // The filename will have a suffix with an optional type indicator. // Examples: @@ -140,8 +157,8 @@ func CreateTemplateNames(d TemplateLookupDescriptor) (TemplateNames, error) { currBaseFilename := fmt.Sprintf("%s-%s", filenameNoSuffix, baseFilename) templateDir := filepath.Dir(fullPath) - themeDir := filepath.Join(d.WorkingDir, d.Theme) + // Find the base, e.g. "_default". baseTemplatedDir := strings.TrimPrefix(templateDir, baseLayoutDir) baseTemplatedDir = strings.TrimPrefix(baseTemplatedDir, helpers.FilePathSeparator) @@ -162,7 +179,7 @@ func CreateTemplateNames(d TemplateLookupDescriptor) (TemplateNames, error) { Loop: for _, pair := range pairsToCheck { - pathsToCheck := basePathsToCheck(pair, baseLayoutDir, themeDir) + pathsToCheck := basePathsToCheck(pair, baseLayoutDir, baseWorkLayoutDir, baseThemeLayoutDir) for _, pathToCheck := range pathsToCheck { if ok, err := d.FileExists(pathToCheck); err == nil && ok { @@ -177,13 +194,18 @@ func CreateTemplateNames(d TemplateLookupDescriptor) (TemplateNames, error) { } -func basePathsToCheck(path []string, layoutDir, themeDir string) []string { - // Always look in the project. - pathsToCheck := []string{filepath.Join((append([]string{layoutDir}, path...))...)} +func basePathsToCheck(path []string, layoutDir, workLayoutDir, themeLayoutDir string) []string { + // workLayoutDir will always be the most specific, so start there. + pathsToCheck := []string{filepath.Join((append([]string{workLayoutDir}, path...))...)} + + if layoutDir != "" && layoutDir != workLayoutDir { + pathsToCheck = append(pathsToCheck, filepath.Join((append([]string{layoutDir}, path...))...)) + } // May have a theme - if themeDir != "" { - pathsToCheck = append(pathsToCheck, filepath.Join((append([]string{themeDir, "layouts"}, path...))...)) + if themeLayoutDir != "" && themeLayoutDir != layoutDir { + pathsToCheck = append(pathsToCheck, filepath.Join((append([]string{themeLayoutDir}, path...))...)) + } return pathsToCheck diff --git a/output/layout_base_test.go b/output/layout_base_test.go index 16be615f2..b78f31352 100644 --- a/output/layout_base_test.go +++ b/output/layout_base_test.go @@ -25,6 +25,7 @@ func TestLayoutBase(t *testing.T) { var ( workingDir = "/sites/mysite/" + themeDir = "/themes/mytheme/" layoutBase1 = "layouts" layoutPath1 = "_default/single.html" layoutPathAmp = "_default/single.amp.html" @@ -38,76 +39,76 @@ func TestLayoutBase(t *testing.T) { basePathMatchStrings string expect TemplateNames }{ - {"No base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1}, false, "", + {"No base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1}, false, "", TemplateNames{ Name: "_default/single.html", OverlayFilename: "/sites/mysite/layouts/_default/single.html", }}, - {"Base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1}, true, "", + {"Base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1}, true, "", TemplateNames{ Name: "_default/single.html", OverlayFilename: "/sites/mysite/layouts/_default/single.html", MasterFilename: "/sites/mysite/layouts/_default/single-baseof.html", }}, - {"Base in theme", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, Theme: "mytheme"}, true, + {"Base in theme", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, ThemeDir: themeDir}, true, "mytheme/layouts/_default/baseof.html", TemplateNames{ Name: "_default/single.html", OverlayFilename: "/sites/mysite/layouts/_default/single.html", - MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html", + MasterFilename: "/themes/mytheme/layouts/_default/baseof.html", }}, - {"Template in theme, base in theme", TemplateLookupDescriptor{WorkingDir: filepath.Join(workingDir, "mytheme"), LayoutDir: layoutBase1, RelPath: layoutPath1, Theme: "mytheme"}, true, + {"Template in theme, base in theme", TemplateLookupDescriptor{TemplateDir: themeDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, ThemeDir: themeDir}, true, "mytheme/layouts/_default/baseof.html", TemplateNames{ Name: "_default/single.html", - OverlayFilename: "/sites/mysite/mytheme/layouts/_default/single.html", - MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html", + OverlayFilename: "/themes/mytheme/layouts/_default/single.html", + MasterFilename: "/themes/mytheme/layouts/_default/baseof.html", }}, - {"Template in theme, base in site", TemplateLookupDescriptor{WorkingDir: filepath.Join(workingDir, "mytheme"), LayoutDir: layoutBase1, RelPath: layoutPath1, Theme: "mytheme"}, true, - "mytheme/layouts/_default/baseof.html", + {"Template in theme, base in site", TemplateLookupDescriptor{TemplateDir: themeDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, ThemeDir: themeDir}, true, + "/sites/mysite/layouts/_default/baseof.html", TemplateNames{ Name: "_default/single.html", - OverlayFilename: "/sites/mysite/mytheme/layouts/_default/single.html", - MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html", + OverlayFilename: "/themes/mytheme/layouts/_default/single.html", + MasterFilename: "/sites/mysite/layouts/_default/baseof.html", }}, - {"Template in site, base in theme", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, Theme: "mytheme"}, true, - "/sites/mysite/mytheme/layouts/_default/baseof.html", + {"Template in site, base in theme", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, ThemeDir: themeDir}, true, + "/themes/mytheme", TemplateNames{ Name: "_default/single.html", OverlayFilename: "/sites/mysite/layouts/_default/single.html", - MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html", + MasterFilename: "/themes/mytheme/layouts/_default/single-baseof.html", }}, - {"With prefix, base in theme", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, - Theme: "mytheme", Prefix: "someprefix"}, true, + {"With prefix, base in theme", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, + ThemeDir: themeDir, Prefix: "someprefix"}, true, "mytheme/layouts/_default/baseof.html", TemplateNames{ Name: "someprefix/_default/single.html", OverlayFilename: "/sites/mysite/layouts/_default/single.html", - MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html", + MasterFilename: "/themes/mytheme/layouts/_default/baseof.html", }}, - {"Partial", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: "partials/menu.html"}, true, + {"Partial", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: "partials/menu.html"}, true, "mytheme/layouts/_default/baseof.html", TemplateNames{ Name: "partials/menu.html", OverlayFilename: "/sites/mysite/layouts/partials/menu.html", }}, - {"AMP, no base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, false, "", + {"AMP, no base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, false, "", TemplateNames{ Name: "_default/single.amp.html", OverlayFilename: "/sites/mysite/layouts/_default/single.amp.html", }}, - {"JSON, no base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathJSON}, false, "", + {"JSON, no base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathJSON}, false, "", TemplateNames{ Name: "_default/single.json", OverlayFilename: "/sites/mysite/layouts/_default/single.json", }}, - {"AMP with base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, true, "single-baseof.html|single-baseof.amp.html", + {"AMP with base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, true, "single-baseof.html|single-baseof.amp.html", TemplateNames{ Name: "_default/single.amp.html", OverlayFilename: "/sites/mysite/layouts/_default/single.amp.html", MasterFilename: "/sites/mysite/layouts/_default/single-baseof.amp.html", }}, - {"AMP with no match in base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, true, "single-baseof.html", + {"AMP with no match in base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, true, "single-baseof.html", TemplateNames{ Name: "_default/single.amp.html", OverlayFilename: "/sites/mysite/layouts/_default/single.amp.html", @@ -115,7 +116,7 @@ func TestLayoutBase(t *testing.T) { MasterFilename: "", }}, - {"JSON with base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathJSON}, true, "single-baseof.json", + {"JSON with base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathJSON}, true, "single-baseof.json", TemplateNames{ Name: "_default/single.json", OverlayFilename: "/sites/mysite/layouts/_default/single.json", diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go index c14d24146..f1ab37ee0 100644 --- a/tpl/tplimpl/template.go +++ b/tpl/tplimpl/template.go @@ -420,13 +420,15 @@ func (t *templateHandler) loadTemplates(absPath string, prefix string) { li := strings.LastIndex(path, layoutDir) + len(layoutDir) + 1 relPath := path[li:] + templateDir := path[:li-len(layoutDir)-1] descriptor := output.TemplateLookupDescriptor{ + TemplateDir: templateDir, WorkingDir: workingDir, LayoutDir: layoutDir, RelPath: relPath, Prefix: prefix, - Theme: t.PathSpec.Theme(), + ThemeDir: themeDir, OutputFormats: t.OutputFormatsConfig, FileExists: func(filename string) (bool, error) { return helpers.Exists(filename, t.Fs.Source)