UNPKG

5.49 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 },
18
19 fixable: "whitespace",
20
21 schema: [
22 {
23 type: "object",
24 properties: {
25 int32Hint: {
26 type: "boolean"
27 }
28 },
29 additionalProperties: false
30 }
31 ]
32 },
33
34 create(context) {
35 const int32Hint = context.options[0] ? context.options[0].int32Hint === true : false;
36
37 const OPERATORS = [
38 "*", "/", "%", "+", "-", "<<", ">>", ">>>", "<", "<=", ">", ">=", "in",
39 "instanceof", "==", "!=", "===", "!==", "&", "^", "|", "&&", "||", "=",
40 "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "^=", "|=",
41 "?", ":", ",", "**"
42 ];
43
44 const sourceCode = context.getSourceCode();
45
46 /**
47 * Returns the first token which violates the rule
48 * @param {ASTNode} left - The left node of the main node
49 * @param {ASTNode} right - The right node of the main node
50 * @returns {Object} The violator token or null
51 * @private
52 */
53 function getFirstNonSpacedToken(left, right) {
54 const tokens = sourceCode.getTokensBetween(left, right, 1);
55
56 for (let i = 1, l = tokens.length - 1; i < l; ++i) {
57 const op = tokens[i];
58
59 if (
60 (op.type === "Punctuator" || op.type === "Keyword") &&
61 OPERATORS.indexOf(op.value) >= 0 &&
62 (tokens[i - 1].range[1] >= op.range[0] || op.range[1] >= tokens[i + 1].range[0])
63 ) {
64 return op;
65 }
66 }
67 return null;
68 }
69
70 /**
71 * Reports an AST node as a rule violation
72 * @param {ASTNode} mainNode - The node to report
73 * @param {Object} culpritToken - The token which has a problem
74 * @returns {void}
75 * @private
76 */
77 function report(mainNode, culpritToken) {
78 context.report({
79 node: mainNode,
80 loc: culpritToken.loc.start,
81 message: "Infix operators must be spaced.",
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 const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode);
113
114 if (nonSpacedNode) {
115 if (!(int32Hint && sourceCode.getText(node).endsWith("|0"))) {
116 report(node, nonSpacedNode);
117 }
118 }
119 }
120
121 /**
122 * Check if the node is conditional
123 * @param {ASTNode} node node to evaluate
124 * @returns {void}
125 * @private
126 */
127 function checkConditional(node) {
128 const nonSpacedConsequesntNode = getFirstNonSpacedToken(node.test, node.consequent);
129 const nonSpacedAlternateNode = getFirstNonSpacedToken(node.consequent, node.alternate);
130
131 if (nonSpacedConsequesntNode) {
132 report(node, nonSpacedConsequesntNode);
133 } else if (nonSpacedAlternateNode) {
134 report(node, nonSpacedAlternateNode);
135 }
136 }
137
138 /**
139 * Check if the node is a variable
140 * @param {ASTNode} node node to evaluate
141 * @returns {void}
142 * @private
143 */
144 function checkVar(node) {
145 const leftNode = (node.id.typeAnnotation) ? node.id.typeAnnotation : node.id;
146 const rightNode = node.init;
147
148 if (rightNode) {
149 const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode);
150
151 if (nonSpacedNode) {
152 report(node, nonSpacedNode);
153 }
154 }
155 }
156
157 return {
158 AssignmentExpression: checkBinary,
159 AssignmentPattern: checkBinary,
160 BinaryExpression: checkBinary,
161 LogicalExpression: checkBinary,
162 ConditionalExpression: checkConditional,
163 VariableDeclarator: checkVar
164 };
165
166 }
167};