]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
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 | * Get location should be reported by AST node. | |
19 | * @param {ASTNode} node AST Node. | |
20 | * @returns {Location} Location information. | |
21 | */ | |
22 | function getLocation(node) { | |
23 | return { | |
24 | start: node.params[0].loc.start, | |
25 | end: node.params[node.params.length - 1].loc.end | |
26 | }; | |
27 | } | |
28 | ||
29 | //------------------------------------------------------------------------------ | |
30 | // Rule Definition | |
31 | //------------------------------------------------------------------------------ | |
32 | ||
33 | module.exports = { | |
34 | meta: { | |
35 | type: "layout", | |
36 | ||
37 | docs: { | |
38 | description: "require parentheses around arrow function arguments", | |
39 | category: "ECMAScript 6", | |
40 | recommended: false, | |
41 | url: "https://eslint.org/docs/rules/arrow-parens" | |
42 | }, | |
43 | ||
44 | fixable: "code", | |
45 | ||
46 | schema: [ | |
47 | { | |
48 | enum: ["always", "as-needed"] | |
49 | }, | |
50 | { | |
51 | type: "object", | |
52 | properties: { | |
53 | requireForBlockBody: { | |
54 | type: "boolean", | |
55 | default: false | |
56 | } | |
57 | }, | |
58 | additionalProperties: false | |
59 | } | |
60 | ], | |
61 | ||
62 | messages: { | |
63 | unexpectedParens: "Unexpected parentheses around single function argument.", | |
64 | expectedParens: "Expected parentheses around arrow function argument.", | |
65 | ||
66 | unexpectedParensInline: "Unexpected parentheses around single function argument having a body with no curly braces.", | |
67 | expectedParensBlock: "Expected parentheses around arrow function argument having a body with curly braces." | |
68 | } | |
69 | }, | |
70 | ||
71 | create(context) { | |
72 | const asNeeded = context.options[0] === "as-needed"; | |
73 | const requireForBlockBody = asNeeded && context.options[1] && context.options[1].requireForBlockBody === true; | |
74 | ||
75 | const sourceCode = context.getSourceCode(); | |
76 | ||
77 | /** | |
78 | * Determines whether a arrow function argument end with `)` | |
79 | * @param {ASTNode} node The arrow function node. | |
80 | * @returns {void} | |
81 | */ | |
82 | function parens(node) { | |
83 | const isAsync = node.async; | |
84 | const firstTokenOfParam = sourceCode.getFirstToken(node, isAsync ? 1 : 0); | |
85 | ||
86 | /** | |
87 | * Remove the parenthesis around a parameter | |
88 | * @param {Fixer} fixer Fixer | |
89 | * @returns {string} fixed parameter | |
90 | */ | |
91 | function fixParamsWithParenthesis(fixer) { | |
92 | const paramToken = sourceCode.getTokenAfter(firstTokenOfParam); | |
93 | ||
94 | /* | |
95 | * ES8 allows Trailing commas in function parameter lists and calls | |
96 | * https://github.com/eslint/eslint/issues/8834 | |
97 | */ | |
98 | const closingParenToken = sourceCode.getTokenAfter(paramToken, astUtils.isClosingParenToken); | |
99 | const asyncToken = isAsync ? sourceCode.getTokenBefore(firstTokenOfParam) : null; | |
100 | const shouldAddSpaceForAsync = asyncToken && (asyncToken.range[1] === firstTokenOfParam.range[0]); | |
101 | ||
102 | return fixer.replaceTextRange([ | |
103 | firstTokenOfParam.range[0], | |
104 | closingParenToken.range[1] | |
105 | ], `${shouldAddSpaceForAsync ? " " : ""}${paramToken.value}`); | |
106 | } | |
107 | ||
ebb53d86 TL |
108 | /** |
109 | * Checks whether there are comments inside the params or not. | |
110 | * @returns {boolean} `true` if there are comments inside of parens, else `false` | |
111 | */ | |
112 | function hasCommentsInParens() { | |
113 | if (astUtils.isOpeningParenToken(firstTokenOfParam)) { | |
114 | const closingParenToken = sourceCode.getTokenAfter(node.params[0], astUtils.isClosingParenToken); | |
115 | ||
116 | return closingParenToken && sourceCode.commentsExistBetween(firstTokenOfParam, closingParenToken); | |
117 | } | |
118 | return false; | |
119 | ||
120 | } | |
121 | ||
122 | if (hasCommentsInParens()) { | |
123 | return; | |
124 | } | |
125 | ||
eb39fafa DC |
126 | // "as-needed", { "requireForBlockBody": true }: x => x |
127 | if ( | |
128 | requireForBlockBody && | |
eb39fafa DC |
129 | node.params[0].type === "Identifier" && |
130 | !node.params[0].typeAnnotation && | |
131 | node.body.type !== "BlockStatement" && | |
132 | !node.returnType | |
133 | ) { | |
134 | if (astUtils.isOpeningParenToken(firstTokenOfParam)) { | |
135 | context.report({ | |
136 | node, | |
137 | messageId: "unexpectedParensInline", | |
138 | loc: getLocation(node), | |
139 | fix: fixParamsWithParenthesis | |
140 | }); | |
141 | } | |
142 | return; | |
143 | } | |
144 | ||
145 | if ( | |
146 | requireForBlockBody && | |
147 | node.body.type === "BlockStatement" | |
148 | ) { | |
149 | if (!astUtils.isOpeningParenToken(firstTokenOfParam)) { | |
150 | context.report({ | |
151 | node, | |
152 | messageId: "expectedParensBlock", | |
153 | loc: getLocation(node), | |
154 | fix(fixer) { | |
155 | return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`); | |
156 | } | |
157 | }); | |
158 | } | |
159 | return; | |
160 | } | |
161 | ||
162 | // "as-needed": x => x | |
163 | if (asNeeded && | |
eb39fafa DC |
164 | node.params[0].type === "Identifier" && |
165 | !node.params[0].typeAnnotation && | |
166 | !node.returnType | |
167 | ) { | |
168 | if (astUtils.isOpeningParenToken(firstTokenOfParam)) { | |
169 | context.report({ | |
170 | node, | |
171 | messageId: "unexpectedParens", | |
172 | loc: getLocation(node), | |
173 | fix: fixParamsWithParenthesis | |
174 | }); | |
175 | } | |
176 | return; | |
177 | } | |
178 | ||
179 | if (firstTokenOfParam.type === "Identifier") { | |
180 | const after = sourceCode.getTokenAfter(firstTokenOfParam); | |
181 | ||
182 | // (x) => x | |
183 | if (after.value !== ")") { | |
184 | context.report({ | |
185 | node, | |
186 | messageId: "expectedParens", | |
187 | loc: getLocation(node), | |
188 | fix(fixer) { | |
189 | return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`); | |
190 | } | |
191 | }); | |
192 | } | |
193 | } | |
194 | } | |
195 | ||
196 | return { | |
ebb53d86 | 197 | "ArrowFunctionExpression[params.length=1]": parens |
eb39fafa DC |
198 | }; |
199 | } | |
200 | }; |