Extract json parser (#10405)

This commit is contained in:
fisker Cheung authored and GitHub committed 2021-02-28 11:38:21 +08:00
1 parent d800fd001e
commit c163d346d4
4 files changed
+136 -117

No files matched your search

+1
View File
@@ -137,3 +137,4 @@ overrides:
- "src/language-js/parser-babel.js"
- "src/language-js/parser-meriyah.js"
- "src/language-js/pragma.js"
- "src/language-js/parser/json.js"
+4 -117
View File
@@ -1,7 +1,6 @@
"use strict";
const flatten = require("lodash/flatten");
const createError = require("../common/parser-create-error");
const tryCombinations = require("../utils/try-combinations");
const {
getNextNonSpaceNonCommentCharacterIndexWithStartIndex,
@@ -9,6 +8,8 @@ const {
} = require("../common/util");
const postprocess = require("./parse-postprocess");
const createParser = require("./parser/create-parser");
const createBabelParseError = require("./parser/create-babel-parse-error");
const jsonParsers = require("./parser/json");
const parseOptions = {
sourceType: "module",
@@ -87,19 +88,6 @@ function parseWithOptions(parseMethod, text, options) {
return ast;
}
function createParseError(error) {
// babel error prints (l:c) with cols that are zero indexed
// so we need our custom error
const { message, loc } = error;
return createError(message.replace(/ \(.*\)/, ""), {
start: {
line: loc ? loc.line : 0,
column: loc ? loc.column + 1 : 0,
},
});
}
function createParse(parseMethod, ...pluginCombinations) {
const commonPlugins = parseOptions.plugins;
pluginCombinations =
@@ -135,7 +123,7 @@ function createParse(parseMethod, ...pluginCombinations) {
);
if (!ast) {
throw createParseError(error);
throw createBabelParseError(error);
}
return postprocess(ast, { ...opts, originalText: text });
@@ -153,7 +141,6 @@ const parseTypeScript = createParse(
["typescript"]
);
const parseExpression = createParse("parseExpression", ["jsx"]);
const parseJson = createJsonParse();
const messagesShouldThrow = new Set([
// TSErrors.UnexpectedTypeAnnotation
@@ -182,96 +169,6 @@ function shouldRethrowRecoveredError(error) {
return messagesShouldThrow.has(message);
}
function createJsonParse(options = {}) {
const { allowComments = true } = options;
return function parse(text /*, parsers, options*/) {
let ast;
try {
ast = require("@babel/parser").parseExpression(text, {
tokens: true,
ranges: true,
});
} catch (error) {
throw createParseError(error);
}
if (!allowComments) {
// @ts-ignore
for (const comment of ast.comments) {
assertJsonNode(comment);
}
}
assertJsonNode(ast);
return ast;
};
}
function assertJsonNode(node, parent) {
switch (node.type) {
case "ArrayExpression":
for (const element of node.elements) {
assertJsonChildNode(element);
}
return;
case "ObjectExpression":
for (const property of node.properties) {
assertJsonChildNode(property);
}
return;
case "ObjectProperty":
if (node.computed) {
throw createJsonError("computed");
}
if (node.shorthand) {
throw createJsonError("shorthand");
}
assertJsonChildNode(node.key);
assertJsonChildNode(node.value);
return;
case "UnaryExpression":
switch (node.operator) {
case "+":
case "-":
return assertJsonChildNode(node.argument);
default:
throw createJsonError("operator");
}
case "Identifier":
if (parent && parent.type === "ObjectProperty" && parent.key === node) {
return;
}
throw createJsonError();
case "NullLiteral":
case "BooleanLiteral":
case "NumericLiteral":
case "StringLiteral":
return;
default:
throw createJsonError();
}
function assertJsonChildNode(child) {
return assertJsonNode(child, node);
}
function createJsonError(attribute) {
const name = !attribute
? node.type
: `${node.type} with ${attribute}=${JSON.stringify(node[attribute])}`;
return createError(`${name} is not allowed in JSON.`, {
start: {
line: node.loc.start.line,
column: node.loc.start.column + 1,
},
});
}
}
const babel = createParser(parse);
const babelExpression = createParser(parseExpression);
@@ -281,17 +178,7 @@ module.exports = {
babel,
"babel-flow": createParser(parseFlow),
"babel-ts": createParser(parseTypeScript),
json: createParser({
parse: parseJson,
hasPragma() {
return true;
},
}),
json5: createParser(parseJson),
"json-stringify": createParser({
parse: createJsonParse({ allowComments: false }),
astFormat: "estree-json",
}),
...jsonParsers,
/** @internal */
__js_expression: babelExpression,
/** for vue filter */
@@ -0,0 +1,18 @@
"use strict";
const createError = require("../../common/parser-create-error");
function createBabelParseError(error) {
// babel error prints (l:c) with cols that are zero indexed
// so we need our custom error
const { message, loc } = error;
return createError(message.replace(/ \(.*\)/, ""), {
start: {
line: loc ? loc.line : 0,
column: loc ? loc.column + 1 : 0,
},
});
}
module.exports = createBabelParseError;
+113
View File
@@ -0,0 +1,113 @@
"use strict";
const createError = require("../../common/parser-create-error");
const createParser = require("./create-parser");
const createBabelParseError = require("./create-babel-parse-error");
function createJsonParse(options = {}) {
const { allowComments = true } = options;
return function parse(text /*, parsers, options*/) {
let ast;
try {
ast = require("@babel/parser").parseExpression(text, {
tokens: true,
ranges: true,
});
} catch (error) {
throw createBabelParseError(error);
}
if (!allowComments) {
// @ts-ignore
for (const comment of ast.comments) {
assertJsonNode(comment);
}
}
assertJsonNode(ast);
return ast;
};
}
function assertJsonNode(node, parent) {
switch (node.type) {
case "ArrayExpression":
for (const element of node.elements) {
assertJsonChildNode(element);
}
return;
case "ObjectExpression":
for (const property of node.properties) {
assertJsonChildNode(property);
}
return;
case "ObjectProperty":
if (node.computed) {
throw createJsonError("computed");
}
if (node.shorthand) {
throw createJsonError("shorthand");
}
assertJsonChildNode(node.key);
assertJsonChildNode(node.value);
return;
case "UnaryExpression":
switch (node.operator) {
case "+":
case "-":
return assertJsonChildNode(node.argument);
default:
throw createJsonError("operator");
}
case "Identifier":
if (parent && parent.type === "ObjectProperty" && parent.key === node) {
return;
}
throw createJsonError();
case "NullLiteral":
case "BooleanLiteral":
case "NumericLiteral":
case "StringLiteral":
return;
default:
throw createJsonError();
}
function assertJsonChildNode(child) {
return assertJsonNode(child, node);
}
function createJsonError(attribute) {
const name = !attribute
? node.type
: `${node.type} with ${attribute}=${JSON.stringify(node[attribute])}`;
return createError(`${name} is not allowed in JSON.`, {
start: {
line: node.loc.start.line,
column: node.loc.start.column + 1,
},
});
}
}
const parseJson = createJsonParse();
const jsonParsers = {
json: createParser({
parse: parseJson,
hasPragma() {
return true;
},
}),
json5: createParser(parseJson),
"json-stringify": createParser({
parse: createJsonParse({ allowComments: false }),
astFormat: "estree-json",
}),
};
module.exports = jsonParsers;