UNPKG

4.44 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 = function(context) {
12 var int32Hint = context.options[0] ? context.options[0].int32Hint === true : false;
13
14 var OPERATORS = [
15 "*", "/", "%", "+", "-", "<<", ">>", ">>>", "<", "<=", ">", ">=", "in",
16 "instanceof", "==", "!=", "===", "!==", "&", "^", "|", "&&", "||", "=",
17 "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "^=", "|=",
18 "?", ":", ","
19 ];
20
21 /**
22 * Returns the first token which violates the rule
23 * @param {ASTNode} left - The left node of the main node
24 * @param {ASTNode} right - The right node of the main node
25 * @returns {object} The violator token or null
26 * @private
27 */
28 function getFirstNonSpacedToken(left, right) {
29 var op, tokens = context.getTokensBetween(left, right, 1);
30 for (var i = 1, l = tokens.length - 1; i < l; ++i) {
31 op = tokens[i];
32 if (
33 op.type === "Punctuator" &&
34 OPERATORS.indexOf(op.value) >= 0 &&
35 (tokens[i - 1].range[1] >= op.range[0] || op.range[1] >= tokens[i + 1].range[0])
36 ) {
37 return op;
38 }
39 }
40 return null;
41 }
42
43 /**
44 * Reports an AST node as a rule violation
45 * @param {ASTNode} mainNode - The node to report
46 * @param {object} culpritToken - The token which has a problem
47 * @returns {void}
48 * @private
49 */
50 function report(mainNode, culpritToken) {
51 context.report({
52 node: mainNode,
53 loc: culpritToken.loc.start,
54 message: "Infix operators must be spaced.",
55 fix: function(fixer) {
56 var previousToken = context.getTokenBefore(culpritToken);
57 var afterToken = context.getTokenAfter(culpritToken);
58 var fixString = "";
59
60 if (culpritToken.range[0] - previousToken.range[1] === 0) {
61 fixString = " ";
62 }
63
64 fixString += culpritToken.value;
65
66 if (afterToken.range[0] - culpritToken.range[1] === 0) {
67 fixString += " ";
68 }
69
70 return fixer.replaceText(culpritToken, fixString);
71 }
72 });
73 }
74
75 /**
76 * Check if the node is binary then report
77 * @param {ASTNode} node node to evaluate
78 * @returns {void}
79 * @private
80 */
81 function checkBinary(node) {
82 var nonSpacedNode = getFirstNonSpacedToken(node.left, node.right);
83
84 if (nonSpacedNode) {
85 if (!(int32Hint && context.getSource(node).substr(-2) === "|0")) {
86 report(node, nonSpacedNode);
87 }
88 }
89 }
90
91 /**
92 * Check if the node is conditional
93 * @param {ASTNode} node node to evaluate
94 * @returns {void}
95 * @private
96 */
97 function checkConditional(node) {
98 var nonSpacedConsequesntNode = getFirstNonSpacedToken(node.test, node.consequent);
99 var nonSpacedAlternateNode = getFirstNonSpacedToken(node.consequent, node.alternate);
100
101 if (nonSpacedConsequesntNode) {
102 report(node, nonSpacedConsequesntNode);
103 } else if (nonSpacedAlternateNode) {
104 report(node, nonSpacedAlternateNode);
105 }
106 }
107
108 /**
109 * Check if the node is a variable
110 * @param {ASTNode} node node to evaluate
111 * @returns {void}
112 * @private
113 */
114 function checkVar(node) {
115 var nonSpacedNode;
116
117 if (node.init) {
118 nonSpacedNode = getFirstNonSpacedToken(node.id, node.init);
119 if (nonSpacedNode) {
120 report(node, nonSpacedNode);
121 }
122 }
123 }
124
125 return {
126 "AssignmentExpression": checkBinary,
127 "AssignmentPattern": checkBinary,
128 "BinaryExpression": checkBinary,
129 "LogicalExpression": checkBinary,
130 "ConditionalExpression": checkConditional,
131 "VariableDeclarator": checkVar
132 };
133
134};
135
136module.exports.schema = [
137 {
138 "type": "object",
139 "properties": {
140 "int32Hint": {
141 "type": "boolean"
142 }
143 },
144 "additionalProperties": false
145 }
146];