UNPKG

5.56 kBJavaScriptView Raw
1/**
2 * @fileoverview Require spaces around infix operators
3 * @author Michael Ficarra
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Rule Definition
9//------------------------------------------------------------------------------
10
11module.exports = {
12 meta: {
13 docs: {
14 description: "require spacing around infix operators",
15 category: "Stylistic Issues",
16 recommended: false,
17 url: "https://eslint.org/docs/rules/space-infix-ops"
18 },
19
20 fixable: "whitespace",
21
22 schema: [
23 {
24 type: "object",
25 properties: {
26 int32Hint: {
27 type: "boolean"
28 }
29 },
30 additionalProperties: false
31 }
32 ]
33 },
34
35 create(context) {
36 const int32Hint = context.options[0] ? context.options[0].int32Hint === true : false;
37
38 const OPERATORS = [
39 "*", "/", "%", "+", "-", "<<", ">>", ">>>", "<", "<=", ">", ">=", "in",
40 "instanceof", "==", "!=", "===", "!==", "&", "^", "|", "&&", "||", "=",
41 "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "^=", "|=",
42 "?", ":", ",", "**"
43 ];
44
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 * @returns {Object} The violator token or null
52 * @private
53 */
54 function getFirstNonSpacedToken(left, right) {
55 const tokens = sourceCode.getTokensBetween(left, right, 1);
56
57 for (let i = 1, l = tokens.length - 1; i < l; ++i) {
58 const op = tokens[i];
59
60 if (
61 (op.type === "Punctuator" || op.type === "Keyword") &&
62 OPERATORS.indexOf(op.value) >= 0 &&
63 (tokens[i - 1].range[1] >= op.range[0] || op.range[1] >= tokens[i + 1].range[0])
64 ) {
65 return op;
66 }
67 }
68 return null;
69 }
70
71 /**
72 * Reports an AST node as a rule violation
73 * @param {ASTNode} mainNode - The node to report
74 * @param {Object} culpritToken - The token which has a problem
75 * @returns {void}
76 * @private
77 */
78 function report(mainNode, culpritToken) {
79 context.report({
80 node: mainNode,
81 loc: culpritToken.loc.start,
82 message: "Infix operators must be spaced.",
83 fix(fixer) {
84 const previousToken = sourceCode.getTokenBefore(culpritToken);
85 const afterToken = sourceCode.getTokenAfter(culpritToken);
86 let fixString = "";
87
88 if (culpritToken.range[0] - previousToken.range[1] === 0) {
89 fixString = " ";
90 }
91
92 fixString += culpritToken.value;
93
94 if (afterToken.range[0] - culpritToken.range[1] === 0) {
95 fixString += " ";
96 }
97
98 return fixer.replaceText(culpritToken, fixString);
99 }
100 });
101 }
102
103 /**
104 * Check if the node is binary then report
105 * @param {ASTNode} node node to evaluate
106 * @returns {void}
107 * @private
108 */
109 function checkBinary(node) {
110 const leftNode = (node.left.typeAnnotation) ? node.left.typeAnnotation : node.left;
111 const rightNode = node.right;
112
113 const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode);
114
115 if (nonSpacedNode) {
116 if (!(int32Hint && sourceCode.getText(node).endsWith("|0"))) {
117 report(node, nonSpacedNode);
118 }
119 }
120 }
121
122 /**
123 * Check if the node is conditional
124 * @param {ASTNode} node node to evaluate
125 * @returns {void}
126 * @private
127 */
128 function checkConditional(node) {
129 const nonSpacedConsequesntNode = getFirstNonSpacedToken(node.test, node.consequent);
130 const nonSpacedAlternateNode = getFirstNonSpacedToken(node.consequent, node.alternate);
131
132 if (nonSpacedConsequesntNode) {
133 report(node, nonSpacedConsequesntNode);
134 } else if (nonSpacedAlternateNode) {
135 report(node, nonSpacedAlternateNode);
136 }
137 }
138
139 /**
140 * Check if the node is a variable
141 * @param {ASTNode} node node to evaluate
142 * @returns {void}
143 * @private
144 */
145 function checkVar(node) {
146 const leftNode = (node.id.typeAnnotation) ? node.id.typeAnnotation : node.id;
147 const rightNode = node.init;
148
149 if (rightNode) {
150 const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode);
151
152 if (nonSpacedNode) {
153 report(node, nonSpacedNode);
154 }
155 }
156 }
157
158 return {
159 AssignmentExpression: checkBinary,
160 AssignmentPattern: checkBinary,
161 BinaryExpression: checkBinary,
162 LogicalExpression: checkBinary,
163 ConditionalExpression: checkConditional,
164 VariableDeclarator: checkVar
165 };
166
167 }
168};