123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- /**
- * @fileoverview Rule to require parens in arrow function arguments.
- * @author Jxck
- */
- "use strict";
- //------------------------------------------------------------------------------
- // 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
- //------------------------------------------------------------------------------
- /** @type {import('../shared/types').Rule} */
- module.exports = {
- meta: {
- type: "layout",
- docs: {
- description: "Require parentheses around arrow function arguments",
- 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]]);
- }
- });
- }
- }
- };
- }
- };
|