URI: 
       Add config.cascade - hugo - [fork] hugo port for 9front
  HTML git clone git@git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
   DIR commit 5cb52c23150032b3fdb211a095745c512369b463
   DIR parent 30eea3915b67f72611a3b2f4547146d4c6a96864
  HTML Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Fri,  9 Jul 2021 11:52:03 +0200
       
       Add config.cascade
       
       This commit adds support for using the `cascade` keyword in your configuration file(s), e.g. `config.toml`.
       
       Note that
       
       * Every feature of `cascade` is available, e.g. `_target` to target specific page sets.
       * Pages, e.g. the home page, can overwrite the cascade defined in config.
       
       Fixes #8741
       
       Diffstat:
         M config/defaultConfigProvider.go     |       2 +-
         M hugolib/cascade_test.go             |      65 +++++++++++++++++++++++++++++++
         M hugolib/content_map_page.go         |       3 +++
         M hugolib/page__meta.go               |      32 ++++---------------------------
         M hugolib/site.go                     |      22 +++++++++++++++++++---
         M resources/page/page_matcher.go      |      37 +++++++++++++++++++++++++++++++
       
       6 files changed, 129 insertions(+), 32 deletions(-)
       ---
   DIR diff --git a/config/defaultConfigProvider.go b/config/defaultConfigProvider.go
       @@ -27,10 +27,10 @@ import (
        var (
        
                // ConfigRootKeysSet contains all of the config map root keys.
       -        // TODO(bep) use this for something (docs etc.)
                ConfigRootKeysSet = map[string]bool{
                        "build":         true,
                        "caches":        true,
       +                "cascade":       true,
                        "frontmatter":   true,
                        "languages":     true,
                        "imaging":       true,
   DIR diff --git a/hugolib/cascade_test.go b/hugolib/cascade_test.go
       @@ -20,6 +20,8 @@ import (
                "strings"
                "testing"
        
       +        "github.com/gohugoio/hugo/common/maps"
       +
                qt "github.com/frankban/quicktest"
                "github.com/gohugoio/hugo/parser"
                "github.com/gohugoio/hugo/parser/metadecoders"
       @@ -50,6 +52,69 @@ func BenchmarkCascade(b *testing.B) {
                }
        }
        
       +func TestCascadeConfig(t *testing.T) {
       +        c := qt.New(t)
       +
       +        // Make sure the cascade from config gets applied even if we're not
       +        // having a content file for the home page.
       +        for _, withHomeContent := range []bool{true, false} {
       +                testName := "Home content file"
       +                if !withHomeContent {
       +                        testName = "No home content file"
       +                }
       +                c.Run(testName, func(c *qt.C) {
       +                        b := newTestSitesBuilder(c)
       +
       +                        b.WithConfigFile("toml", `
       +baseURL="https://example.org"
       +
       +[cascade]
       +img1 = "img1-config.jpg"
       +imgconfig = "img-config.jpg"
       +
       +`)
       +
       +                        if withHomeContent {
       +                                b.WithContent("_index.md", `
       +---
       +title: "Home"
       +cascade:
       +  img1: "img1-home.jpg"
       +  img2: "img2-home.jpg"
       +---
       +`)
       +                        }
       +
       +                        b.WithContent("p1.md", ``)
       +
       +                        b.Build(BuildCfg{})
       +
       +                        p1 := b.H.Sites[0].getPage("p1")
       +
       +                        if withHomeContent {
       +                                b.Assert(p1.Params(), qt.DeepEquals, maps.Params{
       +                                        "imgconfig":     "img-config.jpg",
       +                                        "draft":         bool(false),
       +                                        "iscjklanguage": bool(false),
       +                                        "img1":          "img1-home.jpg",
       +                                        "img2":          "img2-home.jpg",
       +                                })
       +                        } else {
       +                                b.Assert(p1.Params(), qt.DeepEquals, maps.Params{
       +                                        "img1":          "img1-config.jpg",
       +                                        "imgconfig":     "img-config.jpg",
       +                                        "draft":         bool(false),
       +                                        "iscjklanguage": bool(false),
       +                                })
       +
       +                        }
       +
       +                })
       +
       +        }
       +
       +}
       +
        func TestCascade(t *testing.T) {
                allLangs := []string{"en", "nn", "nb", "sv"}
        
   DIR diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go
       @@ -462,10 +462,13 @@ func (m *pageMap) assembleSections() error {
        
                        if parent != nil {
                                parentBucket = parent.p.bucket
       +                } else if s == "/" {
       +                        parentBucket = m.s.siteBucket
                        }
        
                        kind := page.KindSection
                        if s == "/" {
       +
                                kind = page.KindHome
                        }
        
   DIR diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go
       @@ -340,34 +340,10 @@ func (pm *pageMeta) setMetadata(parentBucket *pagesMapBucket, p *pageState, fron
                        if p.bucket != nil {
                                // Check for any cascade define on itself.
                                if cv, found := frontmatter["cascade"]; found {
       -                                if v, err := maps.ToSliceStringMap(cv); err == nil {
       -                                        p.bucket.cascade = make(map[page.PageMatcher]maps.Params)
       -
       -                                        for _, vv := range v {
       -                                                var m page.PageMatcher
       -                                                if mv, found := vv["_target"]; found {
       -                                                        err := page.DecodePageMatcher(mv, &m)
       -                                                        if err != nil {
       -                                                                return err
       -                                                        }
       -                                                }
       -                                                c, found := p.bucket.cascade[m]
       -                                                if found {
       -                                                        // Merge
       -                                                        for k, v := range vv {
       -                                                                if _, found := c[k]; !found {
       -                                                                        c[k] = v
       -                                                                }
       -                                                        }
       -                                                } else {
       -                                                        p.bucket.cascade[m] = vv
       -                                                }
       -
       -                                        }
       -                                } else {
       -                                        p.bucket.cascade = map[page.PageMatcher]maps.Params{
       -                                                {}: maps.ToStringMap(cv),
       -                                        }
       +                                var err error
       +                                p.bucket.cascade, err = page.DecodeCascade(cv)
       +                                if err != nil {
       +                                        return err
                                        }
                                }
                        }
   DIR diff --git a/hugolib/site.go b/hugolib/site.go
       @@ -103,7 +103,7 @@ import (
        type Site struct {
        
                // The owning container. When multiple languages, there will be multiple
       -        // sites.
       +        // sites .
                h *HugoSites
        
                *PageCollections
       @@ -113,7 +113,8 @@ type Site struct {
                Sections Taxonomy
                Info     *SiteInfo
        
       -        language *langs.Language
       +        language   *langs.Language
       +        siteBucket *pagesMapBucket
        
                siteCfg siteConfigHolder
        
       @@ -388,6 +389,7 @@ func (s *Site) reset() *Site {
                        frontmatterHandler:     s.frontmatterHandler,
                        mediaTypesConfig:       s.mediaTypesConfig,
                        language:               s.language,
       +                siteBucket:             s.siteBucket,
                        h:                      s.h,
                        publisher:              s.publisher,
                        siteConfigConfig:       s.siteConfigConfig,
       @@ -539,9 +541,23 @@ But this also means that your site configuration may not do what you expect. If 
                        enableEmoji:      cfg.Language.Cfg.GetBool("enableEmoji"),
                }
        
       -        s := &Site{
       +        var siteBucket *pagesMapBucket
       +        if cfg.Language.IsSet("cascade") {
       +                var err error
       +                cascade, err := page.DecodeCascade(cfg.Language.Get("cascade"))
       +                if err != nil {
       +                        return nil, errors.Errorf("failed to decode cascade config: %s", err)
       +                }
        
       +                siteBucket = &pagesMapBucket{
       +                        cascade: cascade,
       +                }
       +
       +        }
       +
       +        s := &Site{
                        language:      cfg.Language,
       +                siteBucket:    siteBucket,
                        disabledKinds: disabledKinds,
        
                        outputFormats:       outputFormats,
   DIR diff --git a/resources/page/page_matcher.go b/resources/page/page_matcher.go
       @@ -19,6 +19,7 @@ import (
        
                "github.com/pkg/errors"
        
       +        "github.com/gohugoio/hugo/common/maps"
                "github.com/gohugoio/hugo/hugofs/glob"
                "github.com/mitchellh/mapstructure"
        )
       @@ -70,6 +71,42 @@ func (m PageMatcher) Matches(p Page) bool {
                return true
        }
        
       +// DecodeCascade decodes in which could be eiter a map or a slice of maps.
       +func DecodeCascade(in interface{}) (map[PageMatcher]maps.Params, error) {
       +        m, err := maps.ToSliceStringMap(in)
       +        if err != nil {
       +                return map[PageMatcher]maps.Params{
       +                        {}: maps.ToStringMap(in),
       +                }, nil
       +        }
       +
       +        cascade := make(map[PageMatcher]maps.Params)
       +
       +        for _, vv := range m {
       +                var m PageMatcher
       +                if mv, found := vv["_target"]; found {
       +                        err := DecodePageMatcher(mv, &m)
       +                        if err != nil {
       +                                return nil, err
       +                        }
       +                }
       +                c, found := cascade[m]
       +                if found {
       +                        // Merge
       +                        for k, v := range vv {
       +                                if _, found := c[k]; !found {
       +                                        c[k] = v
       +                                }
       +                        }
       +                } else {
       +                        cascade[m] = vv
       +                }
       +        }
       +
       +        return cascade, nil
       +
       +}
       +
        // DecodePageMatcher decodes m into v.
        func DecodePageMatcher(m interface{}, v *PageMatcher) error {
                if err := mapstructure.WeakDecode(m, v); err != nil {