UNPKG

5.04 kBJavaScriptView Raw
1/**
2 * @fileoverview Counts the cyclomatic complexity of each function of the script. See http://en.wikipedia.org/wiki/Cyclomatic_complexity.
3 * Counts the number of if, conditional, for, whilte, try, switch/case,
4 * @author Patrick Brosset
5 */
6
7"use strict";
8
9//------------------------------------------------------------------------------
10// Requirements
11//------------------------------------------------------------------------------
12
13const lodash = require("lodash");
14
15const astUtils = require("../ast-utils");
16
17//------------------------------------------------------------------------------
18// Rule Definition
19//------------------------------------------------------------------------------
20
21module.exports = {
22 meta: {
23 docs: {
24 description: "enforce a maximum cyclomatic complexity allowed in a program",
25 category: "Best Practices",
26 recommended: false,
27 url: "https://eslint.org/docs/rules/complexity"
28 },
29
30 schema: [
31 {
32 oneOf: [
33 {
34 type: "integer",
35 minimum: 0
36 },
37 {
38 type: "object",
39 properties: {
40 maximum: {
41 type: "integer",
42 minimum: 0
43 },
44 max: {
45 type: "integer",
46 minimum: 0
47 }
48 },
49 additionalProperties: false
50 }
51 ]
52 }
53 ],
54
55 messages: {
56 complex: "{{name}} has a complexity of {{complexity}}."
57 }
58 },
59
60 create(context) {
61 const option = context.options[0];
62 let THRESHOLD = 20;
63
64 if (typeof option === "object" && option.hasOwnProperty("maximum") && typeof option.maximum === "number") {
65 THRESHOLD = option.maximum;
66 }
67 if (typeof option === "object" && option.hasOwnProperty("max") && typeof option.max === "number") {
68 THRESHOLD = option.max;
69 }
70 if (typeof option === "number") {
71 THRESHOLD = option;
72 }
73
74 //--------------------------------------------------------------------------
75 // Helpers
76 //--------------------------------------------------------------------------
77
78 // Using a stack to store complexity (handling nested functions)
79 const fns = [];
80
81 /**
82 * When parsing a new function, store it in our function stack
83 * @returns {void}
84 * @private
85 */
86 function startFunction() {
87 fns.push(1);
88 }
89
90 /**
91 * Evaluate the node at the end of function
92 * @param {ASTNode} node node to evaluate
93 * @returns {void}
94 * @private
95 */
96 function endFunction(node) {
97 const name = lodash.upperFirst(astUtils.getFunctionNameWithKind(node));
98 const complexity = fns.pop();
99
100 if (complexity > THRESHOLD) {
101 context.report({
102 node,
103 messageId: "complex",
104 data: { name, complexity }
105 });
106 }
107 }
108
109 /**
110 * Increase the complexity of the function in context
111 * @returns {void}
112 * @private
113 */
114 function increaseComplexity() {
115 if (fns.length) {
116 fns[fns.length - 1]++;
117 }
118 }
119
120 /**
121 * Increase the switch complexity in context
122 * @param {ASTNode} node node to evaluate
123 * @returns {void}
124 * @private
125 */
126 function increaseSwitchComplexity(node) {
127
128 // Avoiding `default`
129 if (node.test) {
130 increaseComplexity();
131 }
132 }
133
134 //--------------------------------------------------------------------------
135 // Public API
136 //--------------------------------------------------------------------------
137
138 return {
139 FunctionDeclaration: startFunction,
140 FunctionExpression: startFunction,
141 ArrowFunctionExpression: startFunction,
142 "FunctionDeclaration:exit": endFunction,
143 "FunctionExpression:exit": endFunction,
144 "ArrowFunctionExpression:exit": endFunction,
145
146 CatchClause: increaseComplexity,
147 ConditionalExpression: increaseComplexity,
148 LogicalExpression: increaseComplexity,
149 ForStatement: increaseComplexity,
150 ForInStatement: increaseComplexity,
151 ForOfStatement: increaseComplexity,
152 IfStatement: increaseComplexity,
153 SwitchCase: increaseSwitchComplexity,
154 WhileStatement: increaseComplexity,
155 DoWhileStatement: increaseComplexity
156 };
157
158 }
159};