Skip to content

Commit 3de36e2

Browse files
committed
fix(toml): handle Infinity/NaN and Date in inline arrays
Inline array stringification used JSON.stringify, which silently turned Infinity/-Infinity/NaN into 'null' and wrapped Date values in extra quotes. Both forms broke round-trip through parse(): stringify({x: [Infinity, -Infinity, NaN]}) // x = [null,null,null] stringify({x: [new Date(0)]}) // x = ["1970-01-01..."] The same flaw affected #printAsInlineValue, which was the path for mixed-type arrays — Date values were quoted and non-finite numbers fell through as String(Infinity) / String(NaN). Replace the JSON.stringify call in #arrayDeclaration with a per-element walk through #printAsInlineValue, and teach #printAsInlineValue to emit TOML's inf / -inf / nan keywords for non-finite numbers and to leave Date values unquoted to match TOML's datetime literal syntax. The 'handles mixed array' test pinned the buggy date = "2022-05-13T00:00:00.000" form inside a nested inline map. Updated to the correct unquoted form. Four new tests pin the issue's specific repros (primitive Infinity/-Infinity/NaN, primitive Date, mixed inf/nan/object, mixed date/object). Fixes #7162 (partially — the null-in-array cases are left for a follow-up since the spec disallows null in TOML and the right behavior is a separate design call).
1 parent cdf74a8 commit 3de36e2

2 files changed

Lines changed: 62 additions & 3 deletions

File tree

toml/stringify.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,19 @@ class Dumper {
134134
}
135135
#printAsInlineValue(value: unknown): string | number {
136136
if (value instanceof Date) {
137-
return `"${this.#printDate(value)}"`;
137+
// TOML datetime literals are unquoted (e.g. 1979-05-27T07:32:00Z).
138+
// The previous wrapping in quotes caused the value to round-trip as a
139+
// string instead of a Date (#7162).
140+
return this.#printDate(value);
138141
} else if (typeof value === "string" || value instanceof RegExp) {
139142
return JSON.stringify(value.toString());
140143
} else if (typeof value === "number") {
144+
// TOML uses inf / -inf / nan as the keywords for non-finite numbers
145+
// (#7162). Letting String(Infinity) / String(NaN) leak produces
146+
// tokens that don't reparse.
147+
if (Number.isNaN(value)) return "nan";
148+
if (value === Infinity) return "inf";
149+
if (value === -Infinity) return "-inf";
141150
return value;
142151
} else if (typeof value === "boolean") {
143152
return value.toString();
@@ -185,7 +194,12 @@ class Dumper {
185194
return `${title} = `;
186195
}
187196
#arrayDeclaration(keys: string[], value: unknown[]): string {
188-
return `${this.#declaration(keys)}${JSON.stringify(value)}`;
197+
// JSON.stringify(value) turned Infinity / -Infinity / NaN into `null` and
198+
// wrapped Date values in extra quotes, producing arrays that round-trip
199+
// incorrectly (#7162). Use the per-element inline formatter instead so
200+
// primitive arrays use the same TOML literal forms as mixed-type arrays.
201+
const inline = value.map((x) => this.#printAsInlineValue(x)).join(",");
202+
return `${this.#declaration(keys)}[${inline}]`;
189203
}
190204
#strDeclaration(keys: string[], value: string): string {
191205
return `${this.#declaration(keys)}${JSON.stringify(value)}`;

toml/stringify_test.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,12 @@ Deno.test({
237237
},
238238
},
239239
};
240+
// TOML datetime literals are unquoted (see #7162). The previous
241+
// `"2022-05-13T00:00:00.000"` form round-tripped as a string.
240242
const expected = `emptyArray = []
241243
mixedArray1 = [1,{b = 2}]
242244
mixedArray2 = [{b = 2},1]
243-
nestedArray1 = [[{b = 1,date = "2022-05-13T00:00:00.000"}]]
245+
nestedArray1 = [[{b = 1,date = 2022-05-13T00:00:00.000}]]
244246
nestedArray2 = [[[{b = 1}]]]
245247
nestedArray3 = [[],[{b = 1}]]
246248
@@ -275,3 +277,46 @@ Deno.test({
275277
assertEquals(actual, expected);
276278
},
277279
});
280+
281+
// https://github.com/denoland/std/issues/7162 — inline array stringification
282+
// used JSON.stringify, which turns Infinity/-Infinity/NaN into null and
283+
// wraps Date values in quotes. Both forms broke round-trip through parse().
284+
Deno.test({
285+
name: "stringify() emits inf/-inf/nan in primitive arrays (#7162)",
286+
fn() {
287+
assertEquals(
288+
stringify({ x: [Infinity, -Infinity, NaN] }),
289+
"x = [inf,-inf,nan]\n",
290+
);
291+
},
292+
});
293+
294+
Deno.test({
295+
name: "stringify() emits inf/-inf/nan in mixed arrays (#7162)",
296+
fn() {
297+
assertEquals(
298+
stringify({ x: [Infinity, -Infinity, NaN, {}] }),
299+
"x = [inf,-inf,nan,{}]\n",
300+
);
301+
},
302+
});
303+
304+
Deno.test({
305+
name: "stringify() does not quote Date values in primitive arrays (#7162)",
306+
fn() {
307+
assertEquals(
308+
stringify({ x: [new Date(0)] }),
309+
"x = [1970-01-01T00:00:00.000]\n",
310+
);
311+
},
312+
});
313+
314+
Deno.test({
315+
name: "stringify() does not quote Date values in mixed arrays (#7162)",
316+
fn() {
317+
assertEquals(
318+
stringify({ x: [new Date(0), {}] }),
319+
"x = [1970-01-01T00:00:00.000,{}]\n",
320+
);
321+
},
322+
});

0 commit comments

Comments
 (0)