diff --git a/changelog_unreleased/json/10346.md b/changelog_unreleased/json/10346.md new file mode 100644 index 000000000..1bbccf9d5 --- /dev/null +++ b/changelog_unreleased/json/10346.md @@ -0,0 +1,17 @@ +#### Stricter JSON parser (#10346 by @fisker) + +Previously we allow all kinds of JavaScript expressions in `json` and `json5` parser, now they are stricter. + + +```js +// Input +[1, 2, 1 + 2] + +// Prettier stable +[1, 2, 1 + 2] + +// Prettier main +SyntaxError: BinaryExpression is not allowed in JSON. (1:8) +> 1 | [1, 2, 1 + 2] + | ^ +``` diff --git a/src/language-js/parser-babel.js b/src/language-js/parser-babel.js index 1f9478c07..d219ed48d 100644 --- a/src/language-js/parser-babel.js +++ b/src/language-js/parser-babel.js @@ -153,6 +153,7 @@ const parseTypeScript = createParse( ["typescript"] ); const parseExpression = createParse("parseExpression", ["jsx"]); +const parseJson = createJsonParse(); const messagesShouldThrow = new Set([ // TSErrors.UnexpectedTypeAnnotation @@ -179,15 +180,31 @@ function shouldRethrowRecoveredError(error) { return messagesShouldThrow.has(message); } -function parseJson(text, parsers, opts) { - const ast = parseExpression(text, parsers, opts); +function createJsonParse(options = {}) { + const { allowComments = true } = options; - for (const comment of ast.comments) { - assertJsonNode(comment); - } - assertJsonNode(ast); + return function parse(text /*, parsers, options*/) { + let ast; + try { + ast = require("@babel/parser").parseExpression(text, { + tokens: true, + ranges: true, + }); + } catch (error) { + throw createParseError(error); + } - return ast; + if (!allowComments) { + // @ts-ignore + for (const comment of ast.comments) { + assertJsonNode(comment); + } + } + + assertJsonNode(ast); + + return ast; + }; } function assertJsonNode(node, parent) { @@ -262,15 +279,15 @@ module.exports = { babel, "babel-flow": createParser(parseFlow), "babel-ts": createParser(parseTypeScript), - json: { - ...babelExpression, + json: createParser({ + parse: parseJson, hasPragma() { return true; }, - }, - json5: babelExpression, + }), + json5: createParser(parseJson), "json-stringify": createParser({ - parse: parseJson, + parse: createJsonParse({ allowComments: false }), astFormat: "estree-json", }), /** @internal */ diff --git a/tests/markdown/multiparser-json/__snapshots__/jsfmt.spec.js.snap b/tests/markdown/multiparser-json/__snapshots__/jsfmt.spec.js.snap index b3f00746c..c6ab38824 100644 --- a/tests/markdown/multiparser-json/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown/multiparser-json/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,26 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`invalid-json.md - {"trailingComma":"all"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +trailingComma: "all" + | printWidth +=====================================input====================================== + +\`\`\`json +packages\\the-hub\\cypress\\fixtures\\gridConfiguration.json +\`\`\` +=====================================output===================================== + + +\`\`\`json +packages\\the-hub\\cypress\\fixtures\\gridConfiguration.json +\`\`\` + +================================================================================ +`; + exports[`jsonc.md - {"trailingComma":"all"} format 1`] = ` ====================================options===================================== parsers: ["markdown"] diff --git a/tests/markdown/multiparser-json/invalid-json.md b/tests/markdown/multiparser-json/invalid-json.md new file mode 100644 index 000000000..4c4e7236b --- /dev/null +++ b/tests/markdown/multiparser-json/invalid-json.md @@ -0,0 +1,4 @@ + +```json +packages\the-hub\cypress\fixtures\gridConfiguration.json +``` \ No newline at end of file diff --git a/tests/misc/errors/json/__snapshots__/jsfmt.spec.js.snap b/tests/misc/errors/json/__snapshots__/jsfmt.spec.js.snap index c39a1a644..488f87de9 100644 --- a/tests/misc/errors/json/__snapshots__/jsfmt.spec.js.snap +++ b/tests/misc/errors/json/__snapshots__/jsfmt.spec.js.snap @@ -1,11 +1,23 @@ // 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 + | ^" +`; + exports[`snippet: #0 [json-stringify] format 1`] = ` "ObjectProperty with shorthand=true 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 + | ^" +`; + exports[`snippet: #1 [json-stringify] format 1`] = ` "ObjectProperty with computed=true is not allowed in JSON. (1:2) > 1 | {[\\"foo\\"]:\\"bar\\"} @@ -35,3 +47,16 @@ exports[`snippet: #5 [json-stringify] format 1`] = ` > 1 | {\\"foo\\": () => {}} | ^" `; + +exports[`snippet: #6 [json-stringify] format 1`] = ` +"CommentBlock is not allowed in JSON. (1:1) +> 1 | /* comment */{\\"foo\\": 1} + | ^" +`; + +exports[`snippet: #7 [json-stringify] format 1`] = ` +"CommentLine is not allowed in JSON. (1:1) +> 1 | // comment + | ^ + 2 | {\\"foo\\": 1}" +`; diff --git a/tests/misc/errors/json/jsfmt.spec.js b/tests/misc/errors/json/jsfmt.spec.js index 38ba22de3..725a04abd 100644 --- a/tests/misc/errors/json/jsfmt.spec.js +++ b/tests/misc/errors/json/jsfmt.spec.js @@ -8,7 +8,20 @@ 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", + ], + }, + ["json"] +);