--- /dev/null
+// Copyright 2025 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hdebug
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/gohugoio/hugo/common/types"
+ "github.com/gohugoio/hugo/htesting"
+)
+
+// Printf is a debug print function that should be removed before committing code to the repository.
+func Printf(format string, args ...any) {
+ panicIfRealCI()
+ if len(args) == 1 && !strings.Contains(format, "%") {
+ format = format + ": %v"
+ }
+ if !strings.HasSuffix(format, "\n") {
+ format = format + "\n"
+ }
+ fmt.Printf(format, args...)
+}
+
+func AssertNotNil(a ...any) {
+ panicIfRealCI()
+ for _, v := range a {
+ if types.IsNil(v) {
+ panic("hdebug.AssertNotNil: value is nil")
+ }
+ }
+}
+
+func Panicf(format string, args ...any) {
+ panicIfRealCI()
+ // fmt.Println(stack())
+ if len(args) == 1 && !strings.Contains(format, "%") {
+ format = format + ": %v"
+ }
+ if !strings.HasSuffix(format, "\n") {
+ format = format + "\n"
+ }
+ panic(fmt.Sprintf(format, args...))
+}
+
+func panicIfRealCI() {
+ if htesting.IsRealCI() {
+ panic("This debug statement should be removed before committing code!")
+ }
+}
import (
"errors"
"fmt"
- xmaps "maps"
"strings"
"github.com/spf13/cast"
// SetParams overwrites values in dst with values in src for common or new keys.
// This is done recursively.
func SetParams(dst, src Params) {
+ setParams(dst, src, 0)
+}
+
+func setParams(dst, src Params, depth int) {
+ const maxDepth = 1000
+ if depth > maxDepth {
+ panic(errors.New("max depth exceeded"))
+ }
for k, v := range src {
vv, found := dst[k]
if !found {
switch vvv := vv.(type) {
case Params:
if pv, ok := v.(Params); ok {
- SetParams(vvv, pv)
+ setParams(vvv, pv, depth+1)
} else {
dst[k] = v
}
}
}
} else if !noUpdate {
- if vvv, ok := v.(Params); ok {
- v = xmaps.Clone(vvv)
- }
p[k] = v
}
}
}
+// CloneParamsDeep does a deep clone of the given Params,
+// meaning that any nested Params will be cloned as well.
+func CloneParamsDeep(m Params) Params {
+ return cloneParamsDeep(m, 0)
+}
+
+func cloneParamsDeep(m Params, depth int) Params {
+ const maxDepth = 1000
+ if depth > maxDepth {
+ panic(errors.New("max depth exceeded"))
+ }
+ m2 := make(Params)
+ for k, v := range m {
+ switch vv := v.(type) {
+ case Params:
+ m2[k] = cloneParamsDeep(vv, depth+1)
+ default:
+ m2[k] = v
+ }
+ }
+ return m2
+}
+
// PrepareParamsClone is like PrepareParams, but it does not modify the input.
func PrepareParamsClone(m Params) Params {
m2 := make(Params)
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/resources/page/pagemeta"
"github.com/spf13/afero"
-
- xmaps "maps"
)
// InternalConfig is the internal configuration for Hugo, not read from any user provided config file.
// baseURL configure don the language level is a multihost setup.
isMultihost = true
}
+
+ if p, ok := vv.(maps.Params); ok {
+ // With the introduction of YAML anchor and alias support, language config entries
+ // may be contain shared references.
+ // This also break potential cycles.
+ vv = maps.CloneParamsDeep(p)
+ }
+
mergedConfig.Set(kk, vv)
rootv := cfg.Get(kk)
if rootv != nil && cfg.IsSet(kk) {
differentRootKeys = append(differentRootKeys, kk)
// Use the language value as base.
- mergedConfigEntry := xmaps.Clone(vvv)
+ // Note that this is already cloned above.
+ mergedConfigEntry := vvv
// Merge in the root value.
maps.MergeParams(mergedConfigEntry, rootv.(maps.Params))
}
func TestConfigYAMLAnchorsCyclicReference(t *testing.T) {
- t.Skip("Skip flaky test for now, will be fixed in issue 14072.")
t.Parallel()
files := `
`
- for range 3 {
+ for range 4 {
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]]|")