package maps
import (
+ "errors"
"fmt"
+ xmaps "maps"
"strings"
"github.com/spf13/cast"
noUpdate = noUpdate || (ps != "" && ps == ParamsMergeStrategyShallow)
for k, v := range pp {
-
if k == MergeStrategyKey {
continue
}
}
}
} else if !noUpdate {
+ if vvv, ok := v.(Params); ok {
+ v = xmaps.Clone(vvv)
+ }
p[k] = v
}
// CleanConfigStringMap is the same as CleanConfigStringMapString but for
// map[string]any.
func CleanConfigStringMap(m map[string]any) map[string]any {
+ return doCleanConfigStringMap(m, 0)
+}
+
+func doCleanConfigStringMap(m map[string]any, depth int) map[string]any {
if len(m) == 0 {
return m
}
+ const maxDepth = 1000
+ if depth > maxDepth {
+ panic(errors.New("max depth exceeded"))
+ }
if _, found := m[MergeStrategyKey]; !found {
return m
}
}
switch v2 := v.(type) {
case map[string]any:
- m2[k] = CleanConfigStringMap(v2)
+ m2[k] = doCleanConfigStringMap(v2, depth+1)
case Params:
- var p Params = CleanConfigStringMap(v2)
+ var p Params = doCleanConfigStringMap(v2, depth+1)
m2[k] = p
case map[string]string:
m2[k] = CleanConfigStringMapString(v2)
"github.com/gohugoio/hugo/resources/page/pagemeta"
"github.com/spf13/afero"
- xmaps "golang.org/x/exp/maps"
+ xmaps "maps"
)
// InternalConfig is the internal configuration for Hugo, not read from any user provided config file.
mergedConfig := config.New()
var differentRootKeys []string
switch x := v.(type) {
+ case nil:
case maps.Params:
_, found := x["params"]
if !found {
langConfigMap[k] = clone
case maps.ParamsMergeStrategy:
+
default:
panic(fmt.Sprintf("unknown type in languages config: %T", v))
//lint:ignore ST1005 end user message.
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")
-func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
+func LoadConfig(d ConfigSourceDescriptor) (configs *Configs, err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = fmt.Errorf("failed to load config: %v", r)
+ }
+ }()
+
if len(d.Environ) == 0 && !hugo.IsRunningAsTest() {
d.Environ = os.Environ()
}
return nil, fmt.Errorf("failed to load config: %w", err)
}
- configs, err := fromLoadConfigResult(d.Fs, d.Logger, res)
+ configs, err = fromLoadConfigResult(d.Fs, d.Logger, res)
if err != nil {
return nil, fmt.Errorf("failed to create config from result: %w", err)
}
loggers.SetGlobalLogger(d.Logger)
- return configs, nil
+ return
}
// ConfigSourceDescriptor describes where to find the config (e.g. config.toml etc.).
return filename, nil
}
-func (l configLoader) deleteMergeStrategies() {
+func (l configLoader) deleteMergeStrategies() (err error) {
l.cfg.WalkParams(func(params ...maps.KeyParams) bool {
params[len(params)-1].Params.DeleteMergeStrategy()
return false
})
+ return
}
func (l configLoader) wrapFileError(err error, filename string) error {
package config
import (
+ "errors"
"fmt"
+ "slices"
"strings"
"sync"
- xmaps "golang.org/x/exp/maps"
+ xmaps "maps"
"github.com/spf13/cast"
func (c *defaultConfigProvider) Keys() []string {
c.mu.RLock()
defer c.mu.RUnlock()
- return xmaps.Keys(c.root)
+ return slices.Collect(xmaps.Keys(c.root))
}
func (c *defaultConfigProvider) WalkParams(walkFn func(params ...maps.KeyParams) bool) {
- var walk func(params ...maps.KeyParams)
- walk = func(params ...maps.KeyParams) {
+ maxDepth := 1000
+ var walk func(depth int, params ...maps.KeyParams)
+ walk = func(depth int, params ...maps.KeyParams) {
+ if depth > maxDepth {
+ panic(errors.New("max depth exceeded"))
+ }
if walkFn(params...) {
return
}
paramsplus1 := make([]maps.KeyParams, i+1)
copy(paramsplus1, params)
paramsplus1[i] = maps.KeyParams{Key: k, Params: p2}
- walk(paramsplus1...)
+ walk(depth+1, paramsplus1...)
}
}
}
- walk(maps.KeyParams{Key: "", Params: c.root})
+ walk(0, maps.KeyParams{Key: "", Params: c.root})
}
func (c *defaultConfigProvider) determineMergeStrategy(params ...maps.KeyParams) maps.ParamsMergeStrategy {
github.com/yuin/goldmark-emoji v1.0.6
go.uber.org/automaxprocs v1.5.3
gocloud.dev v0.43.0
- golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b
golang.org/x/image v0.32.0
golang.org/x/mod v0.29.0
golang.org/x/net v0.46.0
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
-github.com/gohugoio/go-i18n/v2 v2.1.3-0.20251018141905-f04b60c79bd1 h1:95D/7I2Vv3N9icWWlayjoxp7rSzjcr6PaGXLw8DZzgY=
-github.com/gohugoio/go-i18n/v2 v2.1.3-0.20251018141905-f04b60c79bd1/go.mod h1:m5hu1im5Qc7LDycVLvee6MPobJiRLBYHklypFJR0/aE=
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20251018145728-cfcc22d823c6 h1:pxlAea9eRwuAnt/zKbGqlFO2ZszpIe24YpOVLf+N+4I=
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20251018145728-cfcc22d823c6/go.mod h1:m5hu1im5Qc7LDycVLvee6MPobJiRLBYHklypFJR0/aE=
github.com/gohugoio/hashstructure v0.6.0 h1:7wMB/2CfXoThFYhdWRGv3u3rUM761Cq29CxUW+NltUg=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
-golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
`
Test(t, files)
}
+
+func TestConfigYAMLAnchorsMerge(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- hugo.yaml --
+definitions:
+ params: ¶ms
+ p1: p1alias
+
+theme: "mytheme"
+defaultContentLanguage: en
+defaultContentLanguageInSubdir: true
+
+params:
+ <<: *params
+
+languages:
+ en:
+ weight: 1
+ no:
+ weight: 2
+ params:
+ <<: *params
+ p2: p2no
+ sv:
+ weight: 3
+ params: *params
+
+-- layouts/all.html --
+Params: {{ site.Params }}|
+-- themes/mytheme/hugo.yaml --
+definitions:
+ params: ¶ms
+ p1: p1aliastheme
+ p2: p2aliastheme
+
+params: *params
+
+
+`
+ b := Test(t, files)
+
+ b.AssertFileContent("public/en/index.html", "Params: map[p1:p1alias p2:p2aliastheme]|")
+ b.AssertFileContent("public/no/index.html", "Params: map[p1:p1alias p2:p2no]|")
+ b.AssertFileContent("public/sv/index.html", "Params: map[p1:p1alias p2:p2aliastheme]|")
+}
+
+func TestConfigYAMLAnchorsCyclicReference(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- hugo.yaml --
+definitions:
+ params: ¶ms
+ p1: p1alias
+
+params:
+ p3: *params
+
+languages:
+ en:
+ weight: 1
+ sv:
+ weight: 2
+ params: *params
+
+-- layouts/all.html --
+Params: {{ site.Params }}|
+
+`
+
+ for range 3 {
+ b := Test(t, files)
+ b.AssertFileContent("public/index.html", "Params: map[p3:map[p1:p1alias]]|")
+ b.AssertFileContent("public/sv/index.html", "Params: map[p1:p1alias p3:map[p1:p1alias]]|")
+
+ }
+}
"strings"
"time"
+ xmaps "maps"
+
"github.com/bep/logg"
"github.com/gobuffalo/flect"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/markup/converter"
- xmaps "golang.org/x/exp/maps"
"github.com/gohugoio/hugo/source"
"os"
"path/filepath"
"runtime"
+ "slices"
"sort"
"strings"
"sync"
"github.com/gohugoio/hugo/modules"
"github.com/gohugoio/hugo/resources"
+ xmaps "maps"
+
"github.com/gohugoio/hugo/tpl/tplimpl"
"github.com/gohugoio/hugo/tpl/tplimplinit"
- xmaps "golang.org/x/exp/maps"
// Loads the template funcs namespaces.
if w == nil || w.ids == nil {
return nil
}
- return xmaps.Keys(w.ids)
+ return slices.Collect(xmaps.Keys(w.ids))
}
func (w *WhatChanged) Drain() []identity.Identity {
import (
"html/template"
+ "slices"
"sort"
"github.com/gohugoio/hugo/common/maps"
"github.com/mitchellh/mapstructure"
"github.com/spf13/cast"
- "slices"
)
var smc = newMenuCache()
"sync"
"time"
- xmaps "golang.org/x/exp/maps"
+ xmaps "maps"
"github.com/gohugoio/hugo/common/collections"
"github.com/gohugoio/hugo/common/maps"