1/** 2 * @fileoverview Rule to enforce a maximum number of nested callbacks. 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 callbacks can be nested", 18 category: "Stylistic Issues", 19 recommended: false, 20 url: "https://eslint.org/docs/rules/max-nested-callbacks" 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 exceed: "Too many nested callbacks ({{num}}). Maximum allowed is {{max}}." 49 } 50 }, 51 52 create(context) { 53 54 //-------------------------------------------------------------------------- 55 // Constants 56 //-------------------------------------------------------------------------- 57 const option = context.options[0]; 58 let THRESHOLD = 10; 59 60 if ( 61 typeof option === "object" && 62 (Object.prototype.hasOwnProperty.call(option, "maximum") || Object.prototype.hasOwnProperty.call(option, "max")) 63 ) { 64 THRESHOLD = option.maximum || option.max; 65 } else if (typeof option === "number") { 66 THRESHOLD = option; 67 } 68 69 //-------------------------------------------------------------------------- 70 // Helpers 71 //-------------------------------------------------------------------------- 72 73 const callbackStack = []; 74 75 /** 76 * Checks a given function node for too many callbacks. 77 * @param {ASTNode} node The node to check. 78 * @returns {void} 79 * @private 80 */ 81 function checkFunction(node) { 82 const parent = node.parent; 83 84 if (parent.type === "CallExpression") { 85 callbackStack.push(node); 86 } 87 88 if (callbackStack.length > THRESHOLD) { 89 const opts = { num: callbackStack.length, max: THRESHOLD }; 90 91 context.report({ node, messageId: "exceed", data: opts }); 92 } 93 } 94 95 /** 96 * Pops the call stack. 97 * @returns {void} 98 * @private 99 */ 100 function popStack() { 101 callbackStack.pop(); 102 } 103 104 //-------------------------------------------------------------------------- 105 // Public API 106 //-------------------------------------------------------------------------- 107 108 return { 109 ArrowFunctionExpression: checkFunction, 110 "ArrowFunctionExpression:exit": popStack, 111 112 FunctionExpression: checkFunction, 113 "FunctionExpression:exit": popStack 114 }; 115 116 } 117}; 118