From 62faaba1d38f89420bda8a7bc3d948593efae467 Mon Sep 17 00:00:00 2001 From: keroxp Date: Mon, 23 Sep 2019 13:15:19 +0900 Subject: [PATCH 1/3] fix: [toml] adapted to TOML's key encoding spec --- encoding/toml.ts | 66 +++++++++++++++++++++++++------------------ encoding/toml_test.ts | 22 ++++++++++++++- 2 files changed, 60 insertions(+), 28 deletions(-) diff --git a/encoding/toml.ts b/encoding/toml.ts index 228c1180b481..f92ca3c9c10c 100644 --- a/encoding/toml.ts +++ b/encoding/toml.ts @@ -387,6 +387,19 @@ class Parser { } } +// Bare keys may only contain ASCII letters, ASCII digits, underscores, and dashes (A-Za-z0-9_-). +function joinKeys(keys: string[]) { + const doEscape = (str): string => { + return shouldEscape(str) ? `"${str}"` : str; + }; + const shouldEscape = (key): boolean => { + return !!key.match(/[^A-Za-z0-9_-]/); + }; + // Dotted keys are a sequence of bare or quoted keys joined with a dot. + // This allows for grouping similar properties together: + return keys.map(doEscape).join("."); +} + class Dumper { maxPad: number = 0; srcObject: object; @@ -400,7 +413,7 @@ class Dumper { this.output = this._format(); return this.output; } - _parse(obj: Record, path: string = ""): string[] { + _parse(obj: Record, keys: string[] = []): string[] { const out = []; const props = Object.keys(obj); const propObj = props.filter((e: string): boolean => { @@ -422,17 +435,17 @@ class Dumper { const prop = k[i]; const value = obj[prop]; if (value instanceof Date) { - out.push(this._dateDeclaration(prop, value)); + out.push(this._dateDeclaration([prop], value)); } else if (typeof value === "string" || value instanceof RegExp) { - out.push(this._strDeclaration(prop, value.toString())); + out.push(this._strDeclaration([prop], value.toString())); } else if (typeof value === "number") { - out.push(this._numberDeclaration(prop, value)); + out.push(this._numberDeclaration([prop], value)); } else if ( value instanceof Array && this._isSimplySerializable(value[0]) ) { // only if primitives types in the array - out.push(this._arrayDeclaration(prop, value)); + out.push(this._arrayDeclaration([prop], value)); } else if ( value instanceof Array && !this._isSimplySerializable(value[0]) @@ -440,15 +453,15 @@ class Dumper { // array of objects for (let i = 0; i < value.length; i++) { out.push(""); - out.push(this._headerGroup(path + prop)); - out.push(...this._parse(value[i], `${path}${prop}.`)); + out.push(this._headerGroup([...keys, prop])); + out.push(...this._parse(value[i], [...keys, prop])); } } else if (typeof value === "object") { out.push(""); - out.push(this._header(path + prop)); + out.push(this._header([...keys, prop])); if (value) { const toParse = value as Record; - out.push(...this._parse(toParse, `${path}${prop}.`)); + out.push(...this._parse(toParse, [...keys, prop])); } // out.push(...this._parse(value, `${path}${prop}.`)); } @@ -465,35 +478,36 @@ class Dumper { value instanceof Array ); } - _header(title: string): string { - return `[${title}]`; + _header(keys: string[]): string { + return `[${joinKeys(keys)}]`; } - _headerGroup(title: string): string { - return `[[${title}]]`; + _headerGroup(keys: string[]): string { + return `[[${joinKeys(keys)}]]`; } - _declaration(title: string): string { + _declaration(keys: string[]): string { + const title = joinKeys(keys); if (title.length > this.maxPad) { this.maxPad = title.length; } return `${title} = `; } - _arrayDeclaration(title: string, value: unknown[]): string { - return `${this._declaration(title)}${JSON.stringify(value)}`; + _arrayDeclaration(keys: string[], value: unknown[]): string { + return `${this._declaration(keys)}${JSON.stringify(value)}`; } - _strDeclaration(title: string, value: string): string { - return `${this._declaration(title)}"${value}"`; + _strDeclaration(keys: string[], value: string): string { + return `${this._declaration(keys)}"${value}"`; } - _numberDeclaration(title: string, value: number): string { + _numberDeclaration(keys: string[], value: number): string { switch (value) { case Infinity: - return `${this._declaration(title)}inf`; + return `${this._declaration(keys)}inf`; case -Infinity: - return `${this._declaration(title)}-inf`; + return `${this._declaration(keys)}-inf`; default: - return `${this._declaration(title)}${value}`; + return `${this._declaration(keys)}${value}`; } } - _dateDeclaration(title: string, value: Date): string { + _dateDeclaration(keys: string[], value: Date): string { function dtPad(v: string, lPad: number = 2): string { return pad(v, lPad, { char: "0" }); } @@ -505,7 +519,7 @@ class Dumper { const ms = dtPad(value.getUTCMilliseconds().toString(), 3); // formated date const fData = `${value.getUTCFullYear()}-${m}-${d}T${h}:${min}:${s}.${ms}`; - return `${this._declaration(title)}${fData}`; + return `${this._declaration(keys)}${fData}`; } _format(): string[] { const rDeclaration = /(.*)\s=/; @@ -542,9 +556,7 @@ class Dumper { } export function stringify(srcObj: object): string { - let out: string[] = []; - out = new Dumper(srcObj).dump(); - return out.join("\n"); + return new Dumper(srcObj).dump().join("\n"); } export function parse(tomlString: string): object { diff --git a/encoding/toml_test.ts b/encoding/toml_test.ts index 28a6204533cb..22ecfa68a93e 100644 --- a/encoding/toml_test.ts +++ b/encoding/toml_test.ts @@ -1,5 +1,5 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test } from "../testing/mod.ts"; +import { runIfMain, test } from "../testing/mod.ts"; import { assertEquals } from "../testing/asserts.ts"; import { existsSync } from "../fs/exists.ts"; import { readFileStrSync } from "../fs/read_file_str.ts"; @@ -301,6 +301,17 @@ test({ const src = { foo: { bar: "deno" }, this: { is: { nested: "denonono" } }, + "https://deno.land/std": { + $: "doller" + }, + "##": { + deno: { + "https://deno.land": { + proto: "https", + ":80": "port" + } + } + }, arrayObjects: [{ stuff: "in" }, {}, { the: "array" }], deno: "is", not: "[node]", @@ -376,6 +387,13 @@ bar = "deno" [this.is] nested = "denonono" +["https://deno.land/std"] +"$" = "doller" + +["##".deno."https://deno.land"] +proto = "https" +":80" = "port" + [[arrayObjects]] stuff = "in" @@ -388,3 +406,5 @@ the = "array" assertEquals(actual, expected); } }); + +runIfMain(import.meta); From 7ac17a7a59236567e8fffe7fa7a8410d5a1c8e70 Mon Sep 17 00:00:00 2001 From: keroxp Date: Mon, 23 Sep 2019 13:35:18 +0900 Subject: [PATCH 2/3] mt --- encoding/toml.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/encoding/toml.ts b/encoding/toml.ts index f92ca3c9c10c..ff4855587fb9 100644 --- a/encoding/toml.ts +++ b/encoding/toml.ts @@ -387,17 +387,16 @@ class Parser { } } -// Bare keys may only contain ASCII letters, ASCII digits, underscores, and dashes (A-Za-z0-9_-). -function joinKeys(keys: string[]) { - const doEscape = (str): string => { - return shouldEscape(str) ? `"${str}"` : str; - }; - const shouldEscape = (key): boolean => { - return !!key.match(/[^A-Za-z0-9_-]/); - }; +// Bare keys may only contain ASCII letters, +// ASCII digits, underscores, and dashes (A-Za-z0-9_-). +function joinKeys(keys: string[]): string { // Dotted keys are a sequence of bare or quoted keys joined with a dot. // This allows for grouping similar properties together: - return keys.map(doEscape).join("."); + return keys + .map(str => { + return str.match(/[^A-Za-z0-9_-]/) ? `"${str}"` : str; + }) + .join("."); } class Dumper { From c9a26d67ec730df814ec22ed08e890cafb00abaf Mon Sep 17 00:00:00 2001 From: keroxp Date: Mon, 23 Sep 2019 13:40:21 +0900 Subject: [PATCH 3/3] Update toml.ts --- encoding/toml.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encoding/toml.ts b/encoding/toml.ts index ff4855587fb9..2abc039309ea 100644 --- a/encoding/toml.ts +++ b/encoding/toml.ts @@ -393,7 +393,7 @@ function joinKeys(keys: string[]): string { // Dotted keys are a sequence of bare or quoted keys joined with a dot. // This allows for grouping similar properties together: return keys - .map(str => { + .map((str: string): string => { return str.match(/[^A-Za-z0-9_-]/) ? `"${str}"` : str; }) .join(".");