|
|
@ -44,7 +44,8 @@ module.exports = { |
|
|
|
properties: { |
|
|
|
conditionalAssign: { type: "boolean" }, |
|
|
|
nestedBinaryExpressions: { type: "boolean" }, |
|
|
|
returnAssign: { type: "boolean" } |
|
|
|
returnAssign: { type: "boolean" }, |
|
|
|
ignoreJSX: { enum: ["none", "all", "single-line", "multi-line"] } |
|
|
|
}, |
|
|
|
additionalProperties: false |
|
|
|
} |
|
|
@ -59,12 +60,16 @@ module.exports = { |
|
|
|
create(context) { |
|
|
|
const sourceCode = context.getSourceCode(); |
|
|
|
|
|
|
|
const tokensToIgnore = new WeakSet(); |
|
|
|
const isParenthesised = astUtils.isParenthesised.bind(astUtils, sourceCode); |
|
|
|
const precedence = astUtils.getPrecedence; |
|
|
|
const ALL_NODES = context.options[0] !== "functions"; |
|
|
|
const EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false; |
|
|
|
const NESTED_BINARY = ALL_NODES && context.options[1] && context.options[1].nestedBinaryExpressions === false; |
|
|
|
const EXCEPT_RETURN_ASSIGN = ALL_NODES && context.options[1] && context.options[1].returnAssign === false; |
|
|
|
const IGNORE_JSX = ALL_NODES && context.options[1] && context.options[1].ignoreJSX; |
|
|
|
const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" }); |
|
|
|
const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" }); |
|
|
|
|
|
|
|
/** |
|
|
|
* Determines if this rule should be enforced for a node given the current configuration. |
|
|
@ -73,6 +78,31 @@ module.exports = { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
function ruleApplies(node) { |
|
|
|
if (node.type === "JSXElement") { |
|
|
|
const isSingleLine = node.loc.start.line === node.loc.end.line; |
|
|
|
|
|
|
|
switch (IGNORE_JSX) { |
|
|
|
|
|
|
|
// Exclude this JSX element from linting
|
|
|
|
case "all": |
|
|
|
return false; |
|
|
|
|
|
|
|
// Exclude this JSX element if it is multi-line element
|
|
|
|
case "multi-line": |
|
|
|
return isSingleLine; |
|
|
|
|
|
|
|
// Exclude this JSX element if it is single-line element
|
|
|
|
case "single-line": |
|
|
|
return !isSingleLine; |
|
|
|
|
|
|
|
// Nothing special to be done for JSX elements
|
|
|
|
case "none": |
|
|
|
break; |
|
|
|
|
|
|
|
// no default
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression"; |
|
|
|
} |
|
|
|
|
|
|
@ -87,8 +117,8 @@ module.exports = { |
|
|
|
nextToken = sourceCode.getTokenAfter(node, 1); |
|
|
|
|
|
|
|
return isParenthesised(node) && previousToken && nextToken && |
|
|
|
previousToken.value === "(" && previousToken.range[1] <= node.range[0] && |
|
|
|
nextToken.value === ")" && nextToken.range[0] >= node.range[1]; |
|
|
|
astUtils.isOpeningParenToken(previousToken) && previousToken.range[1] <= node.range[0] && |
|
|
|
astUtils.isClosingParenToken(nextToken) && nextToken.range[0] >= node.range[1]; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
@ -140,6 +170,19 @@ module.exports = { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Determines if a constructor function is newed-up with parens |
|
|
|
* @param {ASTNode} newExpression - The NewExpression node to be checked. |
|
|
|
* @returns {boolean} True if the constructor is called with parens. |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
function isNewExpressionWithParens(newExpression) { |
|
|
|
const lastToken = sourceCode.getLastToken(newExpression); |
|
|
|
const penultimateToken = sourceCode.getTokenBefore(lastToken); |
|
|
|
|
|
|
|
return newExpression.arguments.length > 0 || astUtils.isOpeningParenToken(penultimateToken) && astUtils.isClosingParenToken(lastToken); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Determines if a node is or contains an assignment expression |
|
|
|
* @param {ASTNode} node - The node to be checked. |
|
|
@ -175,9 +218,9 @@ module.exports = { |
|
|
|
return node.argument && containsAssignment(node.argument); |
|
|
|
} else if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") { |
|
|
|
return containsAssignment(node.body); |
|
|
|
} else { |
|
|
|
return containsAssignment(node); |
|
|
|
} |
|
|
|
return containsAssignment(node); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
@ -196,69 +239,6 @@ module.exports = { |
|
|
|
return hasDoubleExcessParens(node); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Checks whether or not a given node is located at the head of ExpressionStatement. |
|
|
|
* @param {ASTNode} node - A node to check. |
|
|
|
* @returns {boolean} `true` if the node is located at the head of ExpressionStatement. |
|
|
|
*/ |
|
|
|
function isHeadOfExpressionStatement(node) { |
|
|
|
let parent = node.parent; |
|
|
|
|
|
|
|
while (parent) { |
|
|
|
switch (parent.type) { |
|
|
|
case "SequenceExpression": |
|
|
|
if (parent.expressions[0] !== node || isParenthesised(node)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case "UnaryExpression": |
|
|
|
case "UpdateExpression": |
|
|
|
if (parent.prefix || isParenthesised(node)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case "BinaryExpression": |
|
|
|
case "LogicalExpression": |
|
|
|
if (parent.left !== node || isParenthesised(node)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case "ConditionalExpression": |
|
|
|
if (parent.test !== node || isParenthesised(node)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case "CallExpression": |
|
|
|
if (parent.callee !== node || isParenthesised(node)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case "MemberExpression": |
|
|
|
if (parent.object !== node || isParenthesised(node)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case "ExpressionStatement": |
|
|
|
return true; |
|
|
|
|
|
|
|
default: |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
node = parent; |
|
|
|
parent = parent.parent; |
|
|
|
} |
|
|
|
|
|
|
|
/* istanbul ignore next */ |
|
|
|
throw new Error("unreachable"); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Determines whether a node should be preceded by an additional space when removing parens |
|
|
|
* @param {ASTNode} node node to evaluate; must be surrounded by parentheses |
|
|
@ -276,7 +256,7 @@ module.exports = { |
|
|
|
} |
|
|
|
|
|
|
|
// If the parens are preceded by a keyword (e.g. `typeof(0)`), a space should be inserted (`typeof 0`)
|
|
|
|
const precededByKeyword = tokenBeforeLeftParen.type === "Keyword"; |
|
|
|
const precededByIdentiferPart = esUtils.code.isIdentifierPartES6(tokenBeforeLeftParen.value.slice(-1).charCodeAt(0)); |
|
|
|
|
|
|
|
// However, a space should not be inserted unless the first character of the token is an identifier part
|
|
|
|
// e.g. `typeof([])` should be fixed to `typeof[]`
|
|
|
@ -289,7 +269,7 @@ module.exports = { |
|
|
|
const startsWithUnaryPlus = firstToken.type === "Punctuator" && firstToken.value === "+"; |
|
|
|
const startsWithUnaryMinus = firstToken.type === "Punctuator" && firstToken.value === "-"; |
|
|
|
|
|
|
|
return (precededByKeyword && startsWithIdentifierPart) || |
|
|
|
return (precededByIdentiferPart && startsWithIdentifierPart) || |
|
|
|
(precededByUnaryPlus && startsWithUnaryPlus) || |
|
|
|
(precededByUnaryMinus && startsWithUnaryMinus); |
|
|
|
} |
|
|
@ -304,6 +284,10 @@ module.exports = { |
|
|
|
const leftParenToken = sourceCode.getTokenBefore(node); |
|
|
|
const rightParenToken = sourceCode.getTokenAfter(node); |
|
|
|
|
|
|
|
if (tokensToIgnore.has(sourceCode.getFirstToken(node)) && !isParenthesisedTwice(node)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
context.report({ |
|
|
|
node, |
|
|
|
loc: leftParenToken.loc.start, |
|
|
@ -325,7 +309,11 @@ module.exports = { |
|
|
|
* @returns {void} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
function dryUnaryUpdate(node) { |
|
|
|
function checkUnaryUpdate(node) { |
|
|
|
if (node.type === "UnaryExpression" && node.argument.type === "BinaryExpression" && node.argument.operator === "**") { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (hasExcessParens(node.argument) && precedence(node.argument) >= precedence(node)) { |
|
|
|
report(node.argument); |
|
|
|
} |
|
|
@ -337,10 +325,11 @@ module.exports = { |
|
|
|
* @returns {void} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
function dryCallNew(node) { |
|
|
|
function checkCallNew(node) { |
|
|
|
if (hasExcessParens(node.callee) && precedence(node.callee) >= precedence(node) && !( |
|
|
|
node.type === "CallExpression" && |
|
|
|
node.callee.type === "FunctionExpression" && |
|
|
|
(node.callee.type === "FunctionExpression" || |
|
|
|
node.callee.type === "NewExpression" && !isNewExpressionWithParens(node.callee)) && |
|
|
|
|
|
|
|
// One set of parentheses are allowed for a function expression
|
|
|
|
!hasDoubleExcessParens(node.callee) |
|
|
@ -348,12 +337,12 @@ module.exports = { |
|
|
|
report(node.callee); |
|
|
|
} |
|
|
|
if (node.arguments.length === 1) { |
|
|
|
if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= precedence({ type: "AssignmentExpression" })) { |
|
|
|
if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) { |
|
|
|
report(node.arguments[0]); |
|
|
|
} |
|
|
|
} else { |
|
|
|
[].forEach.call(node.arguments, arg => { |
|
|
|
if (hasExcessParens(arg) && precedence(arg) >= precedence({ type: "AssignmentExpression" })) { |
|
|
|
if (hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) { |
|
|
|
report(arg); |
|
|
|
} |
|
|
|
}); |
|
|
@ -366,23 +355,91 @@ module.exports = { |
|
|
|
* @returns {void} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
function dryBinaryLogical(node) { |
|
|
|
function checkBinaryLogical(node) { |
|
|
|
const prec = precedence(node); |
|
|
|
const shouldSkipLeft = NESTED_BINARY && (node.left.type === "BinaryExpression" || node.left.type === "LogicalExpression"); |
|
|
|
const leftPrecedence = precedence(node.left); |
|
|
|
const rightPrecedence = precedence(node.right); |
|
|
|
const isExponentiation = node.operator === "**"; |
|
|
|
const shouldSkipLeft = (NESTED_BINARY && (node.left.type === "BinaryExpression" || node.left.type === "LogicalExpression")) || |
|
|
|
node.left.type === "UnaryExpression" && isExponentiation; |
|
|
|
const shouldSkipRight = NESTED_BINARY && (node.right.type === "BinaryExpression" || node.right.type === "LogicalExpression"); |
|
|
|
|
|
|
|
if (!shouldSkipLeft && hasExcessParens(node.left) && precedence(node.left) >= prec) { |
|
|
|
if (!shouldSkipLeft && hasExcessParens(node.left) && (leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation))) { |
|
|
|
report(node.left); |
|
|
|
} |
|
|
|
if (!shouldSkipRight && hasExcessParens(node.right) && precedence(node.right) > prec) { |
|
|
|
if (!shouldSkipRight && hasExcessParens(node.right) && (rightPrecedence > prec || (rightPrecedence === prec && isExponentiation))) { |
|
|
|
report(node.right); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Check the parentheses around the super class of the given class definition. |
|
|
|
* @param {ASTNode} node The node of class declarations to check. |
|
|
|
* @returns {void} |
|
|
|
*/ |
|
|
|
function checkClass(node) { |
|
|
|
if (!node.superClass) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// If `node.superClass` is a LeftHandSideExpression, parentheses are extra.
|
|
|
|
// Otherwise, parentheses are needed.
|
|
|
|
const hasExtraParens = precedence(node.superClass) > PRECEDENCE_OF_UPDATE_EXPR |
|
|
|
? hasExcessParens(node.superClass) |
|
|
|
: hasDoubleExcessParens(node.superClass); |
|
|
|
|
|
|
|
if (hasExtraParens) { |
|
|
|
report(node.superClass); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Check the parentheses around the argument of the given spread operator. |
|
|
|
* @param {ASTNode} node The node of spread elements/properties to check. |
|
|
|
* @returns {void} |
|
|
|
*/ |
|
|
|
function checkSpreadOperator(node) { |
|
|
|
const hasExtraParens = precedence(node.argument) >= PRECEDENCE_OF_ASSIGNMENT_EXPR |
|
|
|
? hasExcessParens(node.argument) |
|
|
|
: hasDoubleExcessParens(node.argument); |
|
|
|
|
|
|
|
if (hasExtraParens) { |
|
|
|
report(node.argument); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Checks the parentheses for an ExpressionStatement or ExportDefaultDeclaration |
|
|
|
* @param {ASTNode} node The ExpressionStatement.expression or ExportDefaultDeclaration.declaration node |
|
|
|
* @returns {void} |
|
|
|
*/ |
|
|
|
function checkExpressionOrExportStatement(node) { |
|
|
|
const firstToken = isParenthesised(node) ? sourceCode.getTokenBefore(node) : sourceCode.getFirstToken(node); |
|
|
|
const secondToken = sourceCode.getTokenAfter(firstToken, astUtils.isNotOpeningParenToken); |
|
|
|
|
|
|
|
if ( |
|
|
|
astUtils.isOpeningParenToken(firstToken) && |
|
|
|
( |
|
|
|
astUtils.isOpeningBraceToken(secondToken) || |
|
|
|
secondToken.type === "Keyword" && ( |
|
|
|
secondToken.value === "function" || |
|
|
|
secondToken.value === "class" || |
|
|
|
secondToken.value === "let" && astUtils.isOpeningBracketToken(sourceCode.getTokenAfter(secondToken)) |
|
|
|
) |
|
|
|
) |
|
|
|
) { |
|
|
|
tokensToIgnore.add(secondToken); |
|
|
|
} |
|
|
|
|
|
|
|
if (hasExcessParens(node)) { |
|
|
|
report(node); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return { |
|
|
|
ArrayExpression(node) { |
|
|
|
[].forEach.call(node.elements, e => { |
|
|
|
if (e && hasExcessParens(e) && precedence(e) >= precedence({ type: "AssignmentExpression" })) { |
|
|
|
if (e && hasExcessParens(e) && precedence(e) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) { |
|
|
|
report(e); |
|
|
|
} |
|
|
|
}); |
|
|
@ -394,13 +451,13 @@ module.exports = { |
|
|
|
} |
|
|
|
|
|
|
|
if (node.body.type !== "BlockStatement") { |
|
|
|
if (sourceCode.getFirstToken(node.body).value !== "{" && hasExcessParens(node.body) && precedence(node.body) >= precedence({ type: "AssignmentExpression" })) { |
|
|
|
report(node.body); |
|
|
|
return; |
|
|
|
} |
|
|
|
const firstBodyToken = sourceCode.getFirstToken(node.body, astUtils.isNotOpeningParenToken); |
|
|
|
const tokenBeforeFirst = sourceCode.getTokenBefore(firstBodyToken); |
|
|
|
|
|
|
|
// Object literals *must* be parenthesised
|
|
|
|
if (node.body.type === "ObjectExpression" && hasDoubleExcessParens(node.body)) { |
|
|
|
if (astUtils.isOpeningParenToken(tokenBeforeFirst) && astUtils.isOpeningBraceToken(firstBodyToken)) { |
|
|
|
tokensToIgnore.add(firstBodyToken); |
|
|
|
} |
|
|
|
if (hasExcessParens(node.body) && precedence(node.body) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) { |
|
|
|
report(node.body); |
|
|
|
} |
|
|
|
} |
|
|
@ -416,8 +473,8 @@ module.exports = { |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
BinaryExpression: dryBinaryLogical, |
|
|
|
CallExpression: dryCallNew, |
|
|
|
BinaryExpression: checkBinaryLogical, |
|
|
|
CallExpression: checkCallNew, |
|
|
|
|
|
|
|
ConditionalExpression(node) { |
|
|
|
if (isReturnAssignException(node)) { |
|
|
@ -428,11 +485,11 @@ module.exports = { |
|
|
|
report(node.test); |
|
|
|
} |
|
|
|
|
|
|
|
if (hasExcessParens(node.consequent) && precedence(node.consequent) >= precedence({ type: "AssignmentExpression" })) { |
|
|
|
if (hasExcessParens(node.consequent) && precedence(node.consequent) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) { |
|
|
|
report(node.consequent); |
|
|
|
} |
|
|
|
|
|
|
|
if (hasExcessParens(node.alternate) && precedence(node.alternate) >= precedence({ type: "AssignmentExpression" })) { |
|
|
|
if (hasExcessParens(node.alternate) && precedence(node.alternate) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) { |
|
|
|
report(node.alternate); |
|
|
|
} |
|
|
|
}, |
|
|
@ -443,27 +500,8 @@ module.exports = { |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
ExpressionStatement(node) { |
|
|
|
if (hasExcessParens(node.expression)) { |
|
|
|
const firstTokens = sourceCode.getFirstTokens(node.expression, 2); |
|
|
|
const firstToken = firstTokens[0]; |
|
|
|
const secondToken = firstTokens[1]; |
|
|
|
|
|
|
|
if ( |
|
|
|
!firstToken || |
|
|
|
firstToken.value !== "{" && |
|
|
|
firstToken.value !== "function" && |
|
|
|
firstToken.value !== "class" && |
|
|
|
( |
|
|
|
firstToken.value !== "let" || |
|
|
|
!secondToken || |
|
|
|
secondToken.value !== "[" |
|
|
|
) |
|
|
|
) { |
|
|
|
report(node.expression); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
ExportDefaultDeclaration: node => checkExpressionOrExportStatement(node.declaration), |
|
|
|
ExpressionStatement: node => checkExpressionOrExportStatement(node.expression), |
|
|
|
|
|
|
|
ForInStatement(node) { |
|
|
|
if (hasExcessParens(node.right)) { |
|
|
@ -497,7 +535,7 @@ module.exports = { |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
LogicalExpression: dryBinaryLogical, |
|
|
|
LogicalExpression: checkBinaryLogical, |
|
|
|
|
|
|
|
MemberExpression(node) { |
|
|
|
if ( |
|
|
@ -506,19 +544,11 @@ module.exports = { |
|
|
|
( |
|
|
|
node.computed || |
|
|
|
!( |
|
|
|
(node.object.type === "Literal" && |
|
|
|
typeof node.object.value === "number" && |
|
|
|
astUtils.isDecimalInteger(node.object)) |
|
|
|
|| |
|
|
|
astUtils.isDecimalInteger(node.object) || |
|
|
|
|
|
|
|
// RegExp literal is allowed to have parens (#1589)
|
|
|
|
(node.object.type === "Literal" && node.object.regex) |
|
|
|
) |
|
|
|
) && |
|
|
|
!( |
|
|
|
(node.object.type === "FunctionExpression" || node.object.type === "ClassExpression") && |
|
|
|
isHeadOfExpressionStatement(node) && |
|
|
|
!hasDoubleExcessParens(node.object) |
|
|
|
) |
|
|
|
) { |
|
|
|
report(node.object); |
|
|
@ -528,13 +558,13 @@ module.exports = { |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
NewExpression: dryCallNew, |
|
|
|
NewExpression: checkCallNew, |
|
|
|
|
|
|
|
ObjectExpression(node) { |
|
|
|
[].forEach.call(node.properties, e => { |
|
|
|
const v = e.value; |
|
|
|
|
|
|
|
if (v && hasExcessParens(v) && precedence(v) >= precedence({ type: "AssignmentExpression" })) { |
|
|
|
if (v && hasExcessParens(v) && precedence(v) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) { |
|
|
|
report(v); |
|
|
|
} |
|
|
|
}); |
|
|
@ -584,13 +614,13 @@ module.exports = { |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
UnaryExpression: dryUnaryUpdate, |
|
|
|
UpdateExpression: dryUnaryUpdate, |
|
|
|
AwaitExpression: dryUnaryUpdate, |
|
|
|
UnaryExpression: checkUnaryUpdate, |
|
|
|
UpdateExpression: checkUnaryUpdate, |
|
|
|
AwaitExpression: checkUnaryUpdate, |
|
|
|
|
|
|
|
VariableDeclarator(node) { |
|
|
|
if (node.init && hasExcessParens(node.init) && |
|
|
|
precedence(node.init) >= precedence({ type: "AssignmentExpression" }) && |
|
|
|
precedence(node.init) >= PRECEDENCE_OF_ASSIGNMENT_EXPR && |
|
|
|
|
|
|
|
// RegExp literal is allowed to have parens (#1589)
|
|
|
|
!(node.init.type === "Literal" && node.init.regex)) { |
|
|
@ -620,7 +650,14 @@ module.exports = { |
|
|
|
report(node.argument); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
ClassDeclaration: checkClass, |
|
|
|
ClassExpression: checkClass, |
|
|
|
|
|
|
|
SpreadElement: checkSpreadOperator, |
|
|
|
SpreadProperty: checkSpreadOperator, |
|
|
|
ExperimentalSpreadProperty: checkSpreadOperator |
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|