Add JSON support (#2173)
* Add JSON support This fixes https://github.com/prettier/prettier/issues/1973 by using [json-to-ast] to parse JSON, then converting the AST into a mostly-Babylon AST, so as to piggyback on the existing printer logic. Identifiers and literals are currently printed verbatim from the input, but this could be improved upon later (at least for literals, not sure about identifiers). [json-to-ast]: https://github.com/vtrushin/json-to-ast * Rename `json-to-ast` parser to `json` See https://github.com/prettier/prettier/pull/2173#discussion_r122574644 * json: Use createError to have a pretty error See https://github.com/prettier/prettier/pull/2173#discussion_r122574748 * json: Transform AST without mutations See https://github.com/prettier/prettier/pull/2173#discussion_r122574690
This commit is contained in:
1 parent
59a294fcc9
commit
a42db30946
13 files changed
+281
-2
No files matched your search
@@ -283,7 +283,7 @@ Prettier ships with a handful of customizable format options, usable in both the
|
||||
| **Cursor Offset** - Specify where the cursor is. This option only works with `prettier.formatWithCursor`, and cannot be used with `rangeStart` and `rangeEnd`. | `-1` | `--cursor-offset <int>` | `cursorOffset: <int>` |
|
||||
| **Range Start** - Format code starting at a given character offset. The range will extend backwards to the start of the first line containing the selected statement. This option cannot be used with `cursorOffset`. | `0` | `--range-start <int>` | `rangeStart: <int>` |
|
||||
| **Range End** - Format code ending at a given character offset (exclusive). The range will extend forwards to the end of the selected statement. This option cannot be used with `cursorOffset`. | `Infinity` | `--range-end <int>` | `rangeEnd: <int>` |
|
||||
| **Parser** - Specify which parser to use. Both the `babylon` and `flow` parsers support the same set of JavaScript features (including Flow). Prettier automatically infers the parser from the input file path, so you shouldn't have to change this setting. [Custom parsers](#custom-parser-api) are supported. | `babylon` | <code>--parser <flow|babylon|typescript|postcss></code><br /><code>--parser ./path/to/my-parser</code> | <code>parser: "<flow|babylon|typescript|postcss>"</code><br /><code>parser: require("./my-parser")</code> |
|
||||
| **Parser** - Specify which parser to use. Both the `babylon` and `flow` parsers support the same set of JavaScript features (including Flow). Prettier automatically infers the parser from the input file path, so you shouldn't have to change this setting. [Custom parsers](#custom-parser-api) are supported. | `babylon` | <code>--parser <flow|babylon|typescript|postcss|json></code><br /><code>--parser ./path/to/my-parser</code> | <code>parser: "<flow|babylon|typescript|postcss|json>"</code><br /><code>parser: require("./my-parser")</code> |
|
||||
| **Filepath** - Specify the input filepath this will be used to do parser inference.<br /><br /> Example: <br />`cat foo \| prettier --stdin-filepath foo.css`<br /> will default to use `postcss` parser | | `--stdin-filepath` | `filepath: <string>` |
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -231,7 +231,7 @@ if (argv["help"] || (!filepatterns.length && !stdin)) {
|
||||
" --jsx-bracket-same-line Put > on the last line instead of at a new line.\n" +
|
||||
" --trailing-comma <none|es5|all>\n" +
|
||||
" Print trailing commas wherever possible. Defaults to none.\n" +
|
||||
" --parser <flow|babylon|typescript|postcss>\n" +
|
||||
" --parser <flow|babylon|typescript|postcss|json>\n" +
|
||||
" Specify which parse to use. Defaults to babylon.\n" +
|
||||
" --cursor-offset <int> Print (to stderr) where a cursor at the given position would move to after formatting.\n" +
|
||||
" This option cannot be used with --range-start and --range-end\n" +
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"glob": "7.1.2",
|
||||
"graphql": "0.10.1",
|
||||
"jest-validate": "20.0.3",
|
||||
"json-to-ast": "2.0.0-alpha1.2",
|
||||
"minimist": "1.2.0",
|
||||
"parse5": "3.0.2",
|
||||
"postcss": "^6.0.1",
|
||||
|
||||
@@ -35,6 +35,8 @@ function normalize(options) {
|
||||
normalized.parser = "parse5";
|
||||
} else if (/\.(ts|tsx)$/.test(filepath)) {
|
||||
normalized.parser = "typescript";
|
||||
} else if (/\.json$/.test(filepath)) {
|
||||
normalized.parser = "json";
|
||||
}
|
||||
|
||||
if (typeof normalized.trailingComma === "boolean") {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
"use strict";
|
||||
|
||||
const createError = require("./parser-create-error");
|
||||
|
||||
function parse(text) {
|
||||
const jsonToAst = require("json-to-ast");
|
||||
try {
|
||||
const ast = jsonToAst(text);
|
||||
return toBabylon(ast);
|
||||
} catch (err) {
|
||||
const firstNewlineIndex = err.message.indexOf("\n");
|
||||
const firstLine = err.message.slice(0, firstNewlineIndex);
|
||||
const lastSpaceIndex = firstLine.lastIndexOf(" ");
|
||||
const message = firstLine.slice(0, lastSpaceIndex);
|
||||
const locString = firstLine.slice(lastSpaceIndex + 1);
|
||||
const lineCol = locString.split(":").map(Number);
|
||||
|
||||
throw createError("(json-to-ast) " + message, {
|
||||
start: {
|
||||
line: lineCol[0],
|
||||
column: lineCol[1]
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function toBabylon(node) {
|
||||
const typeMap = {
|
||||
object: "ObjectExpression",
|
||||
property: "ObjectProperty",
|
||||
identifier: "json-identifier",
|
||||
array: "ArrayExpression",
|
||||
literal: "json-literal"
|
||||
};
|
||||
|
||||
const result = {
|
||||
type: typeMap[node.type],
|
||||
start: node.loc.start.offset,
|
||||
end: node.loc.end.offset,
|
||||
loc: node.loc
|
||||
};
|
||||
|
||||
switch (node.type) {
|
||||
case "object":
|
||||
return Object.assign(result, {
|
||||
properties: node.children.map(toBabylon)
|
||||
});
|
||||
case "property":
|
||||
return Object.assign(result, {
|
||||
key: toBabylon(node.key),
|
||||
value: toBabylon(node.value)
|
||||
});
|
||||
case "identifier":
|
||||
return Object.assign(result, {
|
||||
value: node.value
|
||||
});
|
||||
case "array":
|
||||
return Object.assign(result, {
|
||||
elements: node.children.map(toBabylon)
|
||||
});
|
||||
case "literal":
|
||||
return Object.assign(result, {
|
||||
rawValue: node.rawValue
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = parse;
|
||||
@@ -20,6 +20,9 @@ const parsers = {
|
||||
},
|
||||
get postcss() {
|
||||
return eval("require")("./parser-postcss");
|
||||
},
|
||||
get json() {
|
||||
return eval("require")("./parser-json");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2634,6 +2634,10 @@ function genericPrintNoParens(path, options, print, args) {
|
||||
return path.call(bodyPath => {
|
||||
return printStatementSequence(bodyPath, options, print);
|
||||
}, "body");
|
||||
case "json-identifier":
|
||||
return '"' + n.value + '"';
|
||||
case "json-literal":
|
||||
return n.rawValue;
|
||||
|
||||
default:
|
||||
throw new Error("unknown type: " + JSON.stringify(n.type));
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`multi-line.json 1`] = `
|
||||
{"key1":[true,false,null],"key2":{"key3":[1,2,"3",
|
||||
1e10,1e-3]}}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
{
|
||||
"key1": [true, false, null],
|
||||
"key2": {
|
||||
"key3": [1, 2, "3", 1e10, 1e-3]
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`pass1.json 1`] = `
|
||||
[
|
||||
"JSON Test Pattern pass1",
|
||||
{"object with 1 member":["array with 1 element"]},
|
||||
{},
|
||||
[],
|
||||
-42,
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
{
|
||||
"integer": 1234567890,
|
||||
"real": -9876.543210,
|
||||
"e": 0.123456789e-12,
|
||||
"E": 1.234567890E+34,
|
||||
"": 23456789012E66,
|
||||
"zero": 0,
|
||||
"one": 1,
|
||||
"space": " ",
|
||||
"quote": "\\"",
|
||||
"backslash": "\\\\",
|
||||
"controls": "\\b\\f\\n\\r\\t",
|
||||
"slash": "/ & \\/",
|
||||
"alpha": "abcdefghijklmnopqrstuvwyz",
|
||||
"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
|
||||
"digit": "0123456789",
|
||||
"0123456789": "digit",
|
||||
"special": "\`1~!@#$%^&*()_+-={':[,]}|;.</>?",
|
||||
"hex": "\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A",
|
||||
"true": true,
|
||||
"false": false,
|
||||
"null": null,
|
||||
"array":[ ],
|
||||
"object":{ },
|
||||
"address": "50 St. James Street",
|
||||
"url": "http://www.JSON.org/",
|
||||
"comment": "// /* <!-- --",
|
||||
"# -- --> */": " ",
|
||||
" s p a c e d " :[1,2 , 3
|
||||
|
||||
,
|
||||
|
||||
4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
|
||||
"jsontext": "{\\"object with 1 member\\":[\\"array with 1 element\\"]}",
|
||||
"quotes": "" \\u0022 %22 0x22 034 "",
|
||||
"\\/\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t\`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
|
||||
: "A key can be any string"
|
||||
},
|
||||
0.5 ,98.6
|
||||
,
|
||||
99.44
|
||||
,
|
||||
|
||||
1066,
|
||||
1e1,
|
||||
0.1e1,
|
||||
1e-1,
|
||||
1e00,2e+00,2e-00
|
||||
,"rosebud"]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
[
|
||||
"JSON Test Pattern pass1",
|
||||
{ "object with 1 member": ["array with 1 element"] },
|
||||
{},
|
||||
[],
|
||||
-42,
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
{
|
||||
"integer": 1234567890,
|
||||
"real": -9876.543210,
|
||||
"e": 0.123456789e-12,
|
||||
"E": 1.234567890E+34,
|
||||
"": 23456789012E66,
|
||||
"zero": 0,
|
||||
"one": 1,
|
||||
"space": " ",
|
||||
"quote": "\\"",
|
||||
"backslash": "\\\\",
|
||||
"controls": "\\b\\f\\n\\r\\t",
|
||||
"slash": "/ & \\/",
|
||||
"alpha": "abcdefghijklmnopqrstuvwyz",
|
||||
"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
|
||||
"digit": "0123456789",
|
||||
"0123456789": "digit",
|
||||
"special": "\`1~!@#$%^&*()_+-={':[,]}|;.</>?",
|
||||
"hex": "\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A",
|
||||
"true": true,
|
||||
"false": false,
|
||||
"null": null,
|
||||
"array": [],
|
||||
"object": {},
|
||||
"address": "50 St. James Street",
|
||||
"url": "http://www.JSON.org/",
|
||||
"comment": "// /* <!-- --",
|
||||
"# -- --> */": " ",
|
||||
" s p a c e d ": [1, 2, 3, 4, 5, 6, 7],
|
||||
"compact": [1, 2, 3, 4, 5, 6, 7],
|
||||
"jsontext": "{\\"object with 1 member\\":[\\"array with 1 element\\"]}",
|
||||
"quotes": "" \\u0022 %22 0x22 034 "",
|
||||
"\\/\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t\`1~!@#$%^&*()_+-=[]{}|;:',./<>?": "A key can be any string"
|
||||
},
|
||||
0.5,
|
||||
98.6,
|
||||
99.44,
|
||||
1066,
|
||||
1e1,
|
||||
0.1e1,
|
||||
1e-1,
|
||||
1e00,
|
||||
2e+00,
|
||||
2e-00,
|
||||
"rosebud"
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`single-line.json 1`] = `
|
||||
{"key1":[true,false,null],"key2":{"key3":[1,2,"3",1e10,1e-3]}}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
{ "key1": [true, false, null], "key2": { "key3": [1, 2, "3", 1e10, 1e-3] } }
|
||||
`;
|
||||
@@ -0,0 +1 @@
|
||||
run_spec(__dirname, { parser: "json" });
|
||||
@@ -0,0 +1,2 @@
|
||||
{"key1":[true,false,null],"key2":{"key3":[1,2,"3",
|
||||
1e10,1e-3]}}
|
||||
@@ -0,0 +1,58 @@
|
||||
[
|
||||
"JSON Test Pattern pass1",
|
||||
{"object with 1 member":["array with 1 element"]},
|
||||
{},
|
||||
[],
|
||||
-42,
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
{
|
||||
"integer": 1234567890,
|
||||
"real": -9876.543210,
|
||||
"e": 0.123456789e-12,
|
||||
"E": 1.234567890E+34,
|
||||
"": 23456789012E66,
|
||||
"zero": 0,
|
||||
"one": 1,
|
||||
"space": " ",
|
||||
"quote": "\"",
|
||||
"backslash": "\\",
|
||||
"controls": "\b\f\n\r\t",
|
||||
"slash": "/ & \/",
|
||||
"alpha": "abcdefghijklmnopqrstuvwyz",
|
||||
"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
|
||||
"digit": "0123456789",
|
||||
"0123456789": "digit",
|
||||
"special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
|
||||
"hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
|
||||
"true": true,
|
||||
"false": false,
|
||||
"null": null,
|
||||
"array":[ ],
|
||||
"object":{ },
|
||||
"address": "50 St. James Street",
|
||||
"url": "http://www.JSON.org/",
|
||||
"comment": "// /* <!-- --",
|
||||
"# -- --> */": " ",
|
||||
" s p a c e d " :[1,2 , 3
|
||||
|
||||
,
|
||||
|
||||
4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
|
||||
"jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
|
||||
"quotes": "" \u0022 %22 0x22 034 "",
|
||||
"\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
|
||||
: "A key can be any string"
|
||||
},
|
||||
0.5 ,98.6
|
||||
,
|
||||
99.44
|
||||
,
|
||||
|
||||
1066,
|
||||
1e1,
|
||||
0.1e1,
|
||||
1e-1,
|
||||
1e00,2e+00,2e-00
|
||||
,"rosebud"]
|
||||
@@ -0,0 +1 @@
|
||||
{"key1":[true,false,null],"key2":{"key3":[1,2,"3",1e10,1e-3]}}
|
||||
@@ -2316,6 +2316,10 @@ json-stringify-safe@~5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
|
||||
json-to-ast@2.0.0-alpha1.2:
|
||||
version "2.0.0-alpha1.2"
|
||||
resolved "https://registry.yarnpkg.com/json-to-ast/-/json-to-ast-2.0.0-alpha1.2.tgz#fe27fd89eb639eca1e01f578e79d46ee36e238e8"
|
||||
|
||||
json5@^0.5.0, json5@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
|
||||
|
||||
Reference in new issue
Block a user