UNPKG

5.29 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 },
28
29 schema: [
30 {
31 oneOf: [
32 {
33 type: "integer",
34 minimum: 0
35 },
36 {
37 type: "object",
38 properties: {
39 maximum: {
40 type: "integer",
41 minimum: 0
42 },
43 max: {
44 type: "integer",
45 minimum: 0
46 }
47 },
48 additionalProperties: false
49 }
50 ]
51 }
52 ]
53 },
54
55 create(context) {
56 const option = context.options[0];
57 let THRESHOLD = 20;
58
59 if (typeof option === "object" && option.hasOwnProperty("maximum") && typeof option.maximum === "number") {
60 THRESHOLD = option.maximum;
61 }
62 if (typeof option === "object" && option.hasOwnProperty("max") && typeof option.max === "number") {
63 THRESHOLD = option.max;
64 }
65 if (typeof option === "number") {
66 THRESHOLD = option;
67 }
68
69 //--------------------------------------------------------------------------
70 // Helpers
71 //--------------------------------------------------------------------------
72
73 // Using a stack to store complexity (handling nested functions)
74 const fns = [];
75
76 /**
77 * When parsing a new function, store it in our function stack
78 * @returns {void}
79 * @private
80 */
81 function startFunction() {
82 fns.push(1);
83 }
84
85 /**
86 * Evaluate the node at the end of function
87 * @param {ASTNode} node node to evaluate
88 * @returns {void}
89 * @private
90 */
91 function endFunction(node) {
92 const name = lodash.upperFirst(astUtils.getFunctionNameWithKind(node));
93 const complexity = fns.pop();
94
95 if (complexity > THRESHOLD) {
96 context.report({
97 node,
98 message: "{{name}} has a complexity of {{complexity}}.",
99 data: { name, complexity }
100 });
101 }
102 }
103
104 /**
105 * Increase the complexity of the function in context
106 * @returns {void}
107 * @private
108 */
109 function increaseComplexity() {
110 if (fns.length) {
111 fns[fns.length - 1]++;
112 }
113 }
114
115 /**
116 * Increase the switch complexity in context
117 * @param {ASTNode} node node to evaluate
118 * @returns {void}
119 * @private
120 */
121 function increaseSwitchComplexity(node) {
122
123 // Avoiding `default`
124 if (node.test) {
125 increaseComplexity();
126 }
127 }
128
129 /**
130 * Increase the logical path complexity in context
131 * @param {ASTNode} node node to evaluate
132 * @returns {void}
133 * @private
134 */
135 function increaseLogicalComplexity(node) {
136
137 // Avoiding &&
138 if (node.operator === "||") {
139 increaseComplexity();
140 }
141 }
142
143 //--------------------------------------------------------------------------
144 // Public API
145 //--------------------------------------------------------------------------
146
147 return {
148 FunctionDeclaration: startFunction,
149 FunctionExpression: startFunction,
150 ArrowFunctionExpression: startFunction,
151 "FunctionDeclaration:exit": endFunction,
152 "FunctionExpression:exit": endFunction,
153 "ArrowFunctionExpression:exit": endFunction,
154
155 CatchClause: increaseComplexity,
156 ConditionalExpression: increaseComplexity,
157 LogicalExpression: increaseLogicalComplexity,
158 ForStatement: increaseComplexity,
159 ForInStatement: increaseComplexity,
160 ForOfStatement: increaseComplexity,
161 IfStatement: increaseComplexity,
162 SwitchCase: increaseSwitchComplexity,
163 WhileStatement: increaseComplexity,
164 DoWhileStatement: increaseComplexity
165 };
166
167 }
168};