max-combined-conditions.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /**
  2. * @fileoverview Rule to flag use combined conditions
  3. * @author Lisonglin
  4. */
  5. "use strict";
  6. const lodash = require("lodash");
  7. //------------------------------------------------------------------------------
  8. // Helpers
  9. //------------------------------------------------------------------------------
  10. //------------------------------------------------------------------------------
  11. // Rule Definition
  12. //------------------------------------------------------------------------------
  13. const OPTIONS_OR_INTEGER_SCHEMA = {
  14. type: "integer",
  15. minimum: 0
  16. };
  17. module.exports = {
  18. meta: {
  19. type: "problem",
  20. docs: {
  21. description: "disallow combined expressions in conditions",
  22. category: "Possible Errors",
  23. recommended: true,
  24. url: "https://npm"
  25. },
  26. schema: [
  27. OPTIONS_OR_INTEGER_SCHEMA
  28. ],
  29. messages: {
  30. unexpected: "too many combined conditions ({{operatorNum}}). Maximum allowed is {{maxCombination}}."
  31. }
  32. },
  33. create(context) {
  34. const defaultMaxCombination = 1;
  35. const option = context.options[0];
  36. let maxCombination = defaultMaxCombination;
  37. let operatorNum = 0;
  38. if (typeof option === "number") {
  39. maxCombination = option
  40. }
  41. function isContainOtherOperator(node) {
  42. const { left, right } = node;
  43. const leftOperator = lodash.get(left, 'operator', '');
  44. const rightOperator = lodash.get(right, 'operator', '');
  45. const otherOperatorReg = /[><=]/;
  46. return otherOperatorReg.test(leftOperator) || otherOperatorReg.test(rightOperator);
  47. }
  48. function findOperatorNum(node) {
  49. const { left, right, operator } = node;
  50. const isValidNode = left || right;
  51. const isContainAndOrOperator = ['&&', '||'].includes(operator);
  52. const isInvalidOperator = isValidNode && isContainAndOrOperator && isContainOtherOperator(node);
  53. if (isInvalidOperator) operatorNum++;
  54. if (left) findOperatorNum(left);
  55. if (right) findOperatorNum(right);
  56. }
  57. function isTooManyCombined(node) {
  58. operatorNum = 0;
  59. findOperatorNum(node);
  60. return operatorNum > maxCombination
  61. }
  62. /**
  63. * Reports when the given node contains a constant condition.
  64. * @param {ASTNode} node The AST node to check.
  65. * @returns {void}
  66. * @private
  67. */
  68. function reportIfConstant(node) {
  69. if (node.test && isTooManyCombined(node.test)) {
  70. context.report({
  71. node: node.test,
  72. messageId: "unexpected",
  73. data: {
  74. maxCombination,
  75. operatorNum
  76. }
  77. });
  78. }
  79. }
  80. return {
  81. ConditionalExpression: reportIfConstant,
  82. IfStatement: reportIfConstant,
  83. // WhileStatement: checkLoop,
  84. // "WhileStatement:exit": checkConstantConditionLoopInSet,
  85. // DoWhileStatement: checkLoop,
  86. // "DoWhileStatement:exit": checkConstantConditionLoopInSet,
  87. // ForStatement: checkLoop,
  88. // "ForStatement > .test": node => checkLoop(node.parent),
  89. // "ForStatement:exit": checkConstantConditionLoopInSet,
  90. // FunctionDeclaration: enterFunction,
  91. // "FunctionDeclaration:exit": exitFunction,
  92. // FunctionExpression: enterFunction,
  93. // "FunctionExpression:exit": exitFunction,
  94. // YieldExpression: () => loopsInCurrentScope.clear()
  95. };
  96. }
  97. };