max-statements.js 4.5 KB

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