| /** | |
| * @fileoverview Rule to require parens in arrow function arguments. | |
| * @author Jxck | |
| */ | |
| "use strict"; | |
| //------------------------------------------------------------------------------ | |
| // Requirements | |
| //------------------------------------------------------------------------------ | |
| const astUtils = require("./utils/ast-utils"); | |
| //------------------------------------------------------------------------------ | |
| // Helpers | |
| //------------------------------------------------------------------------------ | |
| /** | |
| * Get location should be reported by AST node. | |
| * | |
| * @param {ASTNode} node AST Node. | |
| * @returns {Location} Location information. | |
| */ | |
| function getLocation(node) { | |
| return { | |
| start: node.params[0].loc.start, | |
| end: node.params[node.params.length - 1].loc.end | |
| }; | |
| } | |
| //------------------------------------------------------------------------------ | |
| // 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(); | |
| /** | |
| * Determines whether a arrow function argument end with `)` | |
| * @param {ASTNode} node The arrow function node. | |
| * @returns {void} | |
| */ | |
| function parens(node) { | |
| const isAsync = node.async; | |
| const firstTokenOfParam = sourceCode.getFirstToken(node, isAsync ? 1 : 0); | |
| /** | |
| * Remove the parenthesis around a parameter | |
| * @param {Fixer} fixer Fixer | |
| * @returns {string} fixed parameter | |
| */ | |
| function fixParamsWithParenthesis(fixer) { | |
| const paramToken = sourceCode.getTokenAfter(firstTokenOfParam); | |
| /* | |
| * ES8 allows Trailing commas in function parameter lists and calls | |
| * https://github.com/eslint/eslint/issues/8834 | |
| */ | |
| const closingParenToken = sourceCode.getTokenAfter(paramToken, astUtils.isClosingParenToken); | |
| const asyncToken = isAsync ? sourceCode.getTokenBefore(firstTokenOfParam) : null; | |
| const shouldAddSpaceForAsync = asyncToken && (asyncToken.range[1] === firstTokenOfParam.range[0]); | |
| return fixer.replaceTextRange([ | |
| firstTokenOfParam.range[0], | |
| closingParenToken.range[1] | |
| ], `${shouldAddSpaceForAsync ? " " : ""}${paramToken.value}`); | |
| } | |
| // "as-needed", { "requireForBlockBody": true }: x => x | |
| if ( | |
| requireForBlockBody && | |
| node.params.length === 1 && | |
| node.params[0].type === "Identifier" && | |
| !node.params[0].typeAnnotation && | |
| node.body.type !== "BlockStatement" && | |
| !node.returnType | |
| ) { | |
| if (astUtils.isOpeningParenToken(firstTokenOfParam)) { | |
| context.report({ | |
| node, | |
| messageId: "unexpectedParensInline", | |
| loc: getLocation(node), | |
| fix: fixParamsWithParenthesis | |
| }); | |
| } | |
| return; | |
| } | |
| if ( | |
| requireForBlockBody && | |
| node.body.type === "BlockStatement" | |
| ) { | |
| if (!astUtils.isOpeningParenToken(firstTokenOfParam)) { | |
| context.report({ | |
| node, | |
| messageId: "expectedParensBlock", | |
| loc: getLocation(node), | |
| fix(fixer) { | |
| return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`); | |
| } | |
| }); | |
| } | |
| return; | |
| } | |
| // "as-needed": x => x | |
| if (asNeeded && | |
| node.params.length === 1 && | |
| node.params[0].type === "Identifier" && | |
| !node.params[0].typeAnnotation && | |
| !node.returnType | |
| ) { | |
| if (astUtils.isOpeningParenToken(firstTokenOfParam)) { | |
| context.report({ | |
| node, | |
| messageId: "unexpectedParens", | |
| loc: getLocation(node), | |
| fix: fixParamsWithParenthesis | |
| }); | |
| } | |
| return; | |
| } | |
| if (firstTokenOfParam.type === "Identifier") { | |
| const after = sourceCode.getTokenAfter(firstTokenOfParam); | |
| // (x) => x | |
| if (after.value !== ")") { | |
| context.report({ | |
| node, | |
| messageId: "expectedParens", | |
| loc: getLocation(node), | |
| fix(fixer) { | |
| return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`); | |
| } | |
| }); | |
| } | |
| } | |
| } | |
| return { | |
| ArrowFunctionExpression: parens | |
| }; | |
| } | |
| }; |