| 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]]);                        }                    });                }            }        };    }};
 |