diff --git a/changelog_unreleased/json/10497.md b/changelog_unreleased/json/10497.md new file mode 100644 index 000000000..10e7b5dc7 --- /dev/null +++ b/changelog_unreleased/json/10497.md @@ -0,0 +1,16 @@ +#### Fix syntax error on JSON range formatting (#10497 by @fisker) + + +```js +// Input +[{ a: 1.0000}, {"b": 2.0000 }] +// ^^^^^^^^^^^ range + +// Prettier stable +SyntaxError: Unexpected token (1:4) +> 1 | "b": 2.0000 + | ^ + +// Prettier main +[{ a: 1.0000}, { "b": 2.0 }] +``` diff --git a/src/main/range-util.js b/src/main/range-util.js index 3152e1168..6e27aff84 100644 --- a/src/main/range-util.js +++ b/src/main/range-util.js @@ -3,6 +3,23 @@ const assert = require("assert"); const comments = require("./comments"); +const isJsonParser = ({ parser }) => + parser === "json" || parser === "json5" || parser === "json-stringify"; + +function findCommonAncestor(startNodeAndParents, endNodeAndParents) { + const startNodeAndAncestors = [ + startNodeAndParents.node, + ...startNodeAndParents.parentNodes, + ]; + const endNodeAndAncestors = new Set([ + endNodeAndParents.node, + ...endNodeAndParents.parentNodes, + ]); + return startNodeAndAncestors.find( + (node) => jsonSourceElements.has(node.type) && endNodeAndAncestors.has(node) + ); +} + function findSiblingAncestors(startNodeAndParents, endNodeAndParents, opts) { let resultStartNode = startNodeAndParents.node; let resultEndNode = endNodeAndParents.node; @@ -109,6 +126,8 @@ const jsonSourceElements = new Set([ "NumericLiteral", "BooleanLiteral", "NullLiteral", + "UnaryExpression", + "TemplateLiteral", ]); const graphqlSourceElements = new Set([ "OperationDefinition", @@ -143,6 +162,8 @@ function isSourceElement(opts, node, parentNode) { case "meriyah": return isJsSourceElement(node.type, parentNode && parentNode.type); case "json": + case "json5": + case "json-stringify": return jsonSourceElements.has(node.type); case "graphql": return graphqlSourceElements.has(node.kind); @@ -195,11 +216,22 @@ function calculateRange(text, opts, ast) { }; } - const { startNode, endNode } = findSiblingAncestors( - startNodeAndParents, - endNodeAndParents, - opts - ); + let startNode; + let endNode; + if (isJsonParser(opts)) { + const commonAncestor = findCommonAncestor( + startNodeAndParents, + endNodeAndParents + ); + startNode = commonAncestor; + endNode = commonAncestor; + } else { + ({ startNode, endNode } = findSiblingAncestors( + startNodeAndParents, + endNodeAndParents, + opts + )); + } return { rangeStart: Math.min(locStart(startNode), locStart(endNode)), diff --git a/tests/json/range/__snapshots__/jsfmt.spec.js.snap b/tests/json/range/__snapshots__/jsfmt.spec.js.snap index dcdef5f99..53213d9c2 100644 --- a/tests/json/range/__snapshots__/jsfmt.spec.js.snap +++ b/tests/json/range/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,115 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`cross-array.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 51 +rangeStart: 12 + | | printWidth +=====================================input====================================== + 1 | [{ + 2 | a: [ + 3 | 1, +> 4 | 2, 3,4,5,6, 7, + | ^^^^^^^^^^^^^^^^^^^^ +> 5 | 8 + | ^ +> 6 | ], + | ^ +> 7 | b: [1, 2, 3, 4], + | ^^^^^^^^^^^^^ + 8 | c: [1, 2] + 9 | } + 10 | ,{a: 2}] + 11 | +=====================================output===================================== +[{ + "a": [1, 2, 3, 4, 5, 6, 7, 8], + "b": [1, 2, 3, 4], + "c": [1, 2] +} +,{a: 2}] + +================================================================================ +`; + +exports[`cross-array2.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 36 +rangeStart: 11 + | | printWidth +=====================================input====================================== + 1 | { + 2 | a: [ + 3 | 1, +> 4 | 2, 3,4,5,6, [1, 2, 3, 4], 7, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + 5 | 8 + 6 | ], + 7 | c: [1, 2] + 8 | } + 9 | +=====================================output===================================== +{ +a: [1, 2, 3, 4, 5, 6, [1, 2, 3, 4], 7, 8], +c: [1, 2] +} + +================================================================================ +`; + +exports[`cross-object.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 31 +rangeStart: 12 + | | printWidth +=====================================input====================================== + 1 | [{a: +> 2 | { "b": 2, "c": 3 }, + | ^^^^^^^^^^^^ +> 3 | b: {d:4}, + | ^^^^^^ + 4 | c: {d: 6} + 5 | }, + 6 | {a: 1}] + 7 | +=====================================output===================================== +[{ "a": { "b": 2, "c": 3 }, "b": { "d": 4 }, "c": { "d": 6 } }, +{a: 1}] + +================================================================================ +`; + +exports[`cross-object-2.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 28 +rangeStart: 11 + | | printWidth +=====================================input====================================== + 1 | {a: +> 2 | { "b": 2, "c": 3, + | ^^^^^^^^^^ +> 3 | d: {d:4}, }, + | ^^^^^^ + 4 | c: {d: 6} + 5 | } + 6 | +=====================================output===================================== +{a: +{ "b": 2, "c": 3, "d": { "d": 4 } }, +c: {d: 6} +} + +================================================================================ +`; + exports[`identifier.json format 1`] = ` ====================================options===================================== parsers: ["json"] @@ -17,6 +127,113 @@ rangeStart: 1 ================================================================================ `; +exports[`identifier-2.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 3 +rangeStart: 2 + | | printWidth +=====================================input====================================== +> 1 | Infinity + | ^ + 2 | +=====================================output===================================== +Infinity + +================================================================================ +`; + +exports[`inside-array.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 30 +rangeStart: 11 + | | printWidth +=====================================input====================================== + 1 | { + 2 | a: [ + 3 | 1, +> 4 | 2, 3,4,5,6, 7, + | ^^^^^^^^^^^^^^^^^^^ + 5 | 8 + 6 | ], + 7 | b: [1, 2, 3, 4] + 8 | } + 9 | +=====================================output===================================== +{ +a: [1, 2, 3, 4, 5, 6, 7, 8], +b: [1, 2, 3, 4] +} + +================================================================================ +`; + +exports[`inside-object.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 20 +rangeStart: 11 + | | printWidth +=====================================input====================================== + 1 | {a: +> 2 | { "b": 2, "c": 3 }, + | ^^^^^^^^^ + 3 | b: {d:4} + 4 | } + 5 | +=====================================output===================================== +{a: +{ "b": 2, "c": 3 }, +b: {d:4} +} + +================================================================================ +`; + +exports[`issue-4009.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 19 +rangeStart: 1 + | | printWidth +=====================================input====================================== +> 1 | { + | ^ +> 2 | "foo": "bar" + | ^^^^^^^^^^^^^^^^ +> 3 | } + | ^ + 4 | +=====================================output===================================== +{ + "foo": "bar" +} + +================================================================================ +`; + +exports[`issue-7116.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 7 +rangeStart: 4 + | | printWidth +=====================================input====================================== +> 1 | {"b": 2} + | ^^^ + 2 | +=====================================output===================================== +{ "b": 2 } + +================================================================================ +`; + exports[`issue2297.json format 1`] = ` ====================================options===================================== parsers: ["json"] @@ -33,3 +250,121 @@ rangeStart: 5 ================================================================================ `; + +exports[`number.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 16 +rangeStart: 7 + | | printWidth +=====================================input====================================== +> 1 | { "b": 2, "c": 3 } + | ^^^^^^^^^ + 2 | +=====================================output===================================== +{ "b": 2, "c": 3 } + +================================================================================ +`; + +exports[`template-literal.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 7 +rangeStart: 6 + | | printWidth +=====================================input====================================== + 1 | [ +> 2 | {a:\`a\`,n: + | ^ + 3 | ''}, + 4 | {a:\`a\`,n: + 5 | ''} + 6 | ] + 7 | +=====================================output===================================== +[ +{a:\`a\`,n: +''}, +{a:\`a\`,n: +''} +] + +================================================================================ +`; + +exports[`template-literal-2.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 7 +rangeStart: 4 + | | printWidth +=====================================input====================================== + 1 | [ +> 2 | {a:\`a\`,n: + | ^^^ + 3 | ''}, + 4 | {a:\`a\`,n: + 5 | ''} + 6 | ] + 7 | +=====================================output===================================== +[ +{ "a": \`a\`, "n": "" }, +{a:\`a\`,n: +''} +] + +================================================================================ +`; + +exports[`unary-expression.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 15 +rangeStart: 9 + | | printWidth +=====================================input====================================== + 1 | {a: + 2 | [ + 3 | 1, +> 4 | -2, + | ^^^ +> 5 | -3 + | ^^ + 6 | + 7 | ], + 8 | b: [1, 2] + 9 | } + 10 | +=====================================output===================================== +{a: +[1, -2, -3], +b: [1, 2] +} + +================================================================================ +`; + +exports[`unary-expression-2.json format 1`] = ` +====================================options===================================== +parsers: ["json"] +printWidth: 80 +rangeEnd: 9 +rangeStart: 2 + | | printWidth +=====================================input====================================== + 1 | - +> 2 | 2.00000 + | ^^^^^^^ + 3 | +=====================================output===================================== +- +2.0 + +================================================================================ +`; diff --git a/tests/json/range/cross-array.json b/tests/json/range/cross-array.json new file mode 100644 index 000000000..702f3c890 --- /dev/null +++ b/tests/json/range/cross-array.json @@ -0,0 +1,10 @@ +[{ +a: [ +1, + <<>>2, 3,4,5,6, 7, +8 +], +b: [1, 2, <<>> 3, 4], +c: [1, 2] +} +,{a: 2}] diff --git a/tests/json/range/cross-array2.json b/tests/json/range/cross-array2.json new file mode 100644 index 000000000..6c1585ce2 --- /dev/null +++ b/tests/json/range/cross-array2.json @@ -0,0 +1,8 @@ +{ +a: [ +1, + <<>>2, 3,4,5,6, [1, 2, <<>> 3, 4], 7, +8 +], +c: [1, 2] +} diff --git a/tests/json/range/cross-object-2.json b/tests/json/range/cross-object-2.json new file mode 100644 index 000000000..05c3314fb --- /dev/null +++ b/tests/json/range/cross-object-2.json @@ -0,0 +1,5 @@ +{a: +{ "b": <<>>2, "c": 3, +d: {d:<<>>4}, }, +c: {d: 6} +} diff --git a/tests/json/range/cross-object.json b/tests/json/range/cross-object.json new file mode 100644 index 000000000..891c742a0 --- /dev/null +++ b/tests/json/range/cross-object.json @@ -0,0 +1,6 @@ +[{a: +{ "b": <<>>2, "c": 3 }, +b: {d:<<>>4}, +c: {d: 6} +}, +{a: 1}] diff --git a/tests/json/range/identifier-2.json b/tests/json/range/identifier-2.json new file mode 100644 index 000000000..6dff4c257 --- /dev/null +++ b/tests/json/range/identifier-2.json @@ -0,0 +1 @@ +In<<>>f<<>>inity diff --git a/tests/json/range/inside-array.json b/tests/json/range/inside-array.json new file mode 100644 index 000000000..2f740ea15 --- /dev/null +++ b/tests/json/range/inside-array.json @@ -0,0 +1,8 @@ +{ +a: [ +1, + <<>>2, 3,4,5,6, 7<<>>, +8 +], +b: [1, 2, 3, 4] +} diff --git a/tests/json/range/inside-object.json b/tests/json/range/inside-object.json new file mode 100644 index 000000000..13bbf7a98 --- /dev/null +++ b/tests/json/range/inside-object.json @@ -0,0 +1,4 @@ +{a: +{ "b": <<>>2, "c": 3<<>> }, +b: {d:4} +} diff --git a/tests/json/range/issue-4009.json b/tests/json/range/issue-4009.json new file mode 100644 index 000000000..fd21f6a78 --- /dev/null +++ b/tests/json/range/issue-4009.json @@ -0,0 +1,3 @@ +{<<>> + "foo": "bar" +<<>>} diff --git a/tests/json/range/issue-7116.json b/tests/json/range/issue-7116.json new file mode 100644 index 000000000..c55c0f83b --- /dev/null +++ b/tests/json/range/issue-7116.json @@ -0,0 +1 @@ +{"b"<<>>: 2<<>>} diff --git a/tests/json/range/json-stringify/__snapshots__/jsfmt.spec.js.snap b/tests/json/range/json-stringify/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..7e343effc --- /dev/null +++ b/tests/json/range/json-stringify/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`template-literal.json format 1`] = ` +====================================options===================================== +parsers: ["json-stringify"] +printWidth: 80 +rangeEnd: 8 +rangeStart: 7 + | | printWidth +=====================================input====================================== + 1 | [ +> 2 | {a:\`aaa\`,n: + | ^ + 3 | ''}, + 4 | {a:\`a\`,n: + 5 | ''} + 6 | ] + 7 | +=====================================output===================================== +[ + { + "a": "aaa", + "n": "" + }, + { + "a": "a", + "n": "" + } +] + +================================================================================ +`; diff --git a/tests/json/range/json-stringify/jsfmt.spec.js b/tests/json/range/json-stringify/jsfmt.spec.js new file mode 100644 index 000000000..22498fda3 --- /dev/null +++ b/tests/json/range/json-stringify/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["json-stringify"]); diff --git a/tests/json/range/json-stringify/template-literal.json b/tests/json/range/json-stringify/template-literal.json new file mode 100644 index 000000000..cde30cef7 --- /dev/null +++ b/tests/json/range/json-stringify/template-literal.json @@ -0,0 +1,6 @@ +[ +{a:`a<<>>a<<>>a`,n: +''}, +{a:`a`,n: +''} +] diff --git a/tests/json/range/json5/__snapshots__/jsfmt.spec.js.snap b/tests/json/range/json5/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..b2da96a49 --- /dev/null +++ b/tests/json/range/json5/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,28 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`inside-array.json format 1`] = ` +====================================options===================================== +parsers: ["json5"] +printWidth: 80 +rangeEnd: 30 +rangeStart: 11 + | | printWidth +=====================================input====================================== + 1 | { + 2 | a: [ + 3 | 1, +> 4 | 2, 3,4,5,6, 7, + | ^^^^^^^^^^^^^^^^^^^ + 5 | 8 + 6 | ], + 7 | b: [1, 2, 3, 4] + 8 | } + 9 | +=====================================output===================================== +{ +a: [1, 2, 3, 4, 5, 6, 7, 8], +b: [1, 2, 3, 4] +} + +================================================================================ +`; diff --git a/tests/json/range/json5/inside-array.json b/tests/json/range/json5/inside-array.json new file mode 100644 index 000000000..2f740ea15 --- /dev/null +++ b/tests/json/range/json5/inside-array.json @@ -0,0 +1,8 @@ +{ +a: [ +1, + <<>>2, 3,4,5,6, 7<<>>, +8 +], +b: [1, 2, 3, 4] +} diff --git a/tests/json/range/json5/jsfmt.spec.js b/tests/json/range/json5/jsfmt.spec.js new file mode 100644 index 000000000..a4031df29 --- /dev/null +++ b/tests/json/range/json5/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["json5"]); diff --git a/tests/json/range/number.json b/tests/json/range/number.json new file mode 100644 index 000000000..29bea7dc1 --- /dev/null +++ b/tests/json/range/number.json @@ -0,0 +1 @@ +{ "b": <<>>2, "c": 3<<>> } diff --git a/tests/json/range/template-literal-2.json b/tests/json/range/template-literal-2.json new file mode 100644 index 000000000..2c923a87a --- /dev/null +++ b/tests/json/range/template-literal-2.json @@ -0,0 +1,6 @@ +[ +{a<<>>:`a<<>>`,n: +''}, +{a:`a`,n: +''} +] diff --git a/tests/json/range/template-literal.json b/tests/json/range/template-literal.json new file mode 100644 index 000000000..2119b7925 --- /dev/null +++ b/tests/json/range/template-literal.json @@ -0,0 +1,6 @@ +[ +{a:`<<>>a<<>>`,n: +''}, +{a:`a`,n: +''} +] diff --git a/tests/json/range/unary-expression-2.json b/tests/json/range/unary-expression-2.json new file mode 100644 index 000000000..e3f21808f --- /dev/null +++ b/tests/json/range/unary-expression-2.json @@ -0,0 +1,2 @@ +- +<<>>2.00000<<>> diff --git a/tests/json/range/unary-expression.json b/tests/json/range/unary-expression.json new file mode 100644 index 000000000..d9e9c931b --- /dev/null +++ b/tests/json/range/unary-expression.json @@ -0,0 +1,9 @@ +{a: +[ +1, +<<>>-2, +-3<<>> + +], +b: [1, 2] +}