Spaces:
Runtime error
Runtime error
/** | |
* @fileoverview Rule to require parens in arrow function arguments. | |
* @author Jxck | |
*/ | |
; | |
//------------------------------------------------------------------------------ | |
// Requirements | |
//------------------------------------------------------------------------------ | |
const astUtils = require("./utils/ast-utils"); | |
//------------------------------------------------------------------------------ | |
// Helpers | |
//------------------------------------------------------------------------------ | |
/** | |
* Determines if the given arrow function has block body. | |
* @param {ASTNode} node `ArrowFunctionExpression` node. | |
* @returns {boolean} `true` if the function has block body. | |
*/ | |
function hasBlockBody(node) { | |
return node.body.type === "BlockStatement"; | |
} | |
//------------------------------------------------------------------------------ | |
// Rule Definition | |
//------------------------------------------------------------------------------ | |
module.exports = { | |
meta: { | |
type: "layout", | |
docs: { | |
description: "require parentheses around arrow function arguments", | |
category: "ECMAScript 6", | |
recommended: false, | |
url: "https://eslint.org/docs/rules/arrow-parens" | |
}, | |
fixable: "code", | |
schema: [ | |
{ | |
enum: ["always", "as-needed"] | |
}, | |
{ | |
type: "object", | |
properties: { | |
requireForBlockBody: { | |
type: "boolean", | |
default: false | |
} | |
}, | |
additionalProperties: false | |
} | |
], | |
messages: { | |
unexpectedParens: "Unexpected parentheses around single function argument.", | |
expectedParens: "Expected parentheses around arrow function argument.", | |
unexpectedParensInline: "Unexpected parentheses around single function argument having a body with no curly braces.", | |
expectedParensBlock: "Expected parentheses around arrow function argument having a body with curly braces." | |
} | |
}, | |
create(context) { | |
const asNeeded = context.options[0] === "as-needed"; | |
const requireForBlockBody = asNeeded && context.options[1] && context.options[1].requireForBlockBody === true; | |
const sourceCode = context.getSourceCode(); | |
/** | |
* Finds opening paren of parameters for the given arrow function, if it exists. | |
* It is assumed that the given arrow function has exactly one parameter. | |
* @param {ASTNode} node `ArrowFunctionExpression` node. | |
* @returns {Token|null} the opening paren, or `null` if the given arrow function doesn't have parens of parameters. | |
*/ | |
function findOpeningParenOfParams(node) { | |
const tokenBeforeParams = sourceCode.getTokenBefore(node.params[0]); | |
if ( | |
tokenBeforeParams && | |
astUtils.isOpeningParenToken(tokenBeforeParams) && | |
node.range[0] <= tokenBeforeParams.range[0] | |
) { | |
return tokenBeforeParams; | |
} | |
return null; | |
} | |
/** | |
* Finds closing paren of parameters for the given arrow function. | |
* It is assumed that the given arrow function has parens of parameters and that it has exactly one parameter. | |
* @param {ASTNode} node `ArrowFunctionExpression` node. | |
* @returns {Token} the closing paren of parameters. | |
*/ | |
function getClosingParenOfParams(node) { | |
return sourceCode.getTokenAfter(node.params[0], astUtils.isClosingParenToken); | |
} | |
/** | |
* Determines whether the given arrow function has comments inside parens of parameters. | |
* It is assumed that the given arrow function has parens of parameters. | |
* @param {ASTNode} node `ArrowFunctionExpression` node. | |
* @param {Token} openingParen Opening paren of parameters. | |
* @returns {boolean} `true` if the function has at least one comment inside of parens of parameters. | |
*/ | |
function hasCommentsInParensOfParams(node, openingParen) { | |
return sourceCode.commentsExistBetween(openingParen, getClosingParenOfParams(node)); | |
} | |
/** | |
* Determines whether the given arrow function has unexpected tokens before opening paren of parameters, | |
* in which case it will be assumed that the existing parens of parameters are necessary. | |
* Only tokens within the range of the arrow function (tokens that are part of the arrow function) are taken into account. | |
* Example: <T>(a) => b | |
* @param {ASTNode} node `ArrowFunctionExpression` node. | |
* @param {Token} openingParen Opening paren of parameters. | |
* @returns {boolean} `true` if the function has at least one unexpected token. | |
*/ | |
function hasUnexpectedTokensBeforeOpeningParen(node, openingParen) { | |
const expectedCount = node.async ? 1 : 0; | |
return sourceCode.getFirstToken(node, { skip: expectedCount }) !== openingParen; | |
} | |
return { | |
"ArrowFunctionExpression[params.length=1]"(node) { | |
const shouldHaveParens = !asNeeded || requireForBlockBody && hasBlockBody(node); | |
const openingParen = findOpeningParenOfParams(node); | |
const hasParens = openingParen !== null; | |
const [param] = node.params; | |
if (shouldHaveParens && !hasParens) { | |
context.report({ | |
node, | |
messageId: requireForBlockBody ? "expectedParensBlock" : "expectedParens", | |
loc: param.loc, | |
*fix(fixer) { | |
yield fixer.insertTextBefore(param, "("); | |
yield fixer.insertTextAfter(param, ")"); | |
} | |
}); | |
} | |
if ( | |
!shouldHaveParens && | |
hasParens && | |
param.type === "Identifier" && | |
!param.typeAnnotation && | |
!node.returnType && | |
!hasCommentsInParensOfParams(node, openingParen) && | |
!hasUnexpectedTokensBeforeOpeningParen(node, openingParen) | |
) { | |
context.report({ | |
node, | |
messageId: requireForBlockBody ? "unexpectedParensInline" : "unexpectedParens", | |
loc: param.loc, | |
*fix(fixer) { | |
const tokenBeforeOpeningParen = sourceCode.getTokenBefore(openingParen); | |
const closingParen = getClosingParenOfParams(node); | |
if ( | |
tokenBeforeOpeningParen && | |
tokenBeforeOpeningParen.range[1] === openingParen.range[0] && | |
!astUtils.canTokensBeAdjacent(tokenBeforeOpeningParen, sourceCode.getFirstToken(param)) | |
) { | |
yield fixer.insertTextBefore(openingParen, " "); | |
} | |
// remove parens, whitespace inside parens, and possible trailing comma | |
yield fixer.removeRange([openingParen.range[0], param.range[0]]); | |
yield fixer.removeRange([param.range[1], closingParen.range[1]]); | |
} | |
}); | |
} | |
} | |
}; | |
} | |
}; | |