From 255b7994ee20e8318016009693f0be00ab55416d Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Tue, 25 Jun 2024 16:13:08 +1000 Subject: [PATCH] BREAKING(jsonc): remove `allowTrailingComma` option (#5119) --- jsonc/mod.ts | 6 ---- jsonc/parse.ts | 36 ++++--------------- jsonc/parse_test.ts | 17 +++++---- jsonc/testdata/JSONTestSuite/test.ts | 2 +- ...uation_byte_in_key_and_trailing_comma.json | 1 - jsonc/testdata/node-jsonc-parser/test.ts | 18 ++-------- 6 files changed, 18 insertions(+), 62 deletions(-) delete mode 100644 jsonc/testdata/JSONTestSuite/test_parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json diff --git a/jsonc/mod.ts b/jsonc/mod.ts index 3b6f61402..67d0cd209 100644 --- a/jsonc/mod.ts +++ b/jsonc/mod.ts @@ -14,13 +14,7 @@ * import { assertEquals } from "@std/assert/assert-equals"; * * assertEquals(parse('{"foo": "bar", } // comment'), { foo: "bar" }); - * * assertEquals(parse('{"foo": "bar", } /* comment *\/'), { foo: "bar" }); - * - * assertEquals( - * parse('{"foo": "bar" } // comment', { allowTrailingComma: false }), - * { foo: "bar" } - * ); * ``` * * @module diff --git a/jsonc/parse.ts b/jsonc/parse.ts index 62ec631b1..dbda74b09 100644 --- a/jsonc/parse.ts +++ b/jsonc/parse.ts @@ -4,15 +4,6 @@ import type { JsonValue } from "@std/json/types"; export type { JsonValue } from "@std/json/types"; -/** Options for {@linkcode parse}. */ -export interface ParseOptions { - /** Allow trailing commas at the end of arrays and objects. - * - * @default {true} - */ - allowTrailingComma?: boolean; -} - /** * Converts a JSON with Comments (JSONC) string into an object. * @@ -24,7 +15,6 @@ export interface ParseOptions { * assertEquals(parse('{"foo": "bar"}'), { foo: "bar" }); * assertEquals(parse('{"foo": "bar", }'), { foo: "bar" }); * assertEquals(parse('{"foo": "bar", } /* comment *\/'), { foo: "bar" }); - * assertEquals(parse('{"foo": "bar" } // comment', { allowTrailingComma: false }), { foo: "bar" }); * ``` * * @throws {SyntaxError} If the JSONC string is invalid. @@ -32,15 +22,11 @@ export interface ParseOptions { * @param options Options for parsing. * @returns The parsed JsonValue from the JSONC string. */ -export function parse( - text: string, - options?: ParseOptions, -): JsonValue { - const { allowTrailingComma = true } = { ...options }; +export function parse(text: string): JsonValue { if (new.target) { throw new TypeError("parse is not a constructor"); } - return new JSONCParser(text, { allowTrailingComma }).parse(); + return new JSONCParser(text).parse(); } type TokenType = @@ -77,12 +63,10 @@ class JSONCParser { #text: string; #length: number; #tokenized: Generator; - #options: ParseOptions; - constructor(text: string, options: ParseOptions) { + constructor(text: string) { this.#text = `${text}`; this.#length = this.#text.length; this.#tokenized = this.#tokenize(); - this.#options = options; } parse(): JsonValue { const token = this.#getNext(); @@ -238,12 +222,9 @@ class JSONCParser { // │ │ │ │ │ │ ┌─────token3 // │ │ │ │ │ │ │ ┌─token4 // { "key" : value , "key" : value } - for (let isFirst = true;; isFirst = false) { + while (true) { const token1 = this.#getNext(); - if ( - (isFirst || this.#options.allowTrailingComma) && - token1.type === "EndObject" - ) { + if (token1.type === "EndObject") { return target; } if (token1.type !== "String") { @@ -290,12 +271,9 @@ class JSONCParser { // │ │ ┌─────token1 // │ │ │ ┌─token2 // [ value , value ] - for (let isFirst = true;; isFirst = false) { + while (true) { const token1 = this.#getNext(); - if ( - (isFirst || this.#options.allowTrailingComma) && - token1.type === "EndArray" - ) { + if (token1.type === "EndArray") { return target; } target.push(this.#parseJsonValue(token1)); diff --git a/jsonc/parse_test.ts b/jsonc/parse_test.ts index ca2f806e0..5f82277f1 100644 --- a/jsonc/parse_test.ts +++ b/jsonc/parse_test.ts @@ -1,6 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { parse, type ParseOptions } from "./parse.ts"; +import { parse } from "./parse.ts"; import { assert, assertEquals, @@ -14,12 +14,8 @@ import "./testdata/test262/test.ts"; // The test code for the jsonc module can also be found in the testcode directory. -function assertValidParse( - text: string, - expected: unknown, - options?: ParseOptions, -) { - assertEquals(parse(text, options), expected); +function assertValidParse(text: string, expected: unknown) { + assertEquals(parse(text), expected); } function assertInvalidParse( @@ -27,10 +23,9 @@ function assertInvalidParse( // deno-lint-ignore no-explicit-any ErrorClass: new (...args: any[]) => Error, msgIncludes?: string, - options?: ParseOptions, ) { assertThrows( - () => parse(text, options), + () => parse(text), ErrorClass, msgIncludes, ); @@ -231,3 +226,7 @@ Deno.test({ ); }, }); + +Deno.test("parse() handles lone continuation byte in key and tailing comma", () => { + assertEquals(parse('{"�":"0",}'), { "�": "0" }); +}); diff --git a/jsonc/testdata/JSONTestSuite/test.ts b/jsonc/testdata/JSONTestSuite/test.ts index ecedb9e42..828b0e4e5 100644 --- a/jsonc/testdata/JSONTestSuite/test.ts +++ b/jsonc/testdata/JSONTestSuite/test.ts @@ -34,7 +34,7 @@ for await ( JSON.parse(text); }); const [hasJsoncError, jsoncError, jsoncResult] = getError(() => { - JSONC.parse(text, { allowTrailingComma: false }); + JSONC.parse(text); }); // If an error occurs in JSON.parse() but no error occurs in JSONC.parse(), or vice versa, an error is thrown. diff --git a/jsonc/testdata/JSONTestSuite/test_parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json b/jsonc/testdata/JSONTestSuite/test_parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json deleted file mode 100644 index aa2cb637c..000000000 --- a/jsonc/testdata/JSONTestSuite/test_parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json +++ /dev/null @@ -1 +0,0 @@ -{"¹":"0",} \ No newline at end of file diff --git a/jsonc/testdata/node-jsonc-parser/test.ts b/jsonc/testdata/node-jsonc-parser/test.ts index 79eefe7d3..b8bdea1a3 100644 --- a/jsonc/testdata/node-jsonc-parser/test.ts +++ b/jsonc/testdata/node-jsonc-parser/test.ts @@ -11,19 +11,17 @@ import { assertEquals, assertThrows } from "../../../assert/mod.ts"; function assertValidParse( text: string, expected: unknown, - options?: JSONC.ParseOptions, ) { - assertEquals(JSONC.parse(text, options), expected); + assertEquals(JSONC.parse(text), expected); } function assertInvalidParse( text: string, // deno-lint-ignore no-explicit-any ErrorClass: (new (...args: any[]) => Error), msgIncludes?: string, - options?: JSONC.ParseOptions, ) { assertThrows( - () => JSONC.parse(text, options), + () => JSONC.parse(text), ErrorClass, msgIncludes, ); @@ -83,9 +81,6 @@ Deno.test("[jsonc] parse node-jsonc-parser:arrays", () => { Deno.test("[jsonc] parse node-jsonc-parser:objects with errors", () => { assertInvalidParse("{,}", SyntaxError); - assertInvalidParse('{ "foo": true, }', SyntaxError, undefined, { - allowTrailingComma: false, - }); assertInvalidParse('{ "bar": 8 "xoo": "foo" }', SyntaxError); assertInvalidParse('{ ,"bar": 8 }', SyntaxError); assertInvalidParse('{ ,"bar": 8, "foo" }', SyntaxError); @@ -119,13 +114,4 @@ Deno.test("[jsonc] parse node-jsonc-parser:trailing comma", () => { ); assertValidParse("[ 1, 2, ]", [1, 2]); assertValidParse("[ 1, 2 ]", [1, 2]); - - assertInvalidParse('{ "hello": [], }', SyntaxError, undefined, options); - assertInvalidParse( - '{ "hello": [], "world": {}, }', - SyntaxError, - undefined, - options, - ); - assertInvalidParse("[ 1, 2, ]", SyntaxError, undefined, options); });