]> git.proxmox.com Git - pve-eslint.git/blame - eslint/lib/rules/space-unary-ops.js
import eslint 7.18.0
[pve-eslint.git] / eslint / lib / rules / space-unary-ops.js
CommitLineData
eb39fafa 1/**
456be15e 2 * @fileoverview This rule should require or disallow spaces before or after unary operations.
eb39fafa
DC
3 * @author Marcin Kumorek
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Requirements
9//------------------------------------------------------------------------------
10
11const astUtils = require("./utils/ast-utils");
12
13//------------------------------------------------------------------------------
14// Rule Definition
15//------------------------------------------------------------------------------
16
17module.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};