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