Make cascade front matter order deterministic

Fixes #12594
This commit is contained in:
Bjørn Erik Pedersen
2025-01-22 17:47:54 +01:00
parent 77a8e347bc
commit 7f0f50b133
10 changed files with 318 additions and 48 deletions

View File

@@ -841,3 +841,38 @@ title: p1
b.AssertFileExists("public/s1/index.html", false)
b.AssertFileExists("public/s1/p1/index.html", false)
}
// Issue 12594.
func TestCascadeOrder(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['rss','sitemap','taxonomy','term', 'home']
-- content/_index.md --
---
title: Home
cascade:
- _target:
path: "**"
params:
background: yosemite.jpg
- _target:
params:
background: goldenbridge.jpg
---
-- content/p1.md --
---
title: p1
---
-- layouts/_default/single.html --
Background: {{ .Params.background }}|
-- layouts/_default/list.html --
{{ .Title }}|
`
for i := 0; i < 10; i++ {
b := Test(t, files)
b.AssertFileContent("public/p1/index.html", "Background: yosemite.jpg")
}
}

View File

@@ -1387,7 +1387,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
}
// Handle cascades first to get any default dates set.
var cascade map[page.PageMatcher]maps.Params
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
if keyPage == "" {
// Home page gets it's cascade from the site config.
cascade = sa.conf.Cascade.Config
@@ -1399,7 +1399,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
} else {
_, data := pw.WalkContext.Data().LongestPrefix(keyPage)
if data != nil {
cascade = data.(map[page.PageMatcher]maps.Params)
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
}
}
@@ -1481,11 +1481,11 @@ func (sa *sitePagesAssembler) applyAggregates() error {
pageResource := rs.r.(*pageState)
relPath := pageResource.m.pathInfo.BaseRel(pageBundle.m.pathInfo)
pageResource.m.resourcePath = relPath
var cascade map[page.PageMatcher]maps.Params
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
// Apply cascade (if set) to the page.
_, data := pw.WalkContext.Data().LongestPrefix(resourceKey)
if data != nil {
cascade = data.(map[page.PageMatcher]maps.Params)
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
}
if err := pageResource.setMetaPost(cascade); err != nil {
return false, err
@@ -1549,10 +1549,10 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
const eventName = "dates"
if p.Kind() == kinds.KindTerm {
var cascade map[page.PageMatcher]maps.Params
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
_, data := pw.WalkContext.Data().LongestPrefix(s)
if data != nil {
cascade = data.(map[page.PageMatcher]maps.Params)
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
}
if err := p.setMetaPost(cascade); err != nil {
return false, err

View File

@@ -87,8 +87,8 @@ type pageMetaParams struct {
// These are only set in watch mode.
datesOriginal pagemeta.Dates
paramsOriginal map[string]any // contains the original params as defined in the front matter.
cascadeOriginal map[page.PageMatcher]maps.Params // contains the original cascade as defined in the front matter.
paramsOriginal map[string]any // contains the original params as defined in the front matter.
cascadeOriginal *maps.Ordered[page.PageMatcher, maps.Params] // contains the original cascade as defined in the front matter.
}
// From page front matter.
@@ -96,10 +96,10 @@ type pageMetaFrontMatter struct {
configuredOutputFormats output.Formats // outputs defined in front matter.
}
func (m *pageMetaParams) init(preserveOringal bool) {
if preserveOringal {
func (m *pageMetaParams) init(preserveOriginal bool) {
if preserveOriginal {
m.paramsOriginal = xmaps.Clone[maps.Params](m.pageConfig.Params)
m.cascadeOriginal = xmaps.Clone[map[page.PageMatcher]maps.Params](m.pageConfig.CascadeCompiled)
m.cascadeOriginal = m.pageConfig.CascadeCompiled.Clone()
}
}
@@ -306,22 +306,22 @@ func (p *pageMeta) setMetaPre(pi *contentParseInfo, logger loggers.Logger, conf
return nil
}
func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error {
func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, maps.Params]) error {
ps.m.setMetaPostCount++
var cascadeHashPre uint64
if ps.m.setMetaPostCount > 1 {
cascadeHashPre = hashing.HashUint64(ps.m.pageConfig.CascadeCompiled)
ps.m.pageConfig.CascadeCompiled = xmaps.Clone[map[page.PageMatcher]maps.Params](ps.m.cascadeOriginal)
ps.m.pageConfig.CascadeCompiled = ps.m.cascadeOriginal.Clone()
}
// Apply cascades first so they can be overridden later.
if cascade != nil {
if ps.m.pageConfig.CascadeCompiled != nil {
for k, v := range cascade {
vv, found := ps.m.pageConfig.CascadeCompiled[k]
cascade.Range(func(k page.PageMatcher, v maps.Params) bool {
vv, found := ps.m.pageConfig.CascadeCompiled.Get(k)
if !found {
ps.m.pageConfig.CascadeCompiled[k] = v
ps.m.pageConfig.CascadeCompiled.Set(k, v)
} else {
// Merge
for ck, cv := range v {
@@ -330,7 +330,8 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error
}
}
}
}
return true
})
cascade = ps.m.pageConfig.CascadeCompiled
} else {
ps.m.pageConfig.CascadeCompiled = cascade
@@ -354,16 +355,17 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error
}
// Cascade is also applied to itself.
for m, v := range cascade {
if !m.Matches(ps) {
continue
cascade.Range(func(k page.PageMatcher, v maps.Params) bool {
if !k.Matches(ps) {
return true
}
for kk, vv := range v {
if _, found := ps.m.pageConfig.Params[kk]; !found {
ps.m.pageConfig.Params[kk] = vv
}
}
}
return true
})
if err := ps.setMetaPostParams(); err != nil {
return err