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