From 3458e6b20a11a31771b5f44004ca556ff3b59255 Mon Sep 17 00:00:00 2001 From: Un Known Date: Tue, 30 May 2023 19:51:04 -0400 Subject: [PATCH] fix(toml): various edge case fixes for `toml.stringify` (#3403) --- toml/stringify.ts | 16 +++++++++++----- toml/test.ts | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/toml/stringify.ts b/toml/stringify.ts index a94b5733d678..ea8469a6c9b2 100644 --- a/toml/stringify.ts +++ b/toml/stringify.ts @@ -8,7 +8,9 @@ function joinKeys(keys: string[]): string { // This allows for grouping similar properties together: return keys .map((str: string): string => { - return str.match(/[^A-Za-z0-9_-]/) ? JSON.stringify(str) : str; + return str.length === 0 || str.match(/[^A-Za-z0-9_-]/) + ? JSON.stringify(str) + : str; }) .join("."); } @@ -145,8 +147,9 @@ class Dumper { throw new Error("should never reach"); } const str = Object.keys(value).map((key) => { - // deno-lint-ignore no-explicit-any - return `${key} = ${this.#printAsInlineValue((value as any)[key])}`; + return `${joinKeys([key])} = ${ + // deno-lint-ignore no-explicit-any + this.#printAsInlineValue((value as any)[key])}`; }).join(","); return `{${str}}`; } @@ -221,8 +224,11 @@ class Dumper { const l = this.output[i]; // we keep empty entry for array of objects if (l[0] === "[" && l[1] !== "[") { - // empty object - if (this.output[i + 1] === "") { + // non-empty object with only subobjects as properties + if ( + this.output[i + 1] === "" && + this.output[i + 2]?.slice(0, l.length) === l.slice(0, -1) + "." + ) { i += 1; continue; } diff --git a/toml/test.ts b/toml/test.ts index 22ebc46fd9fe..1d24b9e64b9a 100644 --- a/toml/test.ts +++ b/toml/test.ts @@ -711,3 +711,49 @@ aaaaa = 1 assertEquals(actual, expected); }, }); + +Deno.test({ + name: "[TOML] stringify empty key", + fn() { + const src = { + "": "a", + "b": { "": "c" }, + }; + const actual = stringify(src); + const expected = `"" = "a" + +[b] +"" = "c" +`; + assertEquals(actual, expected); + }, +}); + +Deno.test({ + name: "[TOML] stringify empty object", + fn() { + const src = { + "a": {}, + "b": { "c": {} }, + }; + const actual = stringify(src); + const expected = ` +[a] + +[b.c] +`; + assertEquals(actual, expected); + }, +}); + +Deno.test({ + name: "[TOML] stringify special keys in inline object", + fn() { + const src = { + "a": [{ "/": "b" }, "c"], + }; + const actual = stringify(src); + const expected = 'a = [{"/" = "b"},"c"]\n'; + assertEquals(actual, expected); + }, +});