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
18 changes: 16 additions & 2 deletions toml/stringify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,19 @@ class Dumper {
}
#printAsInlineValue(value: unknown): string | number {
if (value instanceof Date) {
return `"${this.#printDate(value)}"`;
// TOML datetime literals are unquoted (e.g. 1979-05-27T07:32:00Z).
// The previous wrapping in quotes caused the value to round-trip as a
// string instead of a Date (#7162).
return this.#printDate(value);
} else if (typeof value === "string" || value instanceof RegExp) {
return JSON.stringify(value.toString());
} else if (typeof value === "number") {
// TOML uses inf / -inf / nan as the keywords for non-finite numbers
// (#7162). Letting String(Infinity) / String(NaN) leak produces
// tokens that don't reparse.
if (Number.isNaN(value)) return "nan";
if (value === Infinity) return "inf";
if (value === -Infinity) return "-inf";
return value;
} else if (typeof value === "boolean") {
return value.toString();
Expand Down Expand Up @@ -185,7 +194,12 @@ class Dumper {
return `${title} = `;
}
#arrayDeclaration(keys: string[], value: unknown[]): string {
return `${this.#declaration(keys)}${JSON.stringify(value)}`;
// JSON.stringify(value) turned Infinity / -Infinity / NaN into `null` and
// wrapped Date values in extra quotes, producing arrays that round-trip
// incorrectly (#7162). Use the per-element inline formatter instead so
// primitive arrays use the same TOML literal forms as mixed-type arrays.
const inline = value.map((x) => this.#printAsInlineValue(x)).join(",");
return `${this.#declaration(keys)}[${inline}]`;
}
#strDeclaration(keys: string[], value: string): string {
return `${this.#declaration(keys)}${JSON.stringify(value)}`;
Expand Down
47 changes: 46 additions & 1 deletion toml/stringify_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,12 @@ Deno.test({
},
},
};
// TOML datetime literals are unquoted (see #7162). The previous
// `"2022-05-13T00:00:00.000"` form round-tripped as a string.
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}]]

Expand Down Expand Up @@ -275,3 +277,46 @@ Deno.test({
assertEquals(actual, expected);
},
});

// https://github.com/denoland/std/issues/7162 — inline array stringification
// used JSON.stringify, which turns Infinity/-Infinity/NaN into null and
// wraps Date values in quotes. Both forms broke round-trip through parse().
Deno.test({
name: "stringify() emits inf/-inf/nan in primitive arrays (#7162)",
fn() {
assertEquals(
stringify({ x: [Infinity, -Infinity, NaN] }),
"x = [inf,-inf,nan]\n",
);
},
});

Deno.test({
name: "stringify() emits inf/-inf/nan in mixed arrays (#7162)",
fn() {
assertEquals(
stringify({ x: [Infinity, -Infinity, NaN, {}] }),
"x = [inf,-inf,nan,{}]\n",
);
},
});

Deno.test({
name: "stringify() does not quote Date values in primitive arrays (#7162)",
fn() {
assertEquals(
stringify({ x: [new Date(0)] }),
"x = [1970-01-01T00:00:00.000]\n",
);
},
});

Deno.test({
name: "stringify() does not quote Date values in mixed arrays (#7162)",
fn() {
assertEquals(
stringify({ x: [new Date(0), {}] }),
"x = [1970-01-01T00:00:00.000,{}]\n",
);
},
});
Loading