Skip to content
This repository was archived by the owner on Oct 3, 2024. It is now read-only.

Commit b0fada7

Browse files
Ignore React functional components in cognitive-complexity (#219)
1 parent 6c839a4 commit b0fada7

File tree

3 files changed

+74
-3
lines changed

3 files changed

+74
-3
lines changed

ruling/snapshots/cognitive-complexity

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,6 @@ src/react-router/website/modules/components/NativeExample.js: 12
413413
src/react-router/website/modules/components/WebExample.js: 13
414414
src/react/dangerfile.js: 99
415415
src/react/fixtures/attribute-behavior/src/App.js: 126,219,894
416-
src/react/fixtures/fiber-debugger/src/Fibers.js: 17
417416
src/react/packages/create-subscription/src/createSubscription.js: 16
418417
src/react/packages/events/ResponderEventPlugin.js: 315,476
419418
src/react/packages/events/ResponderTouchHistoryStore.js: 180

src/rules/cognitive-complexity.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
import { Rule } from "eslint";
2323
import * as estree from "estree";
24-
import { getParent, isIfStatement, isLogicalExpression } from "../utils/nodes";
24+
import { getParent, isArrowFunctionExpression, isIfStatement, isLogicalExpression } from "../utils/nodes";
2525
import {
2626
getMainFunctionTokenLocation,
2727
getFirstToken,
@@ -72,6 +72,21 @@ const rule: Rule.RuleModule = {
7272
/** Indicator if the current top level function has a structural (generated by control flow statements) complexity */
7373
let topLevelHasStructuralComplexity = false;
7474

75+
/** Indicator if the current top level function is React functional component */
76+
const reactFunctionalComponent = {
77+
nameStartsWithCapital: false,
78+
returnsJsx: false,
79+
80+
isConfirmed() {
81+
return this.nameStartsWithCapital && this.returnsJsx;
82+
},
83+
84+
init(_node: estree.Function) {
85+
this.nameStartsWithCapital = nameStartsWithCapital(_node);
86+
this.returnsJsx = false;
87+
},
88+
};
89+
7590
/** Own (not including nested functions) complexity of the current top function */
7691
let topLevelOwnComplexity: ComplexityPoint[] = [];
7792

@@ -158,6 +173,9 @@ const rule: Rule.RuleModule = {
158173
ConditionalExpression(node: estree.Node) {
159174
visitConditionalExpression(node as estree.ConditionalExpression);
160175
},
176+
ReturnStatement(node: estree.Node) {
177+
visitReturnStatement(node as estree.ReturnStatement);
178+
},
161179
};
162180

163181
function getThreshold() {
@@ -168,6 +186,7 @@ const rule: Rule.RuleModule = {
168186
if (enclosingFunctions.length === 0) {
169187
// top level function
170188
topLevelHasStructuralComplexity = false;
189+
reactFunctionalComponent.init(node);
171190
topLevelOwnComplexity = [];
172191
secondLevelFunctions = [];
173192
} else if (enclosingFunctions.length === 1) {
@@ -185,7 +204,7 @@ const rule: Rule.RuleModule = {
185204
enclosingFunctions.pop();
186205
if (enclosingFunctions.length === 0) {
187206
// top level function
188-
if (topLevelHasStructuralComplexity) {
207+
if (topLevelHasStructuralComplexity && !reactFunctionalComponent.isConfirmed()) {
189208
let totalComplexity = topLevelOwnComplexity;
190209
secondLevelFunctions.forEach(secondLevelFunction => {
191210
totalComplexity = totalComplexity.concat(secondLevelFunction.complexityIfNested);
@@ -268,6 +287,31 @@ const rule: Rule.RuleModule = {
268287
nestingNodes.add(conditionalExpression.alternate);
269288
}
270289

290+
function visitReturnStatement({ argument }: estree.ReturnStatement) {
291+
// top level function
292+
if (enclosingFunctions.length === 1 && argument && (argument.type as any) === "JSXElement") {
293+
reactFunctionalComponent.returnsJsx = true;
294+
}
295+
}
296+
297+
function nameStartsWithCapital(node: estree.Function) {
298+
const checkFirstLetter = (name: string) => {
299+
const firstLetter = name[0];
300+
return firstLetter === firstLetter.toUpperCase();
301+
};
302+
303+
if (!isArrowFunctionExpression(node) && node.id) {
304+
return checkFirstLetter(node.id.name);
305+
}
306+
307+
const parent = getParent(context);
308+
if (parent && parent.type === "VariableDeclarator" && parent.id.type === "Identifier") {
309+
return checkFirstLetter(parent.id.name);
310+
}
311+
312+
return false;
313+
}
314+
271315
function visitLogicalExpression(logicalExpression: estree.LogicalExpression) {
272316
if (!consideredLogicalExpressions.has(logicalExpression)) {
273317
const flattenedLogicalExpressions = flattenLogicalExpression(logicalExpression);

tests/rules/cognitive-complexity.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,34 @@ ruleTester.run("cognitive-complexity", rule, {
450450
options: [0],
451451
errors: [message(1)],
452452
},
453+
454+
// ignore React functional components
455+
{
456+
code: `
457+
function Welcome() {
458+
const handleSomething = () => {
459+
if (x) {} // +1
460+
}
461+
if (x) {} // +1
462+
return <h1>Hello, world</h1>;
463+
}`,
464+
parserOptions: { ecmaFeatures: { jsx: true } },
465+
options: [0],
466+
errors: [message(1, { line: 2 }), message(1, { line: 3 })],
467+
},
468+
{
469+
code: `
470+
const Welcome = () => {
471+
const handleSomething = () => {
472+
if (x) {} // +1
473+
}
474+
if (x) {} // +1
475+
return <h1>Hello, world</h1>;
476+
}`,
477+
parserOptions: { ecmaFeatures: { jsx: true } },
478+
options: [0],
479+
errors: [message(1, { line: 2 }), message(1, { line: 3 })],
480+
},
453481
],
454482
});
455483

0 commit comments

Comments
 (0)