6
6
7
7
const util = require ( 'util' ) ;
8
8
const doctrine = require ( 'doctrine' ) ;
9
+ const arrayIncludes = require ( 'array-includes' ) ;
10
+
9
11
const variableUtil = require ( './variable' ) ;
10
12
const pragmaUtil = require ( './pragma' ) ;
11
13
const astUtil = require ( './ast' ) ;
@@ -253,18 +255,17 @@ function componentRule(rule, context) {
253
255
} ,
254
256
255
257
/**
256
- * Check if createElement is destructured from React import
258
+ * Check if variable is destructured from React import
257
259
*
260
+ * @param {variable } String The variable name to check
258
261
* @returns {Boolean } True if createElement is destructured from React
259
262
*/
260
- hasDestructuredReactCreateElement : function ( ) {
263
+ isDestructuredFromReactImport : function ( variable ) {
261
264
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' ) ;
268
269
}
269
270
return false ;
270
271
} ,
@@ -291,7 +292,7 @@ function componentRule(rule, context) {
291
292
node . callee . name === 'createElement'
292
293
) ;
293
294
294
- if ( this . hasDestructuredReactCreateElement ( ) ) {
295
+ if ( this . isDestructuredFromReactImport ( 'createElement' ) ) {
295
296
return calledDirectly || calledOnReact ;
296
297
}
297
298
return calledOnReact ;
@@ -394,6 +395,18 @@ function componentRule(rule, context) {
394
395
return utils . isReturningJSX ( ASTNode , strict ) || utils . isReturningNull ( ASTNode ) ;
395
396
} ,
396
397
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
+
397
410
/**
398
411
* Find a return statment in the current node
399
412
*
@@ -463,7 +476,7 @@ function componentRule(rule, context) {
463
476
const enclosingScopeParent = enclosingScope && enclosingScope . block . parent ;
464
477
const isClass = enclosingScope && astUtil . isClass ( enclosingScope . block ) ;
465
478
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.)
467
480
// Attribute Expressions inside JSX Elements (<button onClick={() => props.handleClick()}></button>)
468
481
const isJSXExpressionContainer = node . parent && node . parent . type === 'JSXExpressionContainer' ;
469
482
// Stop moving up if we reach a class or an argument (like a callback)
0 commit comments