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