@@ -577,50 +577,67 @@ function componentRule(rule, context) {
577577 } ,
578578
579579 /**
580- * Get the parent stateless component node from the current scope
581- *
582- * @returns {ASTNode } component node, null if we are not in a component
580+ * @param {ASTNode } node
581+ * @returns {boolean }
583582 */
584- getParentStatelessComponent ( ) {
585- let scope = context . getScope ( ) ;
586- while ( scope ) {
587- const node = scope . block ;
588- const isFunction = / F u n c t i o n / . test ( node . type ) ; // Functions
589- const isArrowFunction = astUtil . isArrowFunction ( node ) ;
590- const enclosingScope = isArrowFunction ? utils . getArrowFunctionScope ( scope ) : scope ;
591- const enclosingScopeParent = enclosingScope && enclosingScope . block . parent ;
592- const isClass = enclosingScope && astUtil . isClass ( enclosingScope . block ) ;
593- const isMethod = enclosingScopeParent && enclosingScopeParent . type === 'MethodDefinition' ; // Classes methods
594- const isArgument = node . parent && node . parent . type === 'CallExpression' ; // Arguments (callback, etc.)
595- // Attribute Expressions inside JSX Elements (<button onClick={() => props.handleClick()}></button>)
596- const isJSXExpressionContainer = node . parent && node . parent . type === 'JSXExpressionContainer' ;
597- const pragmaComponentWrapper = this . getPragmaComponentWrapper ( node ) ;
598- if ( isFunction && pragmaComponentWrapper ) {
599- return pragmaComponentWrapper ;
583+ isInAllowedPositionForComponent ( node ) {
584+ switch ( node . parent . type ) {
585+ case 'VariableDeclarator' :
586+ case 'AssignmentExpression' :
587+ case 'Property' :
588+ case 'ReturnStatement' :
589+ case 'ExportDefaultDeclaration' : {
590+ return true ;
600591 }
601- // Stop moving up if we reach a class or an argument (like a callback)
602- if ( isClass || isArgument ) {
603- return null ;
592+ case 'SequenceExpression' : {
593+ return utils . isInAllowedPositionForComponent ( node . parent ) &&
594+ node === node . parent . expressions [ node . parent . expressions . length - 1 ] ;
604595 }
605- // Return the node if it is a function that is not a class method and is not inside a JSX Element
606- if ( isFunction && ! isMethod && ! isJSXExpressionContainer && utils . isReturningJSXOrNull ( node ) ) {
596+ default :
597+ return false ;
598+ }
599+ } ,
600+
601+ /**
602+ * Get node if node is a stateless component, or node.parent in cases like
603+ * `React.memo` or `React.forwardRef`. Otherwise returns `undefined`.
604+ * @param {ASTNode } node
605+ * @returns {ASTNode | undefined }
606+ */
607+ getStatelessComponent ( node ) {
608+ if ( node . type === 'FunctionDeclaration' ) {
609+ if ( utils . isReturningJSXOrNull ( node ) ) {
607610 return node ;
608611 }
609- scope = scope . upper ;
610612 }
611- return null ;
613+
614+ if ( node . type === 'FunctionExpression' || node . type === 'ArrowFunctionExpression' ) {
615+ if ( utils . isInAllowedPositionForComponent ( node ) && utils . isReturningJSXOrNull ( node ) ) {
616+ return node ;
617+ }
618+
619+ // Case like `React.memo(() => <></>)` or `React.forwardRef(...)`
620+ const pragmaComponentWrapper = utils . getPragmaComponentWrapper ( node ) ;
621+ if ( pragmaComponentWrapper ) {
622+ return pragmaComponentWrapper ;
623+ }
624+ }
625+
626+ return undefined ;
612627 } ,
613628
614629 /**
615- * Get an enclosing scope used to find `this` value by an arrow function
616- * @param { Scope } scope Current scope
617- * @returns {Scope } An enclosing scope used by an arrow function
630+ * Get the parent stateless component node from the current scope
631+ *
632+ * @returns {ASTNode } component node, null if we are not in a component
618633 */
619- getArrowFunctionScope ( scope ) {
620- scope = scope . upper ;
634+ getParentStatelessComponent ( ) {
635+ let scope = context . getScope ( ) ;
621636 while ( scope ) {
622- if ( astUtil . isFunction ( scope . block ) || astUtil . isClass ( scope . block ) ) {
623- return scope ;
637+ const node = scope . block ;
638+ const statelessComponent = utils . getStatelessComponent ( node ) ;
639+ if ( statelessComponent ) {
640+ return statelessComponent ;
624641 }
625642 scope = scope . upper ;
626643 }
0 commit comments