diff --git a/toml/stringify.ts b/toml/stringify.ts index 05a53e8e3d5e..c9ebf549a36c 100644 --- a/toml/stringify.ts +++ b/toml/stringify.ts @@ -132,24 +132,28 @@ class Dumper { } return onlyPrimitive ? "ONLY_PRIMITIVE" : "ONLY_OBJECT_EXCLUDING_ARRAY"; } - #printAsInlineValue(value: unknown): string | number { + #printPrimitive(value: unknown): string { if (value instanceof Date) { - return `"${this.#printDate(value)}"`; + return this.#printDate(value); } else if (typeof value === "string" || value instanceof RegExp) { return JSON.stringify(value.toString()); } else if (typeof value === "number") { - return value; + if (Number.isNaN(value)) return "nan"; + if (value === Infinity) return "inf"; + if (value === -Infinity) return "-inf"; + return String(value); } else if (typeof value === "boolean") { return value.toString(); - } else if ( + } + throw new Error("Should never reach"); + } + #printAsInlineValue(value: unknown): string { + if ( value instanceof Array ) { const str = value.map((x) => this.#printAsInlineValue(x)).join(","); return `[${str}]`; - } else if (typeof value === "object") { - if (!value) { - throw new Error("Should never reach"); - } + } else if (value && typeof value === "object" && !(value instanceof Date)) { const str = Object.keys(value).map((key) => { return `${joinKeys([key])} = ${ // deno-lint-ignore no-explicit-any @@ -157,8 +161,7 @@ class Dumper { }).join(","); return `{${str}}`; } - - throw new Error("Should never reach"); + return this.#printPrimitive(value); } #isSimplySerializable(value: unknown): boolean { return ( @@ -185,7 +188,8 @@ class Dumper { return `${title} = `; } #arrayDeclaration(keys: string[], value: unknown[]): string { - return `${this.#declaration(keys)}${JSON.stringify(value)}`; + const items = value.map((v) => this.#printPrimitive(v)).join(","); + return `${this.#declaration(keys)}[${items}]`; } #strDeclaration(keys: string[], value: string): string { return `${this.#declaration(keys)}${JSON.stringify(value)}`; diff --git a/toml/stringify_test.ts b/toml/stringify_test.ts index 523eb45d5ec3..0d44e9071169 100644 --- a/toml/stringify_test.ts +++ b/toml/stringify_test.ts @@ -240,7 +240,7 @@ Deno.test({ const expected = `emptyArray = [] mixedArray1 = [1,{b = 2}] mixedArray2 = [{b = 2},1] -nestedArray1 = [[{b = 1,date = "2022-05-13T00:00:00.000"}]] +nestedArray1 = [[{b = 1,date = 2022-05-13T00:00:00.000}]] nestedArray2 = [[[{b = 1}]]] nestedArray3 = [[],[{b = 1}]] @@ -275,3 +275,74 @@ Deno.test({ assertEquals(actual, expected); }, }); + +// https://github.com/denoland/std/issues/7162 +Deno.test({ + name: + "stringify() emits TOML float literals for Infinity/NaN in primitive arrays", + fn() { + const actual = stringify({ x: [Infinity, -Infinity, NaN] }); + assertEquals(actual, "x = [inf,-inf,nan]\n"); + }, +}); + +// https://github.com/denoland/std/issues/7162 +Deno.test({ + name: "stringify() emits TOML datetime literal for Date in primitive arrays", + fn() { + const actual = stringify({ x: [new Date(0)] }); + assertEquals(actual, "x = [1970-01-01T00:00:00.000]\n"); + }, +}); + +// https://github.com/denoland/std/issues/7162 +Deno.test({ + name: + "stringify() emits TOML float literals for Infinity/NaN in mixed arrays", + fn() { + const actual = stringify({ x: [Infinity, -Infinity, NaN, {}] }); + assertEquals(actual, "x = [inf,-inf,nan,{}]\n"); + }, +}); + +// https://github.com/denoland/std/issues/7162 +Deno.test({ + name: "stringify() emits TOML datetime literal for Date in mixed arrays", + fn() { + const actual = stringify({ x: [new Date(0), {}] }); + assertEquals(actual, "x = [1970-01-01T00:00:00.000,{}]\n"); + }, +}); + +// https://github.com/denoland/std/issues/7162 - regression check for the new +// #printPrimitive path that replaced JSON.stringify: finite numbers in +// primitive arrays must keep their JSON representation. +Deno.test({ + name: "stringify() preserves finite numbers in primitive arrays", + fn() { + const actual = stringify({ x: [1, -1, 0, 3.14, 1e6] }); + assertEquals(actual, "x = [1,-1,0,3.14,1000000]\n"); + }, +}); + +// https://github.com/denoland/std/issues/7162 - bare numeric and Date scalar +// declarations (not array elements) must be unaffected by the array fixes. +Deno.test({ + name: "stringify() preserves scalar Infinity/NaN/Date declarations", + fn() { + const actual = stringify({ + pi: Infinity, + bad: NaN, + neg: -Infinity, + when: new Date(0), + }); + assertEquals( + actual, + `pi = inf +bad = nan +neg = -inf +when = 1970-01-01T00:00:00.000 +`, + ); + }, +});