prettier/src/language-js/parser-babylon.js

211 lines
5.6 KiB
JavaScript

"use strict";
// This file is currently named parser-babylon.js to maintain backwards compatibility.
// However, it should be named parser-babel.js in the next major release.
const createError = require("../common/parser-create-error");
const hasPragma = require("./pragma").hasPragma;
const locFns = require("./loc");
const postprocess = require("./postprocess");
function babelOptions(extraOptions, extraPlugins) {
return Object.assign(
{
sourceType: "module",
allowAwaitOutsideFunction: true,
allowImportExportEverywhere: true,
allowReturnOutsideFunction: true,
allowSuperOutsideMethod: true,
allowUndeclaredExports: true,
plugins: [
"jsx",
"doExpressions",
"objectRestSpread",
"classProperties",
"exportDefaultFrom",
"exportNamespaceFrom",
"asyncGenerators",
"functionBind",
"functionSent",
"dynamicImport",
"numericSeparator",
"importMeta",
"optionalCatchBinding",
"optionalChaining",
"classPrivateProperties",
["pipelineOperator", { proposal: "minimal" }],
"nullishCoalescingOperator",
"bigInt",
"throwExpressions",
"logicalAssignment",
"classPrivateMethods",
"v8intrinsic"
].concat(extraPlugins)
},
extraOptions
);
}
function createParse(parseMethod, extraPlugins) {
return (text, parsers, opts) => {
// Inline the require to avoid loading all the JS if we don't use it
const babel = require("@babel/parser");
const combinations = [
babelOptions(
{ strictMode: true },
["decorators-legacy"].concat(extraPlugins)
),
babelOptions(
{ strictMode: false },
["decorators-legacy"].concat(extraPlugins)
),
babelOptions(
{ strictMode: true },
[["decorators", { decoratorsBeforeExport: false }]].concat(extraPlugins)
),
babelOptions(
{ strictMode: false },
[["decorators", { decoratorsBeforeExport: false }]].concat(extraPlugins)
)
];
let ast;
try {
ast = tryCombinations(babel[parseMethod].bind(null, text), combinations);
} catch (error) {
throw createError(
// babel error prints (l:c) with cols that are zero indexed
// so we need our custom error
error.message.replace(/ \(.*\)/, ""),
{
start: {
line: error.loc.line,
column: error.loc.column + 1
}
}
);
}
delete ast.tokens;
return postprocess(ast, Object.assign({}, opts, { originalText: text }));
};
}
const parse = createParse("parse", ["flow"]);
const parseFlow = createParse("parse", [["flow", { all: true }]]);
const parseExpression = createParse("parseExpression");
function tryCombinations(fn, combinations) {
let error;
for (let i = 0; i < combinations.length; i++) {
try {
return fn(combinations[i]);
} catch (_error) {
if (!error) {
error = _error;
}
}
}
throw error;
}
function parseJson(text, parsers, opts) {
const ast = parseExpression(text, parsers, opts);
ast.comments.forEach(assertJsonNode);
assertJsonNode(ast);
return ast;
}
function assertJsonNode(node, parent) {
switch (node.type) {
case "ArrayExpression":
return node.elements.forEach(assertJsonChildNode);
case "ObjectExpression":
return node.properties.forEach(assertJsonChildNode);
case "ObjectProperty":
// istanbul ignore if
if (node.computed) {
throw createJsonError("computed");
}
// istanbul ignore if
if (node.shorthand) {
throw createJsonError("shorthand");
}
return [node.key, node.value].forEach(assertJsonChildNode);
case "UnaryExpression":
switch (node.operator) {
case "+":
case "-":
return assertJsonChildNode(node.argument);
// istanbul ignore next
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;
// istanbul ignore next
default:
throw createJsonError();
}
function assertJsonChildNode(child) {
return assertJsonNode(child, node);
}
// istanbul ignore next
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 = Object.assign({ parse, astFormat: "estree", hasPragma }, locFns);
const babelFlow = Object.assign({}, babel, { parse: parseFlow });
const babelExpression = Object.assign({}, babel, { parse: parseExpression });
// Export as a plugin so we can reuse the same bundle for UMD loading
module.exports = {
parsers: {
babel,
"babel-flow": babelFlow,
// aliased to keep backwards compatibility
babylon: babel,
json: Object.assign({}, babelExpression, {
hasPragma() {
return true;
}
}),
json5: babelExpression,
"json-stringify": Object.assign(
{
parse: parseJson,
astFormat: "estree-json"
},
locFns
),
/** @internal */
__js_expression: babelExpression,
/** for vue filter */
__vue_expression: babelExpression,
/** for vue event binding to handle semicolon */
__vue_event_binding: babel
}
};