Skip to content

Commit bd50c9c

Browse files
committed
Misc YAML adjustments
Closes #14067
1 parent a8e0ca9 commit bd50c9c

File tree

11 files changed

+128
-24
lines changed

11 files changed

+128
-24
lines changed

‎common/maps/params.go‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
package maps
1515

1616
import (
17+
"errors"
1718
"fmt"
19+
xmaps "maps"
1820
"strings"
1921

2022
"github.com/spf13/cast"
@@ -101,7 +103,6 @@ func (p Params) merge(ps ParamsMergeStrategy, pp Params) {
101103
noUpdate = noUpdate || (ps != "" && ps == ParamsMergeStrategyShallow)
102104

103105
for k, v := range pp {
104-
105106
if k == MergeStrategyKey {
106107
continue
107108
}
@@ -115,6 +116,9 @@ func (p Params) merge(ps ParamsMergeStrategy, pp Params) {
115116
}
116117
}
117118
} else if !noUpdate {
119+
if vvv, ok := v.(Params); ok {
120+
v = xmaps.Clone(vvv)
121+
}
118122
p[k] = v
119123
}
120124

@@ -266,9 +270,17 @@ func CleanConfigStringMapString(m map[string]string) map[string]string {
266270
// CleanConfigStringMap is the same as CleanConfigStringMapString but for
267271
// map[string]any.
268272
func CleanConfigStringMap(m map[string]any) map[string]any {
273+
return doCleanConfigStringMap(m, 0)
274+
}
275+
276+
func doCleanConfigStringMap(m map[string]any, depth int) map[string]any {
269277
if len(m) == 0 {
270278
return m
271279
}
280+
const maxDepth = 1000
281+
if depth > maxDepth {
282+
panic(errors.New("max depth exceeded"))
283+
}
272284
if _, found := m[MergeStrategyKey]; !found {
273285
return m
274286
}
@@ -280,9 +292,9 @@ func CleanConfigStringMap(m map[string]any) map[string]any {
280292
}
281293
switch v2 := v.(type) {
282294
case map[string]any:
283-
m2[k] = CleanConfigStringMap(v2)
295+
m2[k] = doCleanConfigStringMap(v2, depth+1)
284296
case Params:
285-
var p Params = CleanConfigStringMap(v2)
297+
var p Params = doCleanConfigStringMap(v2, depth+1)
286298
m2[k] = p
287299
case map[string]string:
288300
m2[k] = CleanConfigStringMapString(v2)

‎config/allconfig/allconfig.go‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ import (
5656
"github.com/gohugoio/hugo/resources/page/pagemeta"
5757
"github.com/spf13/afero"
5858

59-
xmaps "golang.org/x/exp/maps"
59+
xmaps "maps"
6060
)
6161

6262
// InternalConfig is the internal configuration for Hugo, not read from any user provided config file.
@@ -1054,6 +1054,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
10541054
mergedConfig := config.New()
10551055
var differentRootKeys []string
10561056
switch x := v.(type) {
1057+
case nil:
10571058
case maps.Params:
10581059
_, found := x["params"]
10591060
if !found {
@@ -1129,6 +1130,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
11291130

11301131
langConfigMap[k] = clone
11311132
case maps.ParamsMergeStrategy:
1133+
11321134
default:
11331135
panic(fmt.Sprintf("unknown type in languages config: %T", v))
11341136

‎config/allconfig/load.go‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,13 @@ import (
4040
//lint:ignore ST1005 end user message.
4141
var ErrNoConfigFile = errors.New("Unable to locate config file or config directory. Perhaps you need to create a new site.\n Run `hugo help new` for details.\n")
4242

43-
func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
43+
func LoadConfig(d ConfigSourceDescriptor) (configs *Configs, err error) {
44+
defer func() {
45+
if r := recover(); r != nil {
46+
err = fmt.Errorf("failed to load config: %v", r)
47+
}
48+
}()
49+
4450
if len(d.Environ) == 0 && !hugo.IsRunningAsTest() {
4551
d.Environ = os.Environ()
4652
}
@@ -59,7 +65,7 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
5965
return nil, fmt.Errorf("failed to load config: %w", err)
6066
}
6167

62-
configs, err := fromLoadConfigResult(d.Fs, d.Logger, res)
68+
configs, err = fromLoadConfigResult(d.Fs, d.Logger, res)
6369
if err != nil {
6470
return nil, fmt.Errorf("failed to create config from result: %w", err)
6571
}
@@ -93,7 +99,7 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
9399

94100
loggers.SetGlobalLogger(d.Logger)
95101

96-
return configs, nil
102+
return
97103
}
98104

99105
// ConfigSourceDescriptor describes where to find the config (e.g. config.toml etc.).
@@ -538,11 +544,12 @@ func (l configLoader) loadConfig(configName string) (string, error) {
538544
return filename, nil
539545
}
540546

541-
func (l configLoader) deleteMergeStrategies() {
547+
func (l configLoader) deleteMergeStrategies() (err error) {
542548
l.cfg.WalkParams(func(params ...maps.KeyParams) bool {
543549
params[len(params)-1].Params.DeleteMergeStrategy()
544550
return false
545551
})
552+
return
546553
}
547554

548555
func (l configLoader) wrapFileError(err error, filename string) error {

‎config/defaultConfigProvider.go‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
package config
1515

1616
import (
17+
"errors"
1718
"fmt"
19+
"slices"
1820
"strings"
1921
"sync"
2022

21-
xmaps "golang.org/x/exp/maps"
23+
xmaps "maps"
2224

2325
"github.com/spf13/cast"
2426

@@ -237,12 +239,16 @@ func (c *defaultConfigProvider) Merge(k string, v any) {
237239
func (c *defaultConfigProvider) Keys() []string {
238240
c.mu.RLock()
239241
defer c.mu.RUnlock()
240-
return xmaps.Keys(c.root)
242+
return slices.Collect(xmaps.Keys(c.root))
241243
}
242244

243245
func (c *defaultConfigProvider) WalkParams(walkFn func(params ...maps.KeyParams) bool) {
244-
var walk func(params ...maps.KeyParams)
245-
walk = func(params ...maps.KeyParams) {
246+
maxDepth := 1000
247+
var walk func(depth int, params ...maps.KeyParams)
248+
walk = func(depth int, params ...maps.KeyParams) {
249+
if depth > maxDepth {
250+
panic(errors.New("max depth exceeded"))
251+
}
246252
if walkFn(params...) {
247253
return
248254
}
@@ -253,11 +259,11 @@ func (c *defaultConfigProvider) WalkParams(walkFn func(params ...maps.KeyParams)
253259
paramsplus1 := make([]maps.KeyParams, i+1)
254260
copy(paramsplus1, params)
255261
paramsplus1[i] = maps.KeyParams{Key: k, Params: p2}
256-
walk(paramsplus1...)
262+
walk(depth+1, paramsplus1...)
257263
}
258264
}
259265
}
260-
walk(maps.KeyParams{Key: "", Params: c.root})
266+
walk(0, maps.KeyParams{Key: "", Params: c.root})
261267
}
262268

263269
func (c *defaultConfigProvider) determineMergeStrategy(params ...maps.KeyParams) maps.ParamsMergeStrategy {

‎go.mod‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ require (
7575
github.com/yuin/goldmark-emoji v1.0.6
7676
go.uber.org/automaxprocs v1.5.3
7777
gocloud.dev v0.43.0
78-
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b
7978
golang.org/x/image v0.32.0
8079
golang.org/x/mod v0.29.0
8180
golang.org/x/net v0.46.0

‎go.sum‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,6 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
267267
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
268268
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
269269
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
270-
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20251018141905-f04b60c79bd1 h1:95D/7I2Vv3N9icWWlayjoxp7rSzjcr6PaGXLw8DZzgY=
271-
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20251018141905-f04b60c79bd1/go.mod h1:m5hu1im5Qc7LDycVLvee6MPobJiRLBYHklypFJR0/aE=
272270
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20251018145728-cfcc22d823c6 h1:pxlAea9eRwuAnt/zKbGqlFO2ZszpIe24YpOVLf+N+4I=
273271
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20251018145728-cfcc22d823c6/go.mod h1:m5hu1im5Qc7LDycVLvee6MPobJiRLBYHklypFJR0/aE=
274272
github.com/gohugoio/hashstructure v0.6.0 h1:7wMB/2CfXoThFYhdWRGv3u3rUM761Cq29CxUW+NltUg=
@@ -594,8 +592,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
594592
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
595593
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
596594
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
597-
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
598-
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
599595
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
600596
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
601597
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=

‎hugolib/config_test.go‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,3 +1570,82 @@ title: "P1 us"
15701570
`
15711571
Test(t, files)
15721572
}
1573+
1574+
func TestConfigYAMLAnchorsMerge(t *testing.T) {
1575+
t.Parallel()
1576+
1577+
files := `
1578+
-- hugo.yaml --
1579+
definitions:
1580+
params: &params
1581+
p1: p1alias
1582+
1583+
theme: "mytheme"
1584+
defaultContentLanguage: en
1585+
defaultContentLanguageInSubdir: true
1586+
1587+
params:
1588+
<<: *params
1589+
1590+
languages:
1591+
en:
1592+
weight: 1
1593+
no:
1594+
weight: 2
1595+
params:
1596+
<<: *params
1597+
p2: p2no
1598+
sv:
1599+
weight: 3
1600+
params: *params
1601+
1602+
-- layouts/all.html --
1603+
Params: {{ site.Params }}|
1604+
-- themes/mytheme/hugo.yaml --
1605+
definitions:
1606+
params: &params
1607+
p1: p1aliastheme
1608+
p2: p2aliastheme
1609+
1610+
params: *params
1611+
1612+
1613+
`
1614+
b := Test(t, files)
1615+
1616+
b.AssertFileContent("public/en/index.html", "Params: map[p1:p1alias p2:p2aliastheme]|")
1617+
b.AssertFileContent("public/no/index.html", "Params: map[p1:p1alias p2:p2no]|")
1618+
b.AssertFileContent("public/sv/index.html", "Params: map[p1:p1alias p2:p2aliastheme]|")
1619+
}
1620+
1621+
func TestConfigYAMLAnchorsCyclicReference(t *testing.T) {
1622+
t.Parallel()
1623+
1624+
files := `
1625+
-- hugo.yaml --
1626+
definitions:
1627+
params: &params
1628+
p1: p1alias
1629+
1630+
params:
1631+
p3: *params
1632+
1633+
languages:
1634+
en:
1635+
weight: 1
1636+
sv:
1637+
weight: 2
1638+
params: *params
1639+
1640+
-- layouts/all.html --
1641+
Params: {{ site.Params }}|
1642+
1643+
`
1644+
1645+
for range 3 {
1646+
b := Test(t, files)
1647+
b.AssertFileContent("public/index.html", "Params: map[p3:map[p1:p1alias]]|")
1648+
b.AssertFileContent("public/sv/index.html", "Params: map[p1:p1alias p3:map[p1:p1alias]]|")
1649+
1650+
}
1651+
}

‎hugolib/page__meta.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ import (
2121
"strings"
2222
"time"
2323

24+
xmaps "maps"
25+
2426
"github.com/bep/logg"
2527
"github.com/gobuffalo/flect"
2628
"github.com/gohugoio/hugo/langs"
2729
"github.com/gohugoio/hugo/markup/converter"
28-
xmaps "golang.org/x/exp/maps"
2930

3031
"github.com/gohugoio/hugo/source"
3132

‎hugolib/site.go‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"os"
2424
"path/filepath"
2525
"runtime"
26+
"slices"
2627
"sort"
2728
"strings"
2829
"sync"
@@ -48,9 +49,10 @@ import (
4849
"github.com/gohugoio/hugo/modules"
4950
"github.com/gohugoio/hugo/resources"
5051

52+
xmaps "maps"
53+
5154
"github.com/gohugoio/hugo/tpl/tplimpl"
5255
"github.com/gohugoio/hugo/tpl/tplimplinit"
53-
xmaps "golang.org/x/exp/maps"
5456

5557
// Loads the template funcs namespaces.
5658

@@ -999,7 +1001,7 @@ func (w *WhatChanged) Changes() []identity.Identity {
9991001
if w == nil || w.ids == nil {
10001002
return nil
10011003
}
1002-
return xmaps.Keys(w.ids)
1004+
return slices.Collect(xmaps.Keys(w.ids))
10031005
}
10041006

10051007
func (w *WhatChanged) Drain() []identity.Identity {

‎navigation/menu.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package navigation
1616

1717
import (
1818
"html/template"
19+
"slices"
1920
"sort"
2021

2122
"github.com/gohugoio/hugo/common/maps"
@@ -25,7 +26,6 @@ import (
2526
"github.com/mitchellh/mapstructure"
2627

2728
"github.com/spf13/cast"
28-
"slices"
2929
)
3030

3131
var smc = newMenuCache()

0 commit comments

Comments
 (0)