.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / space-unary-ops.js
1 /**
2  * @fileoverview This rule should require or disallow spaces before or after unary operations.
3  * @author Marcin Kumorek
4  */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Requirements
9 //------------------------------------------------------------------------------
10
11 const astUtils = require("./utils/ast-utils");
12
13 //------------------------------------------------------------------------------
14 // Rule Definition
15 //------------------------------------------------------------------------------
16
17 module.exports = {
18     meta: {
19         type: "layout",
20
21         docs: {
22             description: "enforce consistent spacing before or after unary operators",
23             category: "Stylistic Issues",
24             recommended: false,
25             url: "https://eslint.org/docs/rules/space-unary-ops"
26         },
27
28         fixable: "whitespace",
29
30         schema: [
31             {
32                 type: "object",
33                 properties: {
34                     words: {
35                         type: "boolean",
36                         default: true
37                     },
38                     nonwords: {
39                         type: "boolean",
40                         default: false
41                     },
42                     overrides: {
43                         type: "object",
44                         additionalProperties: {
45                             type: "boolean"
46                         }
47                     }
48                 },
49                 additionalProperties: false
50             }
51         ],
52         messages: {
53             unexpectedBefore: "Unexpected space before unary operator '{{operator}}'.",
54             unexpectedAfter: "Unexpected space after unary operator '{{operator}}'.",
55             unexpectedAfterWord: "Unexpected space after unary word operator '{{word}}'.",
56             wordOperator: "Unary word operator '{{word}}' must be followed by whitespace.",
57             operator: "Unary operator '{{operator}}' must be followed by whitespace.",
58             beforeUnaryExpressions: "Space is required before unary expressions '{{token}}'."
59         }
60     },
61
62     create(context) {
63         const options = context.options[0] || { words: true, nonwords: false };
64
65         const sourceCode = context.getSourceCode();
66
67         //--------------------------------------------------------------------------
68         // Helpers
69         //--------------------------------------------------------------------------
70
71         /**
72          * Check if the node is the first "!" in a "!!" convert to Boolean expression
73          * @param {ASTnode} node AST node
74          * @returns {boolean} Whether or not the node is first "!" in "!!"
75          */
76         function isFirstBangInBangBangExpression(node) {
77             return node && node.type === "UnaryExpression" && node.argument.operator === "!" &&
78                 node.argument && node.argument.type === "UnaryExpression" && node.argument.operator === "!";
79         }
80
81         /**
82          * Checks if an override exists for a given operator.
83          * @param {string} operator Operator
84          * @returns {boolean} Whether or not an override has been provided for the operator
85          */
86         function overrideExistsForOperator(operator) {
87             return options.overrides && Object.prototype.hasOwnProperty.call(options.overrides, operator);
88         }
89
90         /**
91          * Gets the value that the override was set to for this operator
92          * @param {string} operator Operator
93          * @returns {boolean} Whether or not an override enforces a space with this operator
94          */
95         function overrideEnforcesSpaces(operator) {
96             return options.overrides[operator];
97         }
98
99         /**
100          * Verify Unary Word Operator has spaces after the word operator
101          * @param {ASTnode} node AST node
102          * @param {Object} firstToken first token from the AST node
103          * @param {Object} secondToken second token from the AST node
104          * @param {string} word The word to be used for reporting
105          * @returns {void}
106          */
107         function verifyWordHasSpaces(node, firstToken, secondToken, word) {
108             if (secondToken.range[0] === firstToken.range[1]) {
109                 context.report({
110                     node,
111                     messageId: "wordOperator",
112                     data: {
113                         word
114                     },
115                     fix(fixer) {
116                         return fixer.insertTextAfter(firstToken, " ");
117                     }
118                 });
119             }
120         }
121
122         /**
123          * Verify Unary Word Operator doesn't have spaces after the word operator
124          * @param {ASTnode} node AST node
125          * @param {Object} firstToken first token from the AST node
126          * @param {Object} secondToken second token from the AST node
127          * @param {string} word The word to be used for reporting
128          * @returns {void}
129          */
130         function verifyWordDoesntHaveSpaces(node, firstToken, secondToken, word) {
131             if (astUtils.canTokensBeAdjacent(firstToken, secondToken)) {
132                 if (secondToken.range[0] > firstToken.range[1]) {
133                     context.report({
134                         node,
135                         messageId: "unexpectedAfterWord",
136                         data: {
137                             word
138                         },
139                         fix(fixer) {
140                             return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);
141                         }
142                     });
143                 }
144             }
145         }
146
147         /**
148          * Check Unary Word Operators for spaces after the word operator
149          * @param {ASTnode} node AST node
150          * @param {Object} firstToken first token from the AST node
151          * @param {Object} secondToken second token from the AST node
152          * @param {string} word The word to be used for reporting
153          * @returns {void}
154          */
155         function checkUnaryWordOperatorForSpaces(node, firstToken, secondToken, word) {
156             if (overrideExistsForOperator(word)) {
157                 if (overrideEnforcesSpaces(word)) {
158                     verifyWordHasSpaces(node, firstToken, secondToken, word);
159                 } else {
160                     verifyWordDoesntHaveSpaces(node, firstToken, secondToken, word);
161                 }
162             } else if (options.words) {
163                 verifyWordHasSpaces(node, firstToken, secondToken, word);
164             } else {
165                 verifyWordDoesntHaveSpaces(node, firstToken, secondToken, word);
166             }
167         }
168
169         /**
170          * Verifies YieldExpressions satisfy spacing requirements
171          * @param {ASTnode} node AST node
172          * @returns {void}
173          */
174         function checkForSpacesAfterYield(node) {
175             const tokens = sourceCode.getFirstTokens(node, 3),
176                 word = "yield";
177
178             if (!node.argument || node.delegate) {
179                 return;
180             }
181
182             checkUnaryWordOperatorForSpaces(node, tokens[0], tokens[1], word);
183         }
184
185         /**
186          * Verifies AwaitExpressions satisfy spacing requirements
187          * @param {ASTNode} node AwaitExpression AST node
188          * @returns {void}
189          */
190         function checkForSpacesAfterAwait(node) {
191             const tokens = sourceCode.getFirstTokens(node, 3);
192
193             checkUnaryWordOperatorForSpaces(node, tokens[0], tokens[1], "await");
194         }
195
196         /**
197          * Verifies UnaryExpression, UpdateExpression and NewExpression have spaces before or after the operator
198          * @param {ASTnode} node AST node
199          * @param {Object} firstToken First token in the expression
200          * @param {Object} secondToken Second token in the expression
201          * @returns {void}
202          */
203         function verifyNonWordsHaveSpaces(node, firstToken, secondToken) {
204             if (node.prefix) {
205                 if (isFirstBangInBangBangExpression(node)) {
206                     return;
207                 }
208                 if (firstToken.range[1] === secondToken.range[0]) {
209                     context.report({
210                         node,
211                         messageId: "operator",
212                         data: {
213                             operator: firstToken.value
214                         },
215                         fix(fixer) {
216                             return fixer.insertTextAfter(firstToken, " ");
217                         }
218                     });
219                 }
220             } else {
221                 if (firstToken.range[1] === secondToken.range[0]) {
222                     context.report({
223                         node,
224                         messageId: "beforeUnaryExpressions",
225                         data: {
226                             token: secondToken.value
227                         },
228                         fix(fixer) {
229                             return fixer.insertTextBefore(secondToken, " ");
230                         }
231                     });
232                 }
233             }
234         }
235
236         /**
237          * Verifies UnaryExpression, UpdateExpression and NewExpression don't have spaces before or after the operator
238          * @param {ASTnode} node AST node
239          * @param {Object} firstToken First token in the expression
240          * @param {Object} secondToken Second token in the expression
241          * @returns {void}
242          */
243         function verifyNonWordsDontHaveSpaces(node, firstToken, secondToken) {
244             if (node.prefix) {
245                 if (secondToken.range[0] > firstToken.range[1]) {
246                     context.report({
247                         node,
248                         messageId: "unexpectedAfter",
249                         data: {
250                             operator: firstToken.value
251                         },
252                         fix(fixer) {
253                             if (astUtils.canTokensBeAdjacent(firstToken, secondToken)) {
254                                 return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);
255                             }
256                             return null;
257                         }
258                     });
259                 }
260             } else {
261                 if (secondToken.range[0] > firstToken.range[1]) {
262                     context.report({
263                         node,
264                         messageId: "unexpectedBefore",
265                         data: {
266                             operator: secondToken.value
267                         },
268                         fix(fixer) {
269                             return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);
270                         }
271                     });
272                 }
273             }
274         }
275
276         /**
277          * Verifies UnaryExpression, UpdateExpression and NewExpression satisfy spacing requirements
278          * @param {ASTnode} node AST node
279          * @returns {void}
280          */
281         function checkForSpaces(node) {
282             const tokens = node.type === "UpdateExpression" && !node.prefix
283                 ? sourceCode.getLastTokens(node, 2)
284                 : sourceCode.getFirstTokens(node, 2);
285             const firstToken = tokens[0];
286             const secondToken = tokens[1];
287
288             if ((node.type === "NewExpression" || node.prefix) && firstToken.type === "Keyword") {
289                 checkUnaryWordOperatorForSpaces(node, firstToken, secondToken, firstToken.value);
290                 return;
291             }
292
293             const operator = node.prefix ? tokens[0].value : tokens[1].value;
294
295             if (overrideExistsForOperator(operator)) {
296                 if (overrideEnforcesSpaces(operator)) {
297                     verifyNonWordsHaveSpaces(node, firstToken, secondToken);
298                 } else {
299                     verifyNonWordsDontHaveSpaces(node, firstToken, secondToken);
300                 }
301             } else if (options.nonwords) {
302                 verifyNonWordsHaveSpaces(node, firstToken, secondToken);
303             } else {
304                 verifyNonWordsDontHaveSpaces(node, firstToken, secondToken);
305             }
306         }
307
308         //--------------------------------------------------------------------------
309         // Public
310         //--------------------------------------------------------------------------
311
312         return {
313             UnaryExpression: checkForSpaces,
314             UpdateExpression: checkForSpaces,
315             NewExpression: checkForSpaces,
316             YieldExpression: checkForSpacesAfterYield,
317             AwaitExpression: checkForSpacesAfterAwait
318         };
319
320     }
321 };