Skip to content

Commit ecb0fa6

Browse files
committed
Replace to gopkg.in/yaml with github.com/goccy/go-yaml (note)
Fixes gohugoio#8822 Fixes gohugoio#13043
1 parent 5e3133a commit ecb0fa6

18 files changed

+155
-78
lines changed

commands/gen.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/alecthomas/chroma/v2/formatters/html"
2828
"github.com/alecthomas/chroma/v2/styles"
2929
"github.com/bep/simplecobra"
30+
"github.com/goccy/go-yaml"
3031
"github.com/gohugoio/hugo/common/hugo"
3132
"github.com/gohugoio/hugo/docshelper"
3233
"github.com/gohugoio/hugo/helpers"
@@ -35,7 +36,6 @@ import (
3536
"github.com/gohugoio/hugo/parser"
3637
"github.com/spf13/cobra"
3738
"github.com/spf13/cobra/doc"
38-
"gopkg.in/yaml.v2"
3939
)
4040

4141
func newGenCommand() *genCommand {

commands/server.go

-1
Original file line numberDiff line numberDiff line change
@@ -1160,7 +1160,6 @@ func chmodFilter(dst, src os.FileInfo) bool {
11601160
}
11611161

11621162
func cleanErrorLog(content string) string {
1163-
content = strings.ReplaceAll(content, "\n", " ")
11641163
content = logReplacer.Replace(content)
11651164
content = logDuplicateTemplateExecuteRe.ReplaceAllString(content, "")
11661165
content = logDuplicateTemplateParseRe.ReplaceAllString(content, "")

common/herrors/file_error.go

+9-10
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,11 @@ func (fe *fileError) UpdateContent(r io.Reader, linematcher LineMatcherFn) FileE
112112

113113
fe.errorContext = ectx
114114

115-
if ectx.Position.LineNumber > 0 {
115+
if ectx.Position.LineNumber > 0 && ectx.Position.LineNumber > fe.position.LineNumber {
116116
fe.position.LineNumber = ectx.Position.LineNumber
117117
}
118118

119-
if ectx.Position.ColumnNumber > 0 {
119+
if ectx.Position.ColumnNumber > 0 && ectx.Position.ColumnNumber > fe.position.ColumnNumber {
120120
fe.position.ColumnNumber = ectx.Position.ColumnNumber
121121
}
122122

@@ -181,6 +181,7 @@ func NewFileErrorFromName(err error, name string) FileError {
181181
// Filetype is used to determine the Chroma lexer to use.
182182
fileType, pos := extractFileTypePos(err)
183183
pos.Filename = name
184+
184185
if fileType == "" {
185186
_, fileType = paths.FileAndExtNoDelimiter(filepath.Clean(name))
186187
}
@@ -238,7 +239,9 @@ func NewFileErrorFromFile(err error, filename string, fs afero.Fs, linematcher L
238239
return NewFileErrorFromName(err, realFilename)
239240
}
240241
defer f.Close()
241-
return NewFileErrorFromName(err, realFilename).UpdateContent(f, linematcher)
242+
fe := NewFileErrorFromName(err, realFilename)
243+
fe = fe.UpdateContent(f, linematcher)
244+
return fe
242245
}
243246

244247
func openFile(filename string, fs afero.Fs) (afero.File, string, error) {
@@ -306,13 +309,9 @@ func extractFileTypePos(err error) (string, text.Position) {
306309
}
307310

308311
// Look in the error message for the line number.
309-
for _, handle := range lineNumberExtractors {
310-
lno, col := handle(err)
311-
if lno > 0 {
312-
pos.ColumnNumber = col
313-
pos.LineNumber = lno
314-
break
315-
}
312+
if lno, col := commonLineNumberExtractor(err); lno > 0 {
313+
pos.ColumnNumber = col
314+
pos.LineNumber = lno
316315
}
317316

318317
if fileType == "" && pos.Filename != "" {

common/herrors/line_number_extractors.go

+13-3
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,27 @@ import (
1919
)
2020

2121
var lineNumberExtractors = []lineNumberExtractor{
22+
// YAML parse errors.
23+
newLineNumberErrHandlerFromRegexp(`\[(\d+):(\d+)\]`),
24+
2225
// Template/shortcode parse errors
2326
newLineNumberErrHandlerFromRegexp(`:(\d+):(\d*):`),
2427
newLineNumberErrHandlerFromRegexp(`:(\d+):`),
2528

26-
// YAML parse errors
27-
newLineNumberErrHandlerFromRegexp(`line (\d+):`),
28-
2929
// i18n bundle errors
3030
newLineNumberErrHandlerFromRegexp(`\((\d+),\s(\d*)`),
3131
}
3232

33+
func commonLineNumberExtractor(e error) (int, int) {
34+
for _, handler := range lineNumberExtractors {
35+
lno, col := handler(e)
36+
if lno > 0 {
37+
return lno, col
38+
}
39+
}
40+
return 0, 0
41+
}
42+
3343
type lineNumberExtractor func(e error) (int, int)
3444

3545
func newLineNumberErrHandlerFromRegexp(expression string) lineNumberExtractor {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2024 The Hugo Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package herrors
15+
16+
import (
17+
"errors"
18+
"testing"
19+
20+
qt "github.com/frankban/quicktest"
21+
)
22+
23+
func TestCommonLineNumberExtractor(t *testing.T) {
24+
t.Parallel()
25+
26+
c := qt.New(t)
27+
28+
lno, col := commonLineNumberExtractor(errors.New("[4:9] value is not allowed in this context"))
29+
c.Assert(lno, qt.Equals, 4)
30+
c.Assert(col, qt.Equals, 9)
31+
}

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ require (
3636
github.com/ghodss/yaml v1.0.0
3737
github.com/gobuffalo/flect v1.0.3
3838
github.com/gobwas/glob v0.2.3
39+
github.com/goccy/go-yaml v1.14.0
3940
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e
4041
github.com/gohugoio/hashstructure v0.1.0
4142
github.com/gohugoio/httpcache v0.7.0
@@ -83,7 +84,6 @@ require (
8384
golang.org/x/text v0.19.0
8485
golang.org/x/tools v0.26.0
8586
google.golang.org/api v0.191.0
86-
gopkg.in/yaml.v2 v2.4.0
8787
)
8888

8989
require (
@@ -164,6 +164,7 @@ require (
164164
google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988 // indirect
165165
google.golang.org/grpc v1.65.0 // indirect
166166
google.golang.org/protobuf v1.34.2 // indirect
167+
gopkg.in/yaml.v2 v2.4.0 // indirect
167168
gopkg.in/yaml.v3 v3.0.1 // indirect
168169
howett.net/plist v1.0.0 // indirect
169170
software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4
227227
github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
228228
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
229229
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
230+
github.com/goccy/go-yaml v1.14.0 h1:G/NDXJvf1CX0FshjxKn2AOL0MnrxsSJNpY9FpvMRblw=
231+
github.com/goccy/go-yaml v1.14.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
230232
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY=
231233
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e/go.mod h1:3Ltoo9Banwq0gOtcOwxuHG6omk+AwsQPADyw2vQYOJQ=
232234
github.com/gohugoio/hashstructure v0.1.0 h1:kBSTMLMyTXbrJVAxaKI+wv30MMJJxn9Q8kfQtJaZ400=

hugolib/frontmatter_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Strings: {{ printf "%T" .Params.strings }} {{ range .Params.strings }}Strings: {
4040

4141
b.Build()
4242

43-
b.AssertFileContent("public/post/one/index.html", "Ints: []interface {} Int: 1 (int)|Int: 2 (int)|Int: 3 (int)|")
44-
b.AssertFileContent("public/post/one/index.html", "Mixed: []interface {} Mixed: 1 (string)|Mixed: 2 (int)|Mixed: 3 (int)|")
43+
b.AssertFileContent("public/post/one/index.html", "Ints: []interface {} Int: 1 (uint64)|Int: 2 (uint64)|Int: 3 (uint64)|")
44+
b.AssertFileContent("public/post/one/index.html", "Mixed: []interface {} Mixed: 1 (string)|Mixed: 2 (uint64)|Mixed: 3 (uint64)|")
4545
b.AssertFileContent("public/post/one/index.html", "Strings: []string Strings: 1 (string)|Strings: 2 (string)|Strings: 3 (string)|")
4646
}

hugolib/hugo_sites_build_errors_test.go

+33-1
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ line 5
476476
errors := herrors.UnwrapFileErrorsWithErrorContext(err)
477477

478478
b.Assert(errors, qt.HasLen, 3)
479-
b.Assert(errors[0].Error(), qt.Contains, filepath.FromSlash(`"/content/_index.md:1:1": "/layouts/_default/_markup/render-heading.html:2:5": execute of template failed`))
479+
b.Assert(errors[0].Error(), qt.Contains, filepath.FromSlash(`"/content/_index.md:2:5": "/layouts/_default/_markup/render-heading.html:2:5": execute of template failed`))
480480
}
481481

482482
func TestErrorRenderHookCodeblock(t *testing.T) {
@@ -645,3 +645,35 @@ Home.
645645
b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`/layouts/index.html:2:3`))
646646
b.Assert(err.Error(), qt.Contains, `can't evaluate field ThisDoesNotExist`)
647647
}
648+
649+
func TestErrorFrontmatterYAMLSyntax(t *testing.T) {
650+
t.Parallel()
651+
652+
files := `
653+
-- hugo.toml --
654+
-- content/_index.md --
655+
656+
657+
658+
659+
660+
---
661+
line1: 'value1'
662+
x
663+
line2: 'value2'
664+
line3: 'value3'
665+
---
666+
`
667+
668+
b, err := TestE(t, files)
669+
670+
b.Assert(err, qt.Not(qt.IsNil))
671+
b.Assert(err.Error(), qt.Contains, "> 3 |")
672+
fe := herrors.UnwrapFileError(err)
673+
b.Assert(fe, qt.Not(qt.IsNil))
674+
pos := fe.Position()
675+
b.Assert(pos.Filename, qt.Contains, filepath.FromSlash("content/_index.md"))
676+
b.Assert(fe.ErrorContext(), qt.Not(qt.IsNil))
677+
b.Assert(pos.LineNumber, qt.Equals, 9)
678+
b.Assert(pos.ColumnNumber, qt.Equals, 1)
679+
}

hugolib/page__content.go

+11-14
Original file line numberDiff line numberDiff line change
@@ -283,23 +283,20 @@ func (c *contentParseInfo) parseFrontMatter(it pageparser.Item, iter *pageparser
283283
var err error
284284
c.frontMatter, err = metadecoders.Default.UnmarshalToMap(it.Val(source), f)
285285
if err != nil {
286-
if fe, ok := err.(herrors.FileError); ok {
287-
pos := fe.Position()
286+
fe := herrors.UnwrapFileError(err)
287+
if fe == nil {
288+
fe = herrors.NewFileError(err)
289+
}
290+
pos := fe.Position()
288291

289-
// Offset the starting position of front matter.
290-
offset := iter.LineNumber(source) - 1
291-
if f == metadecoders.YAML {
292-
offset -= 1
293-
}
294-
pos.LineNumber += offset
292+
// Offset the starting position of front matter.
293+
offset := iter.LineNumber(source) - 1
295294

296-
fe.UpdatePosition(pos)
297-
fe.SetFilename("") // It will be set later.
295+
pos.LineNumber += offset
298296

299-
return fe
300-
} else {
301-
return err
302-
}
297+
fe.UpdatePosition(pos)
298+
fe.SetFilename("") // It will be set later.
299+
return fe
303300
}
304301

305302
return nil

hugolib/pages_capture.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func (c *pagesCollector) Collect() (collectErr error) {
123123
Handle: func(ctx context.Context, fi hugofs.FileMetaInfo) error {
124124
numPages, numResources, err := c.m.AddFi(fi, c.buildConfig)
125125
if err != nil {
126-
return hugofs.AddFileInfoToError(err, fi, c.fs)
126+
return hugofs.AddFileInfoToError(err, fi, c.h.SourceFs)
127127
}
128128
numFilesProcessedTotal.Add(1)
129129
numPagesProcessedTotal.Add(numPages)

langs/i18n/translationProvider.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ import (
2020

2121
"github.com/gohugoio/hugo/common/paths"
2222

23+
yaml "github.com/goccy/go-yaml"
2324
"github.com/gohugoio/hugo/common/herrors"
2425
"golang.org/x/text/language"
25-
yaml "gopkg.in/yaml.v2"
2626

2727
"github.com/gohugoio/go-i18n/v2/i18n"
2828
"github.com/gohugoio/hugo/helpers"

parser/frontmatter.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ import (
2222

2323
toml "github.com/pelletier/go-toml/v2"
2424

25-
yaml "gopkg.in/yaml.v2"
26-
2725
xml "github.com/clbanning/mxj/v2"
2826
)
2927

@@ -39,7 +37,7 @@ func InterfaceToConfig(in any, format metadecoders.Format, w io.Writer) error {
3937

4038
switch format {
4139
case metadecoders.YAML:
42-
b, err := yaml.Marshal(in)
40+
b, err := metadecoders.MarshalYAML(in)
4341
if err != nil {
4442
return err
4543
}

parser/metadecoders/decoder.go

+3-31
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2018 The Hugo Authors. All rights reserved.
1+
// Copyright 2024 The Hugo Authors. All rights reserved.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -28,10 +28,10 @@ import (
2828
"github.com/niklasfasching/go-org/org"
2929

3030
xml "github.com/clbanning/mxj/v2"
31+
yaml "github.com/goccy/go-yaml"
3132
toml "github.com/pelletier/go-toml/v2"
3233
"github.com/spf13/afero"
3334
"github.com/spf13/cast"
34-
yaml "gopkg.in/yaml.v2"
3535
)
3636

3737
// Decoder provides some configuration options for the decoders.
@@ -164,35 +164,7 @@ func (d Decoder) UnmarshalTo(data []byte, f Format, v any) error {
164164
case TOML:
165165
err = toml.Unmarshal(data, v)
166166
case YAML:
167-
err = yaml.Unmarshal(data, v)
168-
if err != nil {
169-
return toFileError(f, data, fmt.Errorf("failed to unmarshal YAML: %w", err))
170-
}
171-
172-
// To support boolean keys, the YAML package unmarshals maps to
173-
// map[interface{}]interface{}. Here we recurse through the result
174-
// and change all maps to map[string]interface{} like we would've
175-
// gotten from `json`.
176-
var ptr any
177-
switch vv := v.(type) {
178-
case *map[string]any:
179-
ptr = *vv
180-
case *any:
181-
ptr = *vv
182-
default:
183-
// Not a map.
184-
}
185-
186-
if ptr != nil {
187-
if mm, changed := stringifyMapKeys(ptr); changed {
188-
switch vv := v.(type) {
189-
case *map[string]any:
190-
*vv = mm.(map[string]any)
191-
case *any:
192-
*vv = mm
193-
}
194-
}
195-
}
167+
return yaml.UnmarshalWithOptions(data, v)
196168
case CSV:
197169
return d.unmarshalCSV(data, v)
198170

parser/metadecoders/decoder_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ func TestUnmarshalToMap(t *testing.T) {
9191
{`a = "b"`, TOML, expect},
9292
{`a: "b"`, YAML, expect},
9393
// Make sure we get all string keys, even for YAML
94-
{"a: Easy!\nb:\n c: 2\n d: [3, 4]", YAML, map[string]any{"a": "Easy!", "b": map[string]any{"c": 2, "d": []any{3, 4}}}},
95-
{"a:\n true: 1\n false: 2", YAML, map[string]any{"a": map[string]any{"true": 1, "false": 2}}},
94+
{"a: Easy!\nb:\n c: 2\n d: [3, 4]", YAML, map[string]any{"a": "Easy!", "b": map[string]any{"c": uint64(2), "d": []any{uint64(3), uint64(4)}}}},
95+
{"a:\n true: 1\n false: 2", YAML, map[string]any{"a": map[string]any{"true": uint64(1), "false": uint64(2)}}},
9696
{`{ "a": "b" }`, JSON, expect},
9797
{`<root><a>b</a></root>`, XML, expect},
9898
{`#+a: b`, ORG, expect},
@@ -137,7 +137,7 @@ func TestUnmarshalToInterface(t *testing.T) {
137137
{[]byte(`a: "b"`), YAML, expect},
138138
{[]byte(`<root><a>b</a></root>`), XML, expect},
139139
{[]byte(`a,b,c`), CSV, [][]string{{"a", "b", "c"}}},
140-
{[]byte("a: Easy!\nb:\n c: 2\n d: [3, 4]"), YAML, map[string]any{"a": "Easy!", "b": map[string]any{"c": 2, "d": []any{3, 4}}}},
140+
{[]byte("a: Easy!\nb:\n c: 2\n d: [3, 4]"), YAML, map[string]any{"a": "Easy!", "b": map[string]any{"c": uint64(2), "d": []any{uint64(3), uint64(4)}}}},
141141
// errors
142142
{[]byte(`a = "`), TOML, false},
143143
} {
@@ -170,7 +170,7 @@ func TestUnmarshalStringTo(t *testing.T) {
170170
{"32", int64(1234), int64(32)},
171171
{"32", int(1234), int(32)},
172172
{"3.14159", float64(1), float64(3.14159)},
173-
{"[3,7,9]", []any{}, []any{3, 7, 9}},
173+
{"[3,7,9]", []any{}, []any{uint64(3), uint64(7), uint64(9)}},
174174
{"[3.1,7.2,9.3]", []any{}, []any{3.1, 7.2, 9.3}},
175175
} {
176176
msg := qt.Commentf("%d: %T", i, test.to)

0 commit comments

Comments
 (0)