space-infix-ops.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /**
  2. * @fileoverview Require spaces around infix operators
  3. * @author Michael Ficarra
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Rule Definition
  8. //------------------------------------------------------------------------------
  9. module.exports = {
  10. meta: {
  11. type: "layout",
  12. docs: {
  13. description: "require spacing around infix operators",
  14. category: "Stylistic Issues",
  15. recommended: false,
  16. url: "https://eslint.org/docs/rules/space-infix-ops"
  17. },
  18. fixable: "whitespace",
  19. schema: [
  20. {
  21. type: "object",
  22. properties: {
  23. int32Hint: {
  24. type: "boolean",
  25. default: false
  26. }
  27. },
  28. additionalProperties: false
  29. }
  30. ]
  31. },
  32. create(context) {
  33. const int32Hint = context.options[0] ? context.options[0].int32Hint === true : false;
  34. const sourceCode = context.getSourceCode();
  35. /**
  36. * Returns the first token which violates the rule
  37. * @param {ASTNode} left The left node of the main node
  38. * @param {ASTNode} right The right node of the main node
  39. * @param {string} op The operator of the main node
  40. * @returns {Object} The violator token or null
  41. * @private
  42. */
  43. function getFirstNonSpacedToken(left, right, op) {
  44. const operator = sourceCode.getFirstTokenBetween(left, right, token => token.value === op);
  45. const prev = sourceCode.getTokenBefore(operator);
  46. const next = sourceCode.getTokenAfter(operator);
  47. if (!sourceCode.isSpaceBetweenTokens(prev, operator) || !sourceCode.isSpaceBetweenTokens(operator, next)) {
  48. return operator;
  49. }
  50. return null;
  51. }
  52. /**
  53. * Reports an AST node as a rule violation
  54. * @param {ASTNode} mainNode The node to report
  55. * @param {Object} culpritToken The token which has a problem
  56. * @returns {void}
  57. * @private
  58. */
  59. function report(mainNode, culpritToken) {
  60. context.report({
  61. node: mainNode,
  62. loc: culpritToken.loc,
  63. message: "Operator '{{operator}}' must be spaced.",
  64. data: {
  65. operator: culpritToken.value
  66. },
  67. fix(fixer) {
  68. const previousToken = sourceCode.getTokenBefore(culpritToken);
  69. const afterToken = sourceCode.getTokenAfter(culpritToken);
  70. let fixString = "";
  71. if (culpritToken.range[0] - previousToken.range[1] === 0) {
  72. fixString = " ";
  73. }
  74. fixString += culpritToken.value;
  75. if (afterToken.range[0] - culpritToken.range[1] === 0) {
  76. fixString += " ";
  77. }
  78. return fixer.replaceText(culpritToken, fixString);
  79. }
  80. });
  81. }
  82. /**
  83. * Check if the node is binary then report
  84. * @param {ASTNode} node node to evaluate
  85. * @returns {void}
  86. * @private
  87. */
  88. function checkBinary(node) {
  89. const leftNode = (node.left.typeAnnotation) ? node.left.typeAnnotation : node.left;
  90. const rightNode = node.right;
  91. // search for = in AssignmentPattern nodes
  92. const operator = node.operator || "=";
  93. const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode, operator);
  94. if (nonSpacedNode) {
  95. if (!(int32Hint && sourceCode.getText(node).endsWith("|0"))) {
  96. report(node, nonSpacedNode);
  97. }
  98. }
  99. }
  100. /**
  101. * Check if the node is conditional
  102. * @param {ASTNode} node node to evaluate
  103. * @returns {void}
  104. * @private
  105. */
  106. function checkConditional(node) {
  107. const nonSpacedConsequesntNode = getFirstNonSpacedToken(node.test, node.consequent, "?");
  108. const nonSpacedAlternateNode = getFirstNonSpacedToken(node.consequent, node.alternate, ":");
  109. if (nonSpacedConsequesntNode) {
  110. report(node, nonSpacedConsequesntNode);
  111. } else if (nonSpacedAlternateNode) {
  112. report(node, nonSpacedAlternateNode);
  113. }
  114. }
  115. /**
  116. * Check if the node is a variable
  117. * @param {ASTNode} node node to evaluate
  118. * @returns {void}
  119. * @private
  120. */
  121. function checkVar(node) {
  122. const leftNode = (node.id.typeAnnotation) ? node.id.typeAnnotation : node.id;
  123. const rightNode = node.init;
  124. if (rightNode) {
  125. const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode, "=");
  126. if (nonSpacedNode) {
  127. report(node, nonSpacedNode);
  128. }
  129. }
  130. }
  131. return {
  132. AssignmentExpression: checkBinary,
  133. AssignmentPattern: checkBinary,
  134. BinaryExpression: checkBinary,
  135. LogicalExpression: checkBinary,
  136. ConditionalExpression: checkConditional,
  137. VariableDeclarator: checkVar
  138. };
  139. }
  140. };