2121
2222import { Rule } from "eslint" ;
2323import * as estree from "estree" ;
24- import { getParent , isIfStatement , isLogicalExpression } from "../utils/nodes" ;
24+ import { getParent , isArrowFunctionExpression , isIfStatement , isLogicalExpression } from "../utils/nodes" ;
2525import {
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 ) ;
0 commit comments