Skip to content

Commit 4144ea4

Browse files
committed
[Test] Component util isReactHookCall
Rename Components test suite filename to match sibling lib/util/Components filename. Extend Components testComponentsDetect function to accept custom instructions, and to accumulate the results of processing those instructions. Add utility to check whether a CallExpression is a React hook call.
1 parent ac4e311 commit 4144ea4

File tree

3 files changed

+292
-98
lines changed

3 files changed

+292
-98
lines changed

lib/util/Components.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ function mergeUsedPropTypes(propsList, newPropsList) {
4646
return propsList.concat(propsToAdd);
4747
}
4848

49+
const USE_HOOK_PREFIX_REGEX = /^use/i;
50+
4951
const Lists = new WeakMap();
5052
const 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

tests/util/Component.js

Lines changed: 0 additions & 98 deletions
This file was deleted.

0 commit comments

Comments
 (0)