66
77const util = require ( 'util' ) ;
88const doctrine = require ( 'doctrine' ) ;
9+ const arrayIncludes = require ( 'array-includes' ) ;
10+
911const variableUtil = require ( './variable' ) ;
1012const pragmaUtil = require ( './pragma' ) ;
1113const astUtil = require ( './ast' ) ;
@@ -253,18 +255,17 @@ function componentRule(rule, context) {
253255 } ,
254256
255257 /**
256- * Check if createElement is destructured from React import
258+ * Check if variable is destructured from React import
257259 *
260+ * @param {variable } String The variable name to check
258261 * @returns {Boolean } True if createElement is destructured from React
259262 */
260- hasDestructuredReactCreateElement : function ( ) {
263+ isDestructuredFromReactImport : function ( variable ) {
261264 const variables = variableUtil . variablesInScope ( context ) ;
262- const variable = variableUtil . getVariable ( variables , 'createElement' ) ;
263- if ( variable ) {
264- const map = variable . scope . set ;
265- if ( map . has ( 'React' ) ) {
266- return true ;
267- }
265+ const variableInScope = variableUtil . getVariable ( variables , variable ) ;
266+ if ( variableInScope ) {
267+ const map = variableInScope . scope . set ;
268+ return map . has ( 'React' ) ;
268269 }
269270 return false ;
270271 } ,
@@ -291,7 +292,7 @@ function componentRule(rule, context) {
291292 node . callee . name === 'createElement'
292293 ) ;
293294
294- if ( this . hasDestructuredReactCreateElement ( ) ) {
295+ if ( this . isDestructuredFromReactImport ( 'createElement' ) ) {
295296 return calledDirectly || calledOnReact ;
296297 }
297298 return calledOnReact ;
@@ -394,6 +395,18 @@ function componentRule(rule, context) {
394395 return utils . isReturningJSX ( ASTNode , strict ) || utils . isReturningNull ( ASTNode ) ;
395396 } ,
396397
398+ isReactComponentWrapper ( node ) {
399+ if ( node . type !== 'CallExpression' ) {
400+ return false ;
401+ }
402+ const propertyNames = [ 'forwardRef' , 'memo' ] ;
403+ const calleeObject = node . callee . object ;
404+ if ( calleeObject ) {
405+ return arrayIncludes ( propertyNames , node . callee . property . name ) && node . callee . object . name === 'React' ;
406+ }
407+ return arrayIncludes ( propertyNames , node . callee . name ) && this . isDestructuredFromReactImport ( node . callee . name ) ;
408+ } ,
409+
397410 /**
398411 * Find a return statment in the current node
399412 *
@@ -463,7 +476,7 @@ function componentRule(rule, context) {
463476 const enclosingScopeParent = enclosingScope && enclosingScope . block . parent ;
464477 const isClass = enclosingScope && astUtil . isClass ( enclosingScope . block ) ;
465478 const isMethod = enclosingScopeParent && enclosingScopeParent . type === 'MethodDefinition' ; // Classes methods
466- const isArgument = node . parent && node . parent . type === 'CallExpression' ; // Arguments (callback, etc.)
479+ const isArgument = node . parent && node . parent . type === 'CallExpression' && ! this . isReactComponentWrapper ( node . parent ) ; // Arguments (callback, etc.)
467480 // Attribute Expressions inside JSX Elements (<button onClick={() => props.handleClick()}></button>)
468481 const isJSXExpressionContainer = node . parent && node . parent . type === 'JSXExpressionContainer' ;
469482 // Stop moving up if we reach a class or an argument (like a callback)
0 commit comments