1/** 2 * @fileoverview A rule to set the maximum depth block can be nested in a function. 3 * @author Ian Christian Myers 4 */ 5 6"use strict"; 7 8//------------------------------------------------------------------------------ 9// Rule Definition 10//------------------------------------------------------------------------------ 11 12module.exports = { 13 meta: { 14 type: "suggestion", 15 16 docs: { 17 description: "enforce a maximum depth that blocks can be nested", 18 category: "Stylistic Issues", 19 recommended: false, 20 url: "https://eslint.org/docs/rules/max-depth" 21 }, 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 messages: { 48 tooDeeply: "Blocks are nested too deeply ({{depth}}). Maximum allowed is {{maxDepth}}." 49 } 50 }, 51 52 create(context) { 53 54 //-------------------------------------------------------------------------- 55 // Helpers 56 //-------------------------------------------------------------------------- 57 58 const functionStack = [], 59 option = context.options[0]; 60 let maxDepth = 4; 61 62 if ( 63 typeof option === "object" && 64 (Object.prototype.hasOwnProperty.call(option, "maximum") || Object.prototype.hasOwnProperty.call(option, "max")) 65 ) { 66 maxDepth = option.maximum || option.max; 67 } 68 if (typeof option === "number") { 69 maxDepth = option; 70 } 71 72 /** 73 * When parsing a new function, store it in our function stack 74 * @returns {void} 75 * @private 76 */ 77 function startFunction() { 78 functionStack.push(0); 79 } 80 81 /** 82 * When parsing is done then pop out the reference 83 * @returns {void} 84 * @private 85 */ 86 function endFunction() { 87 functionStack.pop(); 88 } 89 90 /** 91 * Save the block and Evaluate the node 92 * @param {ASTNode} node node to evaluate 93 * @returns {void} 94 * @private 95 */ 96 function pushBlock(node) { 97 const len = ++functionStack[functionStack.length - 1]; 98 99 if (len > maxDepth) { 100 context.report({ node, messageId: "tooDeeply", data: { depth: len, maxDepth } }); 101 } 102 } 103 104 /** 105 * Pop the saved block 106 * @returns {void} 107 * @private 108 */ 109 function popBlock() { 110 functionStack[functionStack.length - 1]--; 111 } 112 113 //-------------------------------------------------------------------------- 114 // Public API 115 //-------------------------------------------------------------------------- 116 117 return { 118 Program: startFunction, 119 FunctionDeclaration: startFunction, 120 FunctionExpression: startFunction, 121 ArrowFunctionExpression: startFunction, 122 123 IfStatement(node) { 124 if (node.parent.type !== "IfStatement") { 125 pushBlock(node); 126 } 127 }, 128 SwitchStatement: pushBlock, 129 TryStatement: pushBlock, 130 DoWhileStatement: pushBlock, 131 WhileStatement: pushBlock, 132 WithStatement: pushBlock, 133 ForStatement: pushBlock, 134 ForInStatement: pushBlock, 135 ForOfStatement: pushBlock, 136 137 "IfStatement:exit": popBlock, 138 "SwitchStatement:exit": popBlock, 139 "TryStatement:exit": popBlock, 140 "DoWhileStatement:exit": popBlock, 141 "WhileStatement:exit": popBlock, 142 "WithStatement:exit": popBlock, 143 "ForStatement:exit": popBlock, 144 "ForInStatement:exit": popBlock, 145 "ForOfStatement:exit": popBlock, 146 147 "FunctionDeclaration:exit": endFunction, 148 "FunctionExpression:exit": endFunction, 149 "ArrowFunctionExpression:exit": endFunction, 150 "Program:exit": endFunction 151 }; 152 153 } 154}; 155