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