fix(engine): introduce toPrettyRawJson instead of overriding toPrettyJson

Per maintainer review, do not override Sprig's toPrettyJson/mustToPrettyJson
(which HTML-escape &, <, >), since existing charts may rely on the escaped
output. Instead add new toPrettyRawJson/mustToPrettyRawJson functions that
produce indented JSON with HTML characters unescaped — the indented
counterpart to Sprig's toRawJson — preserving backwards compatibility.

Add a regression test asserting toPrettyJson still escapes HTML.

Co-Authored-By: claude-flow <ruv@ruv.net>
Signed-off-by: piotr.laczykowski <piotr.laczykowski@example.com>
pull/31964/head
piotr.laczykowski 4 weeks ago
parent 5203982eea
commit e98802f10e

@ -63,12 +63,12 @@ func funcMap() template.FuncMap {
"toYamlPretty": toYAMLPretty,
"fromYaml": fromYAML,
"fromYamlArray": fromYAMLArray,
"toJson": toJSON,
"mustToJson": mustToJSON,
"toPrettyJson": toPrettyJSON,
"mustToPrettyJson": mustToPrettyJSON,
"fromJson": fromJSON,
"fromJsonArray": fromJSONArray,
"toJson": toJSON,
"mustToJson": mustToJSON,
"toPrettyRawJson": toPrettyRawJSON,
"mustToPrettyRawJson": mustToPrettyRawJSON,
"fromJson": fromJSON,
"fromJsonArray": fromJSONArray,
// Duration helpers
"mustToDuration": mustToDuration,
@ -241,10 +241,11 @@ func mustToJSON(v any) string {
return string(data)
}
// encodePrettyJSON encodes v as indented JSON without HTML-escaping special
// characters (&, <, >). It uses two-space indentation to match the behavior
// of Sprig's toPrettyJson.
func encodePrettyJSON(v any) (string, error) {
// encodePrettyRawJSON encodes v as indented JSON without HTML-escaping special
// characters (&, <, >). It uses two-space indentation to match the indentation
// of Sprig's toPrettyJson, while leaving HTML characters unescaped like Sprig's
// toRawJson.
func encodePrettyRawJSON(v any) (string, error) {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
enc.SetEscapeHTML(false)
@ -255,13 +256,17 @@ func encodePrettyJSON(v any) (string, error) {
return strings.TrimSuffix(buf.String(), "\n"), nil
}
// toPrettyJSON takes an interface, marshals it to indented JSON without
// toPrettyRawJSON takes an interface, marshals it to indented JSON without
// HTML-escaping special characters (&, <, >), and returns a string. It will
// always return a string, even on marshal error (empty string).
//
// Unlike Sprig's toPrettyJson, HTML characters are not escaped. This is the
// indented counterpart to Sprig's toRawJson. The escaping behavior of
// toPrettyJson is intentionally left unchanged for backwards compatibility.
//
// This is designed to be called from a template.
func toPrettyJSON(v any) string {
s, err := encodePrettyJSON(v)
func toPrettyRawJSON(v any) string {
s, err := encodePrettyRawJSON(v)
if err != nil {
// Swallow errors inside of a template.
return ""
@ -269,14 +274,17 @@ func toPrettyJSON(v any) string {
return s
}
// mustToPrettyJSON takes an interface, marshals it to indented JSON without
// mustToPrettyRawJSON takes an interface, marshals it to indented JSON without
// HTML-escaping special characters (&, <, >), and returns a string.
// It will panic if there is an error.
//
// Unlike Sprig's mustToPrettyJson, HTML characters are not escaped. This is the
// indented counterpart to Sprig's mustToRawJson.
//
// This is designed to be called from a template when you need to ensure that
// the output JSON is valid.
func mustToPrettyJSON(v any) string {
s, err := encodePrettyJSON(v)
func mustToPrettyRawJSON(v any) string {
s, err := encodePrettyRawJSON(v)
if err != nil {
panic(err)
}

@ -73,14 +73,20 @@ keyInElement1 = "valueInElement1"`,
expect: `{"foo":"bar"}`,
vars: map[string]any{"foo": "bar"},
}, {
// toPrettyJson must not HTML-escape &, <, > and must use 2-space indent
tpl: "{{ toPrettyJson . }}",
// toPrettyRawJson must not HTML-escape &, <, > and must use 2-space indent
tpl: "{{ toPrettyRawJson . }}",
expect: "{\n \"url\": \"https://example.com?a=1&b=2<>\"\n}",
vars: map[string]any{"url": "https://example.com?a=1&b=2<>"},
}, {
tpl: "{{ toPrettyJson . }}",
tpl: "{{ toPrettyRawJson . }}",
expect: "{\n \"foo\": \"bar\"\n}",
vars: map[string]any{"foo": "bar"},
}, {
// toPrettyJson (from Sprig) must keep HTML-escaping &, <, > for
// backwards compatibility.
tpl: "{{ toPrettyJson . }}",
expect: "{\n \"url\": \"https://example.com?a=1\\u0026b=2\\u003c\\u003e\"\n}",
vars: map[string]any{"url": "https://example.com?a=1&b=2<>"},
}, {
tpl: `{{ fromYaml . }}`,
expect: "map[hello:world]",
@ -164,14 +170,14 @@ keyInElement1 = "valueInElement1"`,
tpl: `{{ mustToJson . }}`,
vars: loopMap,
}, {
tpl: `{{ mustToPrettyJson . }}`,
tpl: `{{ mustToPrettyRawJson . }}`,
vars: loopMap, // circular reference must panic
}, {
tpl: `{{ mustToPrettyJson . }}`,
tpl: `{{ mustToPrettyRawJson . }}`,
expect: "{\n \"foo\": \"bar\"\n}",
vars: map[string]any{"foo": "bar"},
}, {
tpl: `{{ toPrettyJson . }}`,
tpl: `{{ toPrettyRawJson . }}`,
expect: "", // circular reference must swallow error and return ""
vars: loopMap,
}, {

Loading…
Cancel
Save