JSON: Better error message (#10452)

This commit is contained in:
fisker Cheung authored and GitHub committed 2021-03-02 13:42:49 +08:00
1 parent 23b4822e66
commit 2edfec5eda
5 files changed
+557 -90

No files matched your search

+40 -48
View File
@@ -1,5 +1,6 @@
"use strict";
const { isNonEmptyArray } = require("../../common/util");
const createError = require("../../common/parser-create-error");
const createParser = require("./create-parser");
const createBabelParseError = require("./create-babel-parse-error");
@@ -18,11 +19,10 @@ function createJsonParse(options = {}) {
throw createBabelParseError(error);
}
if (!allowComments) {
// @ts-ignore
if (!allowComments && isNonEmptyArray(ast.comments)) {
// @ts-ignore
for (const comment of ast.comments) {
assertJsonNode(comment);
}
throw createJsonError(ast.comments[0], "Comment");
}
assertJsonNode(ast);
@@ -31,80 +31,72 @@ function createJsonParse(options = {}) {
};
}
function assertJsonNode(node, parent) {
function createJsonError(node, description) {
const [start, end] = [node.loc.start, node.loc.end].map(
({ line, column }) => ({
line,
column: column + 1,
})
);
return createError(`${description} is not allowed in JSON.`, { start, end });
}
function assertJsonNode(node) {
switch (node.type) {
case "ArrayExpression":
for (const element of node.elements) {
if (element === null) {
throw createError("Sparse array is not allowed in JSON.", {
start: {
line: node.loc.start.line,
column: node.loc.start.column + 1,
},
});
throw createJsonError(node, "Sparse array");
}
assertJsonChildNode(element);
assertJsonNode(element);
}
return;
case "ObjectExpression":
for (const property of node.properties) {
assertJsonChildNode(property);
assertJsonNode(property);
}
return;
case "ObjectProperty":
if (node.computed) {
throw createJsonError("computed");
throw createJsonError(node.key, "Computed key");
}
if (node.shorthand) {
throw createJsonError("shorthand");
throw createJsonError(node.key, "Shorthand property");
}
assertJsonChildNode(node.key);
assertJsonChildNode(node.value);
if (node.key.type !== "Identifier") {
assertJsonNode(node.key);
}
assertJsonNode(node.value);
return;
case "UnaryExpression":
switch (node.operator) {
case "+":
case "-":
return assertJsonChildNode(node.argument);
default:
throw createJsonError("operator");
}
case "Identifier":
// JSON5 https://spec.json5.org/#numbers
if (node.name === "Infinity" || node.name === "NaN") {
return;
if (node.operator !== "+" && node.operator !== "-") {
throw createJsonError(node, `Operator '${node.operator}'`);
}
if (parent && parent.type === "ObjectProperty" && parent.key === node) {
return;
assertJsonNode(node.argument);
return;
case "Identifier":
// JSON5 https://spec.json5.org/#numbers
if (node.name !== "Infinity" && node.name !== "NaN") {
throw createJsonError(node, `Identifier '${node.name}'`);
}
throw createJsonError();
return;
case "NullLiteral":
case "BooleanLiteral":
case "NumericLiteral":
case "StringLiteral":
return;
default:
throw createJsonError();
}
function assertJsonChildNode(child) {
return assertJsonNode(child, node);
}
function createJsonError(attribute) {
const { type, loc } = node;
const name = attribute
? `${type} with ${attribute}=${JSON.stringify(node[attribute])}`
: type;
const [start, end] = [loc.start, loc.end].map(({ line, column }) => ({
line,
column: column + 1,
}));
return createError(`${name} is not allowed in JSON.`, { start, end });
throw createJsonError(node, `'${node.type}'`);
}
}
@@ -1,74 +1,511 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`snippet: #0 [json] format 1`] = `
"Expecting Unicode escape sequence \\\\uXXXX (1:10)
> 1 | packages\\\\the-hub\\\\cypress\\\\fixtures\\\\gridConfiguration.json
| ^"
"Shorthand property is not allowed in JSON. (1:2)
> 1 | {foo}
| ^^^"
`;
exports[`snippet: #0 [json-stringify] format 1`] = `
"ObjectProperty with shorthand=true is not allowed in JSON. (1:2)
"Shorthand property is not allowed in JSON. (1:2)
> 1 | {foo}
| ^^^"
`;
exports[`snippet: #0 [json5] format 1`] = `
"Shorthand property is not allowed in JSON. (1:2)
> 1 | {foo}
| ^^^"
`;
exports[`snippet: #1 [json] format 1`] = `
"BinaryExpression is not allowed in JSON. (1:1)
> 1 | 1+2
| ^^^"
"Computed key is not allowed in JSON. (1:3)
> 1 | {[\\"foo\\"]:\\"bar\\"}
| ^^^^^"
`;
exports[`snippet: #1 [json-stringify] format 1`] = `
"ObjectProperty with computed=true is not allowed in JSON. (1:2)
"Computed key is not allowed in JSON. (1:3)
> 1 | {[\\"foo\\"]:\\"bar\\"}
| ^^^^^^^^^^^^^"
| ^^^^^"
`;
exports[`snippet: #1 [json5] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[\\"foo\\"]:\\"bar\\"}
| ^^^^^"
`;
exports[`snippet: #2 [json] format 1`] = `
"Sparse array is not allowed in JSON. (1:1)
> 1 | [,]
| ^"
"Operator '~' is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": ~1}
| ^^"
`;
exports[`snippet: #2 [json-stringify] format 1`] = `
"UnaryExpression with operator=\\"~\\" is not allowed in JSON. (1:9)
"Operator '~' is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": ~1}
| ^^"
`;
exports[`snippet: #2 [json5] format 1`] = `
"Operator '~' is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": ~1}
| ^^"
`;
exports[`snippet: #3 [json] format 1`] = `
"Sparse array is not allowed in JSON. (1:1)
> 1 | [1,,2]
| ^"
`;
exports[`snippet: #3 [json-stringify] format 1`] = `
"LogicalExpression is not allowed in JSON. (1:9)
"'LogicalExpression' is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": false || \\"bar\\"}
| ^^^^^^^^^^^^^^"
`;
exports[`snippet: #4 [json-stringify] format 1`] = `
"Identifier is not allowed in JSON. (1:9)
exports[`snippet: #3 [json-stringify] format 1`] = `
"'LogicalExpression' is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": false || \\"bar\\"}
| ^^^^^^^^^^^^^^"
`;
exports[`snippet: #3 [json5] format 1`] = `
"'LogicalExpression' is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": false || \\"bar\\"}
| ^^^^^^^^^^^^^^"
`;
exports[`snippet: #4 [json] format 1`] = `
"Identifier 'undefined' is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": undefined}
| ^^^^^^^^^"
`;
exports[`snippet: #5 [json-stringify] format 1`] = `
"ArrowFunctionExpression is not allowed in JSON. (1:9)
exports[`snippet: #4 [json-stringify] format 1`] = `
"Identifier 'undefined' is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": undefined}
| ^^^^^^^^^"
`;
exports[`snippet: #4 [json5] format 1`] = `
"Identifier 'undefined' is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": undefined}
| ^^^^^^^^^"
`;
exports[`snippet: #5 [json] format 1`] = `
"'ArrowFunctionExpression' is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": () => {}}
| ^^^^^^^^"
`;
exports[`snippet: #5 [json-stringify] format 1`] = `
"'ArrowFunctionExpression' is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": () => {}}
| ^^^^^^^^"
`;
exports[`snippet: #5 [json5] format 1`] = `
"'ArrowFunctionExpression' is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": () => {}}
| ^^^^^^^^"
`;
exports[`snippet: #6 [json] format 1`] = `
"Expecting Unicode escape sequence \\\\uXXXX (1:10)
> 1 | packages\\\\the-hub\\\\cypress\\\\fixtures\\\\gridConfiguration.json
| ^"
`;
exports[`snippet: #6 [json-stringify] format 1`] = `
"CommentBlock is not allowed in JSON. (1:1)
> 1 | /* comment */{\\"foo\\": 1}
| ^^^^^^^^^^^^^"
"Expecting Unicode escape sequence \\\\uXXXX (1:10)
> 1 | packages\\\\the-hub\\\\cypress\\\\fixtures\\\\gridConfiguration.json
| ^"
`;
exports[`snippet: #6 [json5] format 1`] = `
"Expecting Unicode escape sequence \\\\uXXXX (1:10)
> 1 | packages\\\\the-hub\\\\cypress\\\\fixtures\\\\gridConfiguration.json
| ^"
`;
exports[`snippet: #7 [json] format 1`] = `
"'BinaryExpression' is not allowed in JSON. (1:1)
> 1 | 1+2
| ^^^"
`;
exports[`snippet: #7 [json-stringify] format 1`] = `
"CommentLine is not allowed in JSON. (1:1)
"'BinaryExpression' is not allowed in JSON. (1:1)
> 1 | 1+2
| ^^^"
`;
exports[`snippet: #7 [json5] format 1`] = `
"'BinaryExpression' is not allowed in JSON. (1:1)
> 1 | 1+2
| ^^^"
`;
exports[`snippet: #8 [json] format 1`] = `
"Sparse array is not allowed in JSON. (1:1)
> 1 | [,]
| ^^^"
`;
exports[`snippet: #8 [json-stringify] format 1`] = `
"Sparse array is not allowed in JSON. (1:1)
> 1 | [,]
| ^^^"
`;
exports[`snippet: #8 [json5] format 1`] = `
"Sparse array is not allowed in JSON. (1:1)
> 1 | [,]
| ^^^"
`;
exports[`snippet: #9 [json] format 1`] = `
"Sparse array is not allowed in JSON. (1:1)
> 1 | [1,,2]
| ^^^^^^"
`;
exports[`snippet: #9 [json-stringify] format 1`] = `
"Sparse array is not allowed in JSON. (1:1)
> 1 | [1,,2]
| ^^^^^^"
`;
exports[`snippet: #9 [json5] format 1`] = `
"Sparse array is not allowed in JSON. (1:1)
> 1 | [1,,2]
| ^^^^^^"
`;
exports[`snippet: #10 [json] format 1`] = `
"Shorthand property is not allowed in JSON. (1:2)
> 1 | {Infinity}
| ^^^^^^^^"
`;
exports[`snippet: #10 [json-stringify] format 1`] = `
"Shorthand property is not allowed in JSON. (1:2)
> 1 | {Infinity}
| ^^^^^^^^"
`;
exports[`snippet: #10 [json5] format 1`] = `
"Shorthand property is not allowed in JSON. (1:2)
> 1 | {Infinity}
| ^^^^^^^^"
`;
exports[`snippet: #11 [json] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[key]: 1}
| ^^^"
`;
exports[`snippet: #11 [json-stringify] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[key]: 1}
| ^^^"
`;
exports[`snippet: #11 [json5] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[key]: 1}
| ^^^"
`;
exports[`snippet: #12 [json] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[key()]: 1}
| ^^^^^"
`;
exports[`snippet: #12 [json-stringify] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[key()]: 1}
| ^^^^^"
`;
exports[`snippet: #12 [json5] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[key()]: 1}
| ^^^^^"
`;
exports[`snippet: #13 [json] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {['CallExpression']: 1}
| ^^^^^^^^^^^^^^^^"
`;
exports[`snippet: #13 [json-stringify] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {['CallExpression']: 1}
| ^^^^^^^^^^^^^^^^"
`;
exports[`snippet: #13 [json5] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {['CallExpression']: 1}
| ^^^^^^^^^^^^^^^^"
`;
exports[`snippet: #14 [json] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {['StringLiteral']: 1}
| ^^^^^^^^^^^^^^^"
`;
exports[`snippet: #14 [json-stringify] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {['StringLiteral']: 1}
| ^^^^^^^^^^^^^^^"
`;
exports[`snippet: #14 [json5] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {['StringLiteral']: 1}
| ^^^^^^^^^^^^^^^"
`;
exports[`snippet: #15 [json] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {['string']: 1}
| ^^^^^^^^"
`;
exports[`snippet: #15 [json-stringify] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {['string']: 1}
| ^^^^^^^^"
`;
exports[`snippet: #15 [json5] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {['string']: 1}
| ^^^^^^^^"
`;
exports[`snippet: #16 [json] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[1]: 1}
| ^"
`;
exports[`snippet: #16 [json-stringify] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[1]: 1}
| ^"
`;
exports[`snippet: #16 [json5] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[1]: 1}
| ^"
`;
exports[`snippet: #17 [json] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[Infinity]: 1}
| ^^^^^^^^"
`;
exports[`snippet: #17 [json-stringify] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[Infinity]: 1}
| ^^^^^^^^"
`;
exports[`snippet: #17 [json5] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[Infinity]: 1}
| ^^^^^^^^"
`;
exports[`snippet: #18 [json] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[-Infinity]: 1}
| ^^^^^^^^^"
`;
exports[`snippet: #18 [json-stringify] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[-Infinity]: 1}
| ^^^^^^^^^"
`;
exports[`snippet: #18 [json5] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[-Infinity]: 1}
| ^^^^^^^^^"
`;
exports[`snippet: #19 [json] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[{key: 'value'}]: 1}
| ^^^^^^^^^^^^^^"
`;
exports[`snippet: #19 [json-stringify] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[{key: 'value'}]: 1}
| ^^^^^^^^^^^^^^"
`;
exports[`snippet: #19 [json5] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[{key: 'value'}]: 1}
| ^^^^^^^^^^^^^^"
`;
exports[`snippet: #20 [json] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[[]]: 1}
| ^^"
`;
exports[`snippet: #20 [json-stringify] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[[]]: 1}
| ^^"
`;
exports[`snippet: #20 [json5] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[[]]: 1}
| ^^"
`;
exports[`snippet: #21 [json] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[null]: 1}
| ^^^^"
`;
exports[`snippet: #21 [json-stringify] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[null]: 1}
| ^^^^"
`;
exports[`snippet: #21 [json5] format 1`] = `
"Computed key is not allowed in JSON. (1:3)
> 1 | {[null]: 1}
| ^^^^"
`;
exports[`snippet: #22 [json] format 1`] = `
"'CallExpression' is not allowed in JSON. (1:8)
> 1 | {key: +foo()}
| ^^^^^"
`;
exports[`snippet: #22 [json-stringify] format 1`] = `
"'CallExpression' is not allowed in JSON. (1:8)
> 1 | {key: +foo()}
| ^^^^^"
`;
exports[`snippet: #22 [json5] format 1`] = `
"'CallExpression' is not allowed in JSON. (1:8)
> 1 | {key: +foo()}
| ^^^^^"
`;
exports[`snippet: #23 [json] format 1`] = `
"Operator 'void' is not allowed in JSON. (1:7)
> 1 | {key: void foo()}
| ^^^^^^^^^^"
`;
exports[`snippet: #23 [json-stringify] format 1`] = `
"Operator 'void' is not allowed in JSON. (1:7)
> 1 | {key: void foo()}
| ^^^^^^^^^^"
`;
exports[`snippet: #23 [json5] format 1`] = `
"Operator 'void' is not allowed in JSON. (1:7)
> 1 | {key: void foo()}
| ^^^^^^^^^^"
`;
exports[`snippet: #24 [json] format 1`] = `
"Unexpected token (1:1)
> 1 | #!/usr/bin/env node
| ^
2 | {}"
`;
exports[`snippet: #24 [json-stringify] format 1`] = `
"Unexpected token (1:1)
> 1 | #!/usr/bin/env node
| ^
2 | {}"
`;
exports[`snippet: #24 [json5] format 1`] = `
"Unexpected token (1:1)
> 1 | #!/usr/bin/env node
| ^
2 | {}"
`;
exports[`snippet: #25 [json] format 1`] = `
"Unexpected token (2:1)
1 | \\"use strict\\"
> 2 | {}
| ^"
`;
exports[`snippet: #25 [json-stringify] format 1`] = `
"Unexpected token (2:1)
1 | \\"use strict\\"
> 2 | {}
| ^"
`;
exports[`snippet: #25 [json5] format 1`] = `
"Unexpected token (2:1)
1 | \\"use strict\\"
> 2 | {}
| ^"
`;
exports[`snippet: #26 [json] format 1`] = `
"Unexpected token (1:14)
> 1 | /* comment */
| ^"
`;
exports[`snippet: #26 [json-stringify] format 1`] = `
"Unexpected token (1:14)
> 1 | /* comment */
| ^"
`;
exports[`snippet: #26 [json5] format 1`] = `
"Unexpected token (1:14)
> 1 | /* comment */
| ^"
`;
exports[`snippet: #27 [json] format 1`] = `
"Unexpected token (1:11)
> 1 | // comment
| ^^^^^^^^^^
2 | {\\"foo\\": 1}"
| ^"
`;
exports[`snippet: #27 [json-stringify] format 1`] = `
"Unexpected token (1:11)
> 1 | // comment
| ^"
`;
exports[`snippet: #27 [json5] format 1`] = `
"Unexpected token (1:11)
> 1 | // comment
| ^"
`;
+19 -12
View File
@@ -8,22 +8,29 @@ run_spec(
'{"foo": false || "bar"}',
'{"foo": undefined}',
'{"foo": () => {}}',
'/* comment */{"foo": 1}',
'// comment\n{"foo": 1}',
],
},
["json-stringify"]
);
run_spec(
{
dirname: __dirname,
snippets: [
"packages\\the-hub\\cypress\\fixtures\\gridConfiguration.json",
"1+2",
"[,]",
"[1,,2]",
"{Infinity}",
"{[key]: 1}",
"{[key()]: 1}",
"{['CallExpression']: 1}",
"{['StringLiteral']: 1}",
"{['string']: 1}",
"{[1]: 1}",
"{[Infinity]: 1}",
"{[-Infinity]: 1}",
"{[{key: 'value'}]: 1}",
"{[[]]: 1}",
"{[null]: 1}",
"{key: +foo()}",
"{key: void foo()}",
"#!/usr/bin/env node\n{}",
'"use strict"\n{}',
"/* comment */",
"// comment",
],
},
["json"]
["json", "json5", "json-stringify"]
);
@@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`snippet: #0 [json-stringify] format 1`] = `
"Comment is not allowed in JSON. (1:1)
> 1 | /* comment */{\\"foo\\": 1}
| ^^^^^^^^^^^^^"
`;
exports[`snippet: #1 [json-stringify] format 1`] = `
"Comment is not allowed in JSON. (1:1)
> 1 | // comment
| ^^^^^^^^^^
2 | {\\"foo\\": 1}"
`;
exports[`snippet: #2 [json-stringify] format 1`] = `
"Comment is not allowed in JSON. (1:9)
> 1 | {\\"foo\\": /* comment */ 1}
| ^^^^^^^^^^^^^"
`;
@@ -0,0 +1,11 @@
run_spec(
{
dirname: __dirname,
snippets: [
'/* comment */{"foo": 1}',
'// comment\n{"foo": 1}',
'{"foo": /* comment */ 1}',
],
},
["json-stringify"]
);