max-statements.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. /**
  2. * @fileoverview A rule to set the maximum number of statements in a function.
  3. * @author Ian Christian Myers
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const astUtils = require("./utils/ast-utils");
  10. const { upperCaseFirst } = require("../shared/string-utils");
  11. //------------------------------------------------------------------------------
  12. // Rule Definition
  13. //------------------------------------------------------------------------------
  14. /** @type {import('../shared/types').Rule} */
  15. module.exports = {
  16. meta: {
  17. type: "suggestion",
  18. docs: {
  19. description: "Enforce a maximum number of statements allowed in function blocks",
  20. recommended: false,
  21. url: "https://eslint.org/docs/rules/max-statements"
  22. },
  23. schema: [
  24. {
  25. oneOf: [
  26. {
  27. type: "integer",
  28. minimum: 0
  29. },
  30. {
  31. type: "object",
  32. properties: {
  33. maximum: {
  34. type: "integer",
  35. minimum: 0
  36. },
  37. max: {
  38. type: "integer",
  39. minimum: 0
  40. }
  41. },
  42. additionalProperties: false
  43. }
  44. ]
  45. },
  46. {
  47. type: "object",
  48. properties: {
  49. ignoreTopLevelFunctions: {
  50. type: "boolean"
  51. }
  52. },
  53. additionalProperties: false
  54. }
  55. ],
  56. messages: {
  57. exceed: "{{name}} has too many statements ({{count}}). Maximum allowed is {{max}}."
  58. }
  59. },
  60. create(context) {
  61. //--------------------------------------------------------------------------
  62. // Helpers
  63. //--------------------------------------------------------------------------
  64. const functionStack = [],
  65. option = context.options[0],
  66. ignoreTopLevelFunctions = context.options[1] && context.options[1].ignoreTopLevelFunctions || false,
  67. topLevelFunctions = [];
  68. let maxStatements = 10;
  69. if (
  70. typeof option === "object" &&
  71. (Object.prototype.hasOwnProperty.call(option, "maximum") || Object.prototype.hasOwnProperty.call(option, "max"))
  72. ) {
  73. maxStatements = option.maximum || option.max;
  74. } else if (typeof option === "number") {
  75. maxStatements = option;
  76. }
  77. /**
  78. * Reports a node if it has too many statements
  79. * @param {ASTNode} node node to evaluate
  80. * @param {int} count Number of statements in node
  81. * @param {int} max Maximum number of statements allowed
  82. * @returns {void}
  83. * @private
  84. */
  85. function reportIfTooManyStatements(node, count, max) {
  86. if (count > max) {
  87. const name = upperCaseFirst(astUtils.getFunctionNameWithKind(node));
  88. context.report({
  89. node,
  90. messageId: "exceed",
  91. data: { name, count, max }
  92. });
  93. }
  94. }
  95. /**
  96. * When parsing a new function, store it in our function stack
  97. * @returns {void}
  98. * @private
  99. */
  100. function startFunction() {
  101. functionStack.push(0);
  102. }
  103. /**
  104. * Evaluate the node at the end of function
  105. * @param {ASTNode} node node to evaluate
  106. * @returns {void}
  107. * @private
  108. */
  109. function endFunction(node) {
  110. const count = functionStack.pop();
  111. /*
  112. * This rule does not apply to class static blocks, but we have to track them so
  113. * that statements in them do not count as statements in the enclosing function.
  114. */
  115. if (node.type === "StaticBlock") {
  116. return;
  117. }
  118. if (ignoreTopLevelFunctions && functionStack.length === 0) {
  119. topLevelFunctions.push({ node, count });
  120. } else {
  121. reportIfTooManyStatements(node, count, maxStatements);
  122. }
  123. }
  124. /**
  125. * Increment the count of the functions
  126. * @param {ASTNode} node node to evaluate
  127. * @returns {void}
  128. * @private
  129. */
  130. function countStatements(node) {
  131. functionStack[functionStack.length - 1] += node.body.length;
  132. }
  133. //--------------------------------------------------------------------------
  134. // Public API
  135. //--------------------------------------------------------------------------
  136. return {
  137. FunctionDeclaration: startFunction,
  138. FunctionExpression: startFunction,
  139. ArrowFunctionExpression: startFunction,
  140. StaticBlock: startFunction,
  141. BlockStatement: countStatements,
  142. "FunctionDeclaration:exit": endFunction,
  143. "FunctionExpression:exit": endFunction,
  144. "ArrowFunctionExpression:exit": endFunction,
  145. "StaticBlock:exit": endFunction,
  146. "Program:exit"() {
  147. if (topLevelFunctions.length === 1) {
  148. return;
  149. }
  150. topLevelFunctions.forEach(element => {
  151. const count = element.count;
  152. const node = element.node;
  153. reportIfTooManyStatements(node, count, maxStatements);
  154. });
  155. }
  156. };
  157. }
  158. };