.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / arrow-parens.js
1 /**
2  * @fileoverview Rule to require parens in arrow function arguments.
3  * @author Jxck
4  */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Requirements
9 //------------------------------------------------------------------------------
10
11 const astUtils = require("./utils/ast-utils");
12
13 //------------------------------------------------------------------------------
14 // Helpers
15 //------------------------------------------------------------------------------
16
17 /**
18  * Determines if the given arrow function has block body.
19  * @param {ASTNode} node `ArrowFunctionExpression` node.
20  * @returns {boolean} `true` if the function has block body.
21  */
22 function hasBlockBody(node) {
23     return node.body.type === "BlockStatement";
24 }
25
26 //------------------------------------------------------------------------------
27 // Rule Definition
28 //------------------------------------------------------------------------------
29
30 module.exports = {
31     meta: {
32         type: "layout",
33
34         docs: {
35             description: "require parentheses around arrow function arguments",
36             category: "ECMAScript 6",
37             recommended: false,
38             url: "https://eslint.org/docs/rules/arrow-parens"
39         },
40
41         fixable: "code",
42
43         schema: [
44             {
45                 enum: ["always", "as-needed"]
46             },
47             {
48                 type: "object",
49                 properties: {
50                     requireForBlockBody: {
51                         type: "boolean",
52                         default: false
53                     }
54                 },
55                 additionalProperties: false
56             }
57         ],
58
59         messages: {
60             unexpectedParens: "Unexpected parentheses around single function argument.",
61             expectedParens: "Expected parentheses around arrow function argument.",
62
63             unexpectedParensInline: "Unexpected parentheses around single function argument having a body with no curly braces.",
64             expectedParensBlock: "Expected parentheses around arrow function argument having a body with curly braces."
65         }
66     },
67
68     create(context) {
69         const asNeeded = context.options[0] === "as-needed";
70         const requireForBlockBody = asNeeded && context.options[1] && context.options[1].requireForBlockBody === true;
71
72         const sourceCode = context.getSourceCode();
73
74         /**
75          * Finds opening paren of parameters for the given arrow function, if it exists.
76          * It is assumed that the given arrow function has exactly one parameter.
77          * @param {ASTNode} node `ArrowFunctionExpression` node.
78          * @returns {Token|null} the opening paren, or `null` if the given arrow function doesn't have parens of parameters.
79          */
80         function findOpeningParenOfParams(node) {
81             const tokenBeforeParams = sourceCode.getTokenBefore(node.params[0]);
82
83             if (
84                 tokenBeforeParams &&
85                 astUtils.isOpeningParenToken(tokenBeforeParams) &&
86                 node.range[0] <= tokenBeforeParams.range[0]
87             ) {
88                 return tokenBeforeParams;
89             }
90
91             return null;
92         }
93
94         /**
95          * Finds closing paren of parameters for the given arrow function.
96          * It is assumed that the given arrow function has parens of parameters and that it has exactly one parameter.
97          * @param {ASTNode} node `ArrowFunctionExpression` node.
98          * @returns {Token} the closing paren of parameters.
99          */
100         function getClosingParenOfParams(node) {
101             return sourceCode.getTokenAfter(node.params[0], astUtils.isClosingParenToken);
102         }
103
104         /**
105          * Determines whether the given arrow function has comments inside parens of parameters.
106          * It is assumed that the given arrow function has parens of parameters.
107          * @param {ASTNode} node `ArrowFunctionExpression` node.
108          * @param {Token} openingParen Opening paren of parameters.
109          * @returns {boolean} `true` if the function has at least one comment inside of parens of parameters.
110          */
111         function hasCommentsInParensOfParams(node, openingParen) {
112             return sourceCode.commentsExistBetween(openingParen, getClosingParenOfParams(node));
113         }
114
115         /**
116          * Determines whether the given arrow function has unexpected tokens before opening paren of parameters,
117          * in which case it will be assumed that the existing parens of parameters are necessary.
118          * Only tokens within the range of the arrow function (tokens that are part of the arrow function) are taken into account.
119          * Example: <T>(a) => b
120          * @param {ASTNode} node `ArrowFunctionExpression` node.
121          * @param {Token} openingParen Opening paren of parameters.
122          * @returns {boolean} `true` if the function has at least one unexpected token.
123          */
124         function hasUnexpectedTokensBeforeOpeningParen(node, openingParen) {
125             const expectedCount = node.async ? 1 : 0;
126
127             return sourceCode.getFirstToken(node, { skip: expectedCount }) !== openingParen;
128         }
129
130         return {
131             "ArrowFunctionExpression[params.length=1]"(node) {
132                 const shouldHaveParens = !asNeeded || requireForBlockBody && hasBlockBody(node);
133                 const openingParen = findOpeningParenOfParams(node);
134                 const hasParens = openingParen !== null;
135                 const [param] = node.params;
136
137                 if (shouldHaveParens && !hasParens) {
138                     context.report({
139                         node,
140                         messageId: requireForBlockBody ? "expectedParensBlock" : "expectedParens",
141                         loc: param.loc,
142                         *fix(fixer) {
143                             yield fixer.insertTextBefore(param, "(");
144                             yield fixer.insertTextAfter(param, ")");
145                         }
146                     });
147                 }
148
149                 if (
150                     !shouldHaveParens &&
151                     hasParens &&
152                     param.type === "Identifier" &&
153                     !param.typeAnnotation &&
154                     !node.returnType &&
155                     !hasCommentsInParensOfParams(node, openingParen) &&
156                     !hasUnexpectedTokensBeforeOpeningParen(node, openingParen)
157                 ) {
158                     context.report({
159                         node,
160                         messageId: requireForBlockBody ? "unexpectedParensInline" : "unexpectedParens",
161                         loc: param.loc,
162                         *fix(fixer) {
163                             const tokenBeforeOpeningParen = sourceCode.getTokenBefore(openingParen);
164                             const closingParen = getClosingParenOfParams(node);
165
166                             if (
167                                 tokenBeforeOpeningParen &&
168                                 tokenBeforeOpeningParen.range[1] === openingParen.range[0] &&
169                                 !astUtils.canTokensBeAdjacent(tokenBeforeOpeningParen, sourceCode.getFirstToken(param))
170                             ) {
171                                 yield fixer.insertTextBefore(openingParen, " ");
172                             }
173
174                             // remove parens, whitespace inside parens, and possible trailing comma
175                             yield fixer.removeRange([openingParen.range[0], param.range[0]]);
176                             yield fixer.removeRange([param.range[1], closingParen.range[1]]);
177                         }
178                     });
179                 }
180             }
181         };
182     }
183 };