mirror of
https://github.com/denoland/std.git
synced 2024-11-21 20:50:22 +00:00
BREAKING(jsonc): remove allowTrailingComma
option (#5119)
This commit is contained in:
parent
9411d0957e
commit
255b7994ee
@ -14,13 +14,7 @@
|
|||||||
* import { assertEquals } from "@std/assert/assert-equals";
|
* 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 *\/'), { foo: "bar" });
|
* assertEquals(parse('{"foo": "bar", } /* comment *\/'), { foo: "bar" });
|
||||||
*
|
|
||||||
* assertEquals(
|
|
||||||
* parse('{"foo": "bar" } // comment', { allowTrailingComma: false }),
|
|
||||||
* { foo: "bar" }
|
|
||||||
* );
|
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @module
|
* @module
|
||||||
|
@ -4,15 +4,6 @@
|
|||||||
import type { JsonValue } from "@std/json/types";
|
import type { JsonValue } from "@std/json/types";
|
||||||
export 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.
|
* 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", }'), { foo: "bar" });
|
* assertEquals(parse('{"foo": "bar", }'), { foo: "bar" });
|
||||||
* assertEquals(parse('{"foo": "bar", } /* comment *\/'), { 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.
|
* @throws {SyntaxError} If the JSONC string is invalid.
|
||||||
@ -32,15 +22,11 @@ export interface ParseOptions {
|
|||||||
* @param options Options for parsing.
|
* @param options Options for parsing.
|
||||||
* @returns The parsed JsonValue from the JSONC string.
|
* @returns The parsed JsonValue from the JSONC string.
|
||||||
*/
|
*/
|
||||||
export function parse(
|
export function parse(text: string): JsonValue {
|
||||||
text: string,
|
|
||||||
options?: ParseOptions,
|
|
||||||
): JsonValue {
|
|
||||||
const { allowTrailingComma = true } = { ...options };
|
|
||||||
if (new.target) {
|
if (new.target) {
|
||||||
throw new TypeError("parse is not a constructor");
|
throw new TypeError("parse is not a constructor");
|
||||||
}
|
}
|
||||||
return new JSONCParser(text, { allowTrailingComma }).parse();
|
return new JSONCParser(text).parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenType =
|
type TokenType =
|
||||||
@ -77,12 +63,10 @@ class JSONCParser {
|
|||||||
#text: string;
|
#text: string;
|
||||||
#length: number;
|
#length: number;
|
||||||
#tokenized: Generator<Token, void>;
|
#tokenized: Generator<Token, void>;
|
||||||
#options: ParseOptions;
|
constructor(text: string) {
|
||||||
constructor(text: string, options: ParseOptions) {
|
|
||||||
this.#text = `${text}`;
|
this.#text = `${text}`;
|
||||||
this.#length = this.#text.length;
|
this.#length = this.#text.length;
|
||||||
this.#tokenized = this.#tokenize();
|
this.#tokenized = this.#tokenize();
|
||||||
this.#options = options;
|
|
||||||
}
|
}
|
||||||
parse(): JsonValue {
|
parse(): JsonValue {
|
||||||
const token = this.#getNext();
|
const token = this.#getNext();
|
||||||
@ -238,12 +222,9 @@ class JSONCParser {
|
|||||||
// │ │ │ │ │ │ ┌─────token3
|
// │ │ │ │ │ │ ┌─────token3
|
||||||
// │ │ │ │ │ │ │ ┌─token4
|
// │ │ │ │ │ │ │ ┌─token4
|
||||||
// { "key" : value , "key" : value }
|
// { "key" : value , "key" : value }
|
||||||
for (let isFirst = true;; isFirst = false) {
|
while (true) {
|
||||||
const token1 = this.#getNext();
|
const token1 = this.#getNext();
|
||||||
if (
|
if (token1.type === "EndObject") {
|
||||||
(isFirst || this.#options.allowTrailingComma) &&
|
|
||||||
token1.type === "EndObject"
|
|
||||||
) {
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
if (token1.type !== "String") {
|
if (token1.type !== "String") {
|
||||||
@ -290,12 +271,9 @@ class JSONCParser {
|
|||||||
// │ │ ┌─────token1
|
// │ │ ┌─────token1
|
||||||
// │ │ │ ┌─token2
|
// │ │ │ ┌─token2
|
||||||
// [ value , value ]
|
// [ value , value ]
|
||||||
for (let isFirst = true;; isFirst = false) {
|
while (true) {
|
||||||
const token1 = this.#getNext();
|
const token1 = this.#getNext();
|
||||||
if (
|
if (token1.type === "EndArray") {
|
||||||
(isFirst || this.#options.allowTrailingComma) &&
|
|
||||||
token1.type === "EndArray"
|
|
||||||
) {
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
target.push(this.#parseJsonValue(token1));
|
target.push(this.#parseJsonValue(token1));
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
import { parse, type ParseOptions } from "./parse.ts";
|
import { parse } from "./parse.ts";
|
||||||
import {
|
import {
|
||||||
assert,
|
assert,
|
||||||
assertEquals,
|
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.
|
// The test code for the jsonc module can also be found in the testcode directory.
|
||||||
|
|
||||||
function assertValidParse(
|
function assertValidParse(text: string, expected: unknown) {
|
||||||
text: string,
|
assertEquals(parse(text), expected);
|
||||||
expected: unknown,
|
|
||||||
options?: ParseOptions,
|
|
||||||
) {
|
|
||||||
assertEquals(parse(text, options), expected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertInvalidParse(
|
function assertInvalidParse(
|
||||||
@ -27,10 +23,9 @@ function assertInvalidParse(
|
|||||||
// deno-lint-ignore no-explicit-any
|
// deno-lint-ignore no-explicit-any
|
||||||
ErrorClass: new (...args: any[]) => Error,
|
ErrorClass: new (...args: any[]) => Error,
|
||||||
msgIncludes?: string,
|
msgIncludes?: string,
|
||||||
options?: ParseOptions,
|
|
||||||
) {
|
) {
|
||||||
assertThrows(
|
assertThrows(
|
||||||
() => parse(text, options),
|
() => parse(text),
|
||||||
ErrorClass,
|
ErrorClass,
|
||||||
msgIncludes,
|
msgIncludes,
|
||||||
);
|
);
|
||||||
@ -231,3 +226,7 @@ Deno.test({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test("parse() handles lone continuation byte in key and tailing comma", () => {
|
||||||
|
assertEquals(parse('{"<22>":"0",}'), { "<22>": "0" });
|
||||||
|
});
|
||||||
|
2
jsonc/testdata/JSONTestSuite/test.ts
vendored
2
jsonc/testdata/JSONTestSuite/test.ts
vendored
@ -34,7 +34,7 @@ for await (
|
|||||||
JSON.parse(text);
|
JSON.parse(text);
|
||||||
});
|
});
|
||||||
const [hasJsoncError, jsoncError, jsoncResult] = getError(() => {
|
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.
|
// If an error occurs in JSON.parse() but no error occurs in JSONC.parse(), or vice versa, an error is thrown.
|
||||||
|
@ -1 +0,0 @@
|
|||||||
{"ケ":"0",}
|
|
18
jsonc/testdata/node-jsonc-parser/test.ts
vendored
18
jsonc/testdata/node-jsonc-parser/test.ts
vendored
@ -11,19 +11,17 @@ import { assertEquals, assertThrows } from "../../../assert/mod.ts";
|
|||||||
function assertValidParse(
|
function assertValidParse(
|
||||||
text: string,
|
text: string,
|
||||||
expected: unknown,
|
expected: unknown,
|
||||||
options?: JSONC.ParseOptions,
|
|
||||||
) {
|
) {
|
||||||
assertEquals(JSONC.parse(text, options), expected);
|
assertEquals(JSONC.parse(text), expected);
|
||||||
}
|
}
|
||||||
function assertInvalidParse(
|
function assertInvalidParse(
|
||||||
text: string,
|
text: string,
|
||||||
// deno-lint-ignore no-explicit-any
|
// deno-lint-ignore no-explicit-any
|
||||||
ErrorClass: (new (...args: any[]) => Error),
|
ErrorClass: (new (...args: any[]) => Error),
|
||||||
msgIncludes?: string,
|
msgIncludes?: string,
|
||||||
options?: JSONC.ParseOptions,
|
|
||||||
) {
|
) {
|
||||||
assertThrows(
|
assertThrows(
|
||||||
() => JSONC.parse(text, options),
|
() => JSONC.parse(text),
|
||||||
ErrorClass,
|
ErrorClass,
|
||||||
msgIncludes,
|
msgIncludes,
|
||||||
);
|
);
|
||||||
@ -83,9 +81,6 @@ Deno.test("[jsonc] parse node-jsonc-parser:arrays", () => {
|
|||||||
|
|
||||||
Deno.test("[jsonc] parse node-jsonc-parser:objects with errors", () => {
|
Deno.test("[jsonc] parse node-jsonc-parser:objects with errors", () => {
|
||||||
assertInvalidParse("{,}", SyntaxError);
|
assertInvalidParse("{,}", SyntaxError);
|
||||||
assertInvalidParse('{ "foo": true, }', SyntaxError, undefined, {
|
|
||||||
allowTrailingComma: false,
|
|
||||||
});
|
|
||||||
assertInvalidParse('{ "bar": 8 "xoo": "foo" }', SyntaxError);
|
assertInvalidParse('{ "bar": 8 "xoo": "foo" }', SyntaxError);
|
||||||
assertInvalidParse('{ ,"bar": 8 }', SyntaxError);
|
assertInvalidParse('{ ,"bar": 8 }', SyntaxError);
|
||||||
assertInvalidParse('{ ,"bar": 8, "foo" }', 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]);
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user