@@ -46,6 +46,8 @@ function mergeUsedPropTypes(propsList, newPropsList) {
4646 return propsList . concat ( propsToAdd ) ;
4747}
4848
49+ const USE_HOOK_PREFIX_REGEX = / ^ u s e / i;
50+
4951const Lists = new WeakMap ( ) ;
5052const ReactImports = new WeakMap ( ) ;
5153
@@ -787,6 +789,75 @@ function componentRule(rule, context) {
787789 && ! ! ( node . params || [ ] ) . length
788790 ) ;
789791 } ,
792+
793+ isReactHookCall ( node , expectedHookNames ) {
794+ if ( node . type !== 'CallExpression' ) {
795+ return false ;
796+ }
797+
798+ const defaultReactImports = components . getDefaultReactImports ( ) ;
799+ const namedReactImports = components . getNamedReactImports ( ) ;
800+
801+ const defaultReactImportSpecifier = defaultReactImports
802+ ? defaultReactImports [ 0 ]
803+ : undefined ;
804+
805+ const defaultReactImportName = defaultReactImportSpecifier
806+ ? defaultReactImportSpecifier . local . name
807+ : undefined ;
808+
809+ const reactHookImportSpecifiers = namedReactImports
810+ ? namedReactImports . filter ( ( specifier ) => specifier . imported . name . match ( USE_HOOK_PREFIX_REGEX ) )
811+ : undefined ;
812+ const reactHookImportNames = reactHookImportSpecifiers
813+ ? reactHookImportSpecifiers . map ( ( specifier ) => specifier . local . name )
814+ : undefined ;
815+
816+ const isPotentialReactHookCall = ! ! (
817+ defaultReactImportName
818+ && node . callee . type === 'MemberExpression'
819+ && node . callee . object . type === 'Identifier'
820+ && node . callee . object . name === defaultReactImportName
821+ && node . callee . property . type === 'Identifier'
822+ && node . callee . property . name . match ( USE_HOOK_PREFIX_REGEX )
823+ ) ;
824+
825+ const isPotentialHookCall = ! ! (
826+ reactHookImportNames
827+ && node . callee . type === 'Identifier'
828+ && node . callee . name . match ( USE_HOOK_PREFIX_REGEX )
829+ ) ;
830+
831+ const scope = isPotentialReactHookCall || isPotentialHookCall
832+ ? context . getScope ( )
833+ : undefined ;
834+
835+ const reactResolvedDefs = isPotentialReactHookCall && scope . references . find (
836+ ( reference ) => reference . identifier . name === defaultReactImportName
837+ ) . resolved . defs ;
838+ const hookResolvedDefs = isPotentialHookCall && scope . references . find (
839+ ( reference ) => reactHookImportNames . includes ( reference . identifier . name )
840+ ) . resolved . defs ;
841+
842+ const hookName = ( isPotentialReactHookCall && node . callee . property . name )
843+ || ( isPotentialHookCall && node . callee . name ) ;
844+
845+ const isReactShadowed = isPotentialReactHookCall && reactResolvedDefs
846+ && reactResolvedDefs . some ( ( reactDef ) => reactDef . type !== 'ImportBinding' ) ;
847+
848+ const isHookShadowed = isPotentialHookCall
849+ && hookResolvedDefs
850+ && hookResolvedDefs . some (
851+ ( hookDef ) => hookDef . name . name === hookName
852+ && hookDef . type !== 'ImportBinding'
853+ ) ;
854+
855+ const isHookCall = ( isPotentialReactHookCall && ! isReactShadowed )
856+ || ( isPotentialHookCall && ! isHookShadowed ) ;
857+
858+ return isHookCall
859+ && ( ! expectedHookNames || expectedHookNames . includes ( hookName ) ) ;
860+ } ,
790861 } ;
791862
792863 // Component detection instructions
0 commit comments