]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/space-infix-ops.js
8065b5240b9ffac0e43c5e882de0bc86e4bb9db2
[pve-eslint.git] / eslint / lib / rules / space-infix-ops.js
1 /**
2 * @fileoverview Require spaces around infix operators
3 * @author Michael Ficarra
4 */
5 "use strict";
6
7 const { isEqToken } = require("./utils/ast-utils");
8
9 //------------------------------------------------------------------------------
10 // Rule Definition
11 //------------------------------------------------------------------------------
12
13 module.exports = {
14 meta: {
15 type: "layout",
16
17 docs: {
18 description: "require spacing around infix operators",
19 recommended: false,
20 url: "https://eslint.org/docs/rules/space-infix-ops"
21 },
22
23 fixable: "whitespace",
24
25 schema: [
26 {
27 type: "object",
28 properties: {
29 int32Hint: {
30 type: "boolean",
31 default: false
32 }
33 },
34 additionalProperties: false
35 }
36 ],
37
38 messages: {
39 missingSpace: "Operator '{{operator}}' must be spaced."
40 }
41 },
42
43 create(context) {
44 const int32Hint = context.options[0] ? context.options[0].int32Hint === true : false;
45 const sourceCode = context.getSourceCode();
46
47 /**
48 * Returns the first token which violates the rule
49 * @param {ASTNode} left The left node of the main node
50 * @param {ASTNode} right The right node of the main node
51 * @param {string} op The operator of the main node
52 * @returns {Object} The violator token or null
53 * @private
54 */
55 function getFirstNonSpacedToken(left, right, op) {
56 const operator = sourceCode.getFirstTokenBetween(left, right, token => token.value === op);
57 const prev = sourceCode.getTokenBefore(operator);
58 const next = sourceCode.getTokenAfter(operator);
59
60 if (!sourceCode.isSpaceBetweenTokens(prev, operator) || !sourceCode.isSpaceBetweenTokens(operator, next)) {
61 return operator;
62 }
63
64 return null;
65 }
66
67 /**
68 * Reports an AST node as a rule violation
69 * @param {ASTNode} mainNode The node to report
70 * @param {Object} culpritToken The token which has a problem
71 * @returns {void}
72 * @private
73 */
74 function report(mainNode, culpritToken) {
75 context.report({
76 node: mainNode,
77 loc: culpritToken.loc,
78 messageId: "missingSpace",
79 data: {
80 operator: culpritToken.value
81 },
82 fix(fixer) {
83 const previousToken = sourceCode.getTokenBefore(culpritToken);
84 const afterToken = sourceCode.getTokenAfter(culpritToken);
85 let fixString = "";
86
87 if (culpritToken.range[0] - previousToken.range[1] === 0) {
88 fixString = " ";
89 }
90
91 fixString += culpritToken.value;
92
93 if (afterToken.range[0] - culpritToken.range[1] === 0) {
94 fixString += " ";
95 }
96
97 return fixer.replaceText(culpritToken, fixString);
98 }
99 });
100 }
101
102 /**
103 * Check if the node is binary then report
104 * @param {ASTNode} node node to evaluate
105 * @returns {void}
106 * @private
107 */
108 function checkBinary(node) {
109 const leftNode = (node.left.typeAnnotation) ? node.left.typeAnnotation : node.left;
110 const rightNode = node.right;
111
112 // search for = in AssignmentPattern nodes
113 const operator = node.operator || "=";
114
115 const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode, operator);
116
117 if (nonSpacedNode) {
118 if (!(int32Hint && sourceCode.getText(node).endsWith("|0"))) {
119 report(node, nonSpacedNode);
120 }
121 }
122 }
123
124 /**
125 * Check if the node is conditional
126 * @param {ASTNode} node node to evaluate
127 * @returns {void}
128 * @private
129 */
130 function checkConditional(node) {
131 const nonSpacedConsequentNode = getFirstNonSpacedToken(node.test, node.consequent, "?");
132 const nonSpacedAlternateNode = getFirstNonSpacedToken(node.consequent, node.alternate, ":");
133
134 if (nonSpacedConsequentNode) {
135 report(node, nonSpacedConsequentNode);
136 }
137
138 if (nonSpacedAlternateNode) {
139 report(node, nonSpacedAlternateNode);
140 }
141 }
142
143 /**
144 * Check if the node is a variable
145 * @param {ASTNode} node node to evaluate
146 * @returns {void}
147 * @private
148 */
149 function checkVar(node) {
150 const leftNode = (node.id.typeAnnotation) ? node.id.typeAnnotation : node.id;
151 const rightNode = node.init;
152
153 if (rightNode) {
154 const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode, "=");
155
156 if (nonSpacedNode) {
157 report(node, nonSpacedNode);
158 }
159 }
160 }
161
162 return {
163 AssignmentExpression: checkBinary,
164 AssignmentPattern: checkBinary,
165 BinaryExpression: checkBinary,
166 LogicalExpression: checkBinary,
167 ConditionalExpression: checkConditional,
168 VariableDeclarator: checkVar,
169
170 PropertyDefinition(node) {
171 if (!node.value) {
172 return;
173 }
174
175 /*
176 * Because of computed properties and type annotations, some
177 * tokens may exist between `node.key` and `=`.
178 * Therefore, find the `=` from the right.
179 */
180 const operatorToken = sourceCode.getTokenBefore(node.value, isEqToken);
181 const leftToken = sourceCode.getTokenBefore(operatorToken);
182 const rightToken = sourceCode.getTokenAfter(operatorToken);
183
184 if (
185 !sourceCode.isSpaceBetweenTokens(leftToken, operatorToken) ||
186 !sourceCode.isSpaceBetweenTokens(operatorToken, rightToken)
187 ) {
188 report(node, operatorToken);
189 }
190 }
191 };
192
193 }
194 };