blob: df24a96da583954eafe907366a328ab347096cb2 [file] [log] [blame]
Yang Guo4fd355c2019-09-19 10:59:03 +02001/**
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",
Yang Guo4fd355c2019-09-19 10:59:03 +020018 recommended: false,
19 url: "https://eslint.org/docs/rules/max-nested-callbacks"
20 },
21
22 schema: [
23 {
24 oneOf: [
25 {
26 type: "integer",
27 minimum: 0
28 },
29 {
30 type: "object",
31 properties: {
32 maximum: {
33 type: "integer",
34 minimum: 0
35 },
36 max: {
37 type: "integer",
38 minimum: 0
39 }
40 },
41 additionalProperties: false
42 }
43 ]
44 }
45 ],
46 messages: {
47 exceed: "Too many nested callbacks ({{num}}). Maximum allowed is {{max}}."
48 }
49 },
50
51 create(context) {
52
53 //--------------------------------------------------------------------------
54 // Constants
55 //--------------------------------------------------------------------------
56 const option = context.options[0];
57 let THRESHOLD = 10;
58
59 if (
60 typeof option === "object" &&
61 (Object.prototype.hasOwnProperty.call(option, "maximum") || Object.prototype.hasOwnProperty.call(option, "max"))
62 ) {
63 THRESHOLD = option.maximum || option.max;
64 } else if (typeof option === "number") {
65 THRESHOLD = option;
66 }
67
68 //--------------------------------------------------------------------------
69 // Helpers
70 //--------------------------------------------------------------------------
71
72 const callbackStack = [];
73
74 /**
75 * Checks a given function node for too many callbacks.
76 * @param {ASTNode} node The node to check.
77 * @returns {void}
78 * @private
79 */
80 function checkFunction(node) {
81 const parent = node.parent;
82
83 if (parent.type === "CallExpression") {
84 callbackStack.push(node);
85 }
86
87 if (callbackStack.length > THRESHOLD) {
88 const opts = { num: callbackStack.length, max: THRESHOLD };
89
90 context.report({ node, messageId: "exceed", data: opts });
91 }
92 }
93
94 /**
95 * Pops the call stack.
96 * @returns {void}
97 * @private
98 */
99 function popStack() {
100 callbackStack.pop();
101 }
102
103 //--------------------------------------------------------------------------
104 // Public API
105 //--------------------------------------------------------------------------
106
107 return {
108 ArrowFunctionExpression: checkFunction,
109 "ArrowFunctionExpression:exit": popStack,
110
111 FunctionExpression: checkFunction,
112 "FunctionExpression:exit": popStack
113 };
114
115 }
116};