Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,11 @@ func builtinParseYAML(i *interpreter, str value) (value, error) {
return nil, err
}
s := sval.getGoString()
// Strip a leading comment/blank-only prefix before the first
// explicit --- separator. Otherwise the YAML reader treats that
// prefix as a separate (empty) document and emits a stray null in
// the result array.
s = stripLeadingYAMLComments(s)

elems := []interface{}{}
d := NewYAMLToJSONDecoder(strings.NewReader(s))
Expand All @@ -1611,6 +1616,55 @@ func builtinParseYAML(i *interpreter, str value) (value, error) {
return jsonToValue(i, elems[0])
}

// stripLeadingYAMLComments removes leading lines that are blank or
// pure comments up to (and including) the first --- document marker,
// but only when no real content appears before that marker AND
// content follows the marker. This avoids emitting a stray null
// element for streams whose first "document" is just commentary
// while leaving a bare `---` (an explicit single-null document)
// alone.
func stripLeadingYAMLComments(s string) string {
idx := 0
hasComment := false
for idx < len(s) {
nl := strings.IndexByte(s[idx:], '\n')
var line string
var next int
if nl < 0 {
line = s[idx:]
next = len(s)
} else {
line = s[idx : idx+nl]
next = idx + nl + 1
}
trimmed := strings.TrimSpace(line)
switch {
case len(trimmed) == 0:
// blank, keep scanning
case strings.HasPrefix(trimmed, "#"):
hasComment = true
case trimmed == "---":
if !hasComment {
return s
}
// Only strip if real content follows; otherwise the input is
// a comment-only-then-marker stream that should still decode
// to a single null doc, matching the bare `---` case.
rest := s[next:]
for _, c := range rest {
if c != '\n' && c != '\r' && c != ' ' && c != '\t' {
return rest
}
}
return s
default:
return s
}
idx = next
}
return s
}

func jsonEncode(v interface{}) (string, error) {
buf := new(bytes.Buffer)
enc := json.NewEncoder(buf)
Expand Down
8 changes: 8 additions & 0 deletions testdata/parseYaml.golden
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,13 @@
],
[
null
],
[
{
"foo": "bar"
},
{
"baz": "cuux"
}
]
]
10 changes: 10 additions & 0 deletions testdata/parseYaml.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,15 @@
|||,

"---",

// Comment-only prefix before the first document separator
// shouldn't be exposed as a leading null.
|||
# Test
---
foo: bar
---
baz: cuux
|||,
]
]