diff --git a/src/language-js/parser/json.js b/src/language-js/parser/json.js index 44e1d10dd..0fc3cc3e9 100644 --- a/src/language-js/parser/json.js +++ b/src/language-js/parser/json.js @@ -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}'`); } } diff --git a/tests/misc/errors/json/__snapshots__/jsfmt.spec.js.snap b/tests/misc/errors/json/__snapshots__/jsfmt.spec.js.snap index 9094dec5c..e1a6d008a 100644 --- a/tests/misc/errors/json/__snapshots__/jsfmt.spec.js.snap +++ b/tests/misc/errors/json/__snapshots__/jsfmt.spec.js.snap @@ -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 + | ^" `; diff --git a/tests/misc/errors/json/jsfmt.spec.js b/tests/misc/errors/json/jsfmt.spec.js index e26dd130f..abccd7b90 100644 --- a/tests/misc/errors/json/jsfmt.spec.js +++ b/tests/misc/errors/json/jsfmt.spec.js @@ -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"] ); diff --git a/tests/misc/errors/json/json-stringify/__snapshots__/jsfmt.spec.js.snap b/tests/misc/errors/json/json-stringify/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..2f1c24a06 --- /dev/null +++ b/tests/misc/errors/json/json-stringify/__snapshots__/jsfmt.spec.js.snap @@ -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} + | ^^^^^^^^^^^^^" +`; diff --git a/tests/misc/errors/json/json-stringify/jsfmt.spec.js b/tests/misc/errors/json/json-stringify/jsfmt.spec.js new file mode 100644 index 000000000..0ecb266a2 --- /dev/null +++ b/tests/misc/errors/json/json-stringify/jsfmt.spec.js @@ -0,0 +1,11 @@ +run_spec( + { + dirname: __dirname, + snippets: [ + '/* comment */{"foo": 1}', + '// comment\n{"foo": 1}', + '{"foo": /* comment */ 1}', + ], + }, + ["json-stringify"] +);