max-depth.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /**
  2. * @fileoverview A rule to set the maximum depth block can be nested in a function.
  3. * @author Ian Christian Myers
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Rule Definition
  8. //------------------------------------------------------------------------------
  9. /** @type {import('../types').Rule.RuleModule} */
  10. module.exports = {
  11. meta: {
  12. type: "suggestion",
  13. docs: {
  14. description: "Enforce a maximum depth that blocks can be nested",
  15. recommended: false,
  16. url: "https://eslint.org/docs/latest/rules/max-depth",
  17. },
  18. schema: [
  19. {
  20. oneOf: [
  21. {
  22. type: "integer",
  23. minimum: 0,
  24. },
  25. {
  26. type: "object",
  27. properties: {
  28. maximum: {
  29. type: "integer",
  30. minimum: 0,
  31. },
  32. max: {
  33. type: "integer",
  34. minimum: 0,
  35. },
  36. },
  37. additionalProperties: false,
  38. },
  39. ],
  40. },
  41. ],
  42. messages: {
  43. tooDeeply:
  44. "Blocks are nested too deeply ({{depth}}). Maximum allowed is {{maxDepth}}.",
  45. },
  46. },
  47. create(context) {
  48. //--------------------------------------------------------------------------
  49. // Helpers
  50. //--------------------------------------------------------------------------
  51. const functionStack = [],
  52. option = context.options[0];
  53. let maxDepth = 4;
  54. if (
  55. typeof option === "object" &&
  56. (Object.hasOwn(option, "maximum") || Object.hasOwn(option, "max"))
  57. ) {
  58. maxDepth = option.maximum || option.max;
  59. }
  60. if (typeof option === "number") {
  61. maxDepth = option;
  62. }
  63. /**
  64. * When parsing a new function, store it in our function stack
  65. * @returns {void}
  66. * @private
  67. */
  68. function startFunction() {
  69. functionStack.push(0);
  70. }
  71. /**
  72. * When parsing is done then pop out the reference
  73. * @returns {void}
  74. * @private
  75. */
  76. function endFunction() {
  77. functionStack.pop();
  78. }
  79. /**
  80. * Save the block and Evaluate the node
  81. * @param {ASTNode} node node to evaluate
  82. * @returns {void}
  83. * @private
  84. */
  85. function pushBlock(node) {
  86. const len = ++functionStack[functionStack.length - 1];
  87. if (len > maxDepth) {
  88. context.report({
  89. node,
  90. messageId: "tooDeeply",
  91. data: { depth: len, maxDepth },
  92. });
  93. }
  94. }
  95. /**
  96. * Pop the saved block
  97. * @returns {void}
  98. * @private
  99. */
  100. function popBlock() {
  101. functionStack[functionStack.length - 1]--;
  102. }
  103. //--------------------------------------------------------------------------
  104. // Public API
  105. //--------------------------------------------------------------------------
  106. return {
  107. Program: startFunction,
  108. FunctionDeclaration: startFunction,
  109. FunctionExpression: startFunction,
  110. ArrowFunctionExpression: startFunction,
  111. StaticBlock: startFunction,
  112. IfStatement(node) {
  113. if (node.parent.type !== "IfStatement") {
  114. pushBlock(node);
  115. }
  116. },
  117. SwitchStatement: pushBlock,
  118. TryStatement: pushBlock,
  119. DoWhileStatement: pushBlock,
  120. WhileStatement: pushBlock,
  121. WithStatement: pushBlock,
  122. ForStatement: pushBlock,
  123. ForInStatement: pushBlock,
  124. ForOfStatement: pushBlock,
  125. "IfStatement:exit": popBlock,
  126. "SwitchStatement:exit": popBlock,
  127. "TryStatement:exit": popBlock,
  128. "DoWhileStatement:exit": popBlock,
  129. "WhileStatement:exit": popBlock,
  130. "WithStatement:exit": popBlock,
  131. "ForStatement:exit": popBlock,
  132. "ForInStatement:exit": popBlock,
  133. "ForOfStatement:exit": popBlock,
  134. "FunctionDeclaration:exit": endFunction,
  135. "FunctionExpression:exit": endFunction,
  136. "ArrowFunctionExpression:exit": endFunction,
  137. "StaticBlock:exit": endFunction,
  138. "Program:exit": endFunction,
  139. };
  140. },
  141. };