688
if (functionName !== null && isComponentName(functionName)) {
689
let isComponent =
690
callsHooksOrCreatesJsx(node, hookPattern) &&
691
- isValidComponentParams(node.get("params"));
691
+ isValidComponentParams(node.get("params")) &&
692
+ !returnsNonNode(node);
693
return isComponent ? "Component" : null;
694
} else if (functionName !== null && isHook(functionName, hookPattern)) {
695
// Hooks have hook invocations or JSX, but can take any # of arguments
709
return null;
710
}
711
712
+function skipNestedFunctions(
713
+ node: NodePath<
714
+ t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
715
+ >
716
+) {
717
+ return (
718
+ fn: NodePath<
719
+ t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
720
+ >
721
+ ): void => {
722
+ if (fn.node !== node.node) {
723
+ fn.skip();
724
+ }
725
+ };
726
+}
727
+
728
function callsHooksOrCreatesJsx(
712
- node: NodePath<t.Node>,
729
+ node: NodePath<
730
+ t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
731
+ >,
732
hookPattern: string | null
733
): boolean {
734
let invokesHooks = false;
735
let createsJsx = false;
736
+
737
node.traverse({
738
JSX() {
739
createsJsx = true;
744
invokesHooks = true;
745
}
746
},
747
+ ArrowFunctionExpression: skipNestedFunctions(node),
748
+ FunctionExpression: skipNestedFunctions(node),
749
+ FunctionDeclaration: skipNestedFunctions(node),
750
});
751
752
return invokesHooks || createsJsx;
753
}
754
755
+function returnsNonNode(
756
+ node: NodePath<
757
+ t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
758
+ >
759
+): boolean {
760
+ let hasReturn = false;
761
+ let returnsNonNode = false;
762
+
763
+ node.traverse({
764
+ ReturnStatement(ret) {
765
+ hasReturn = true;
766
+ const argument = ret.node.argument;
767
+ if (argument == null) {
768
+ returnsNonNode = true;
769
+ } else {
770
+ switch (argument.type) {
771
+ case "ObjectExpression":
772
+ case "ArrowFunctionExpression":
773
+ case "FunctionExpression":
774
+ case "BigIntLiteral":
775
+ case "ClassExpression":
776
+ case "NewExpression": // technically `new Array()` is legit, but unlikely
777
+ returnsNonNode = true;
778
+ }
779
+ }
780
+ },
781
+ ArrowFunctionExpression: skipNestedFunctions(node),
782
+ FunctionExpression: skipNestedFunctions(node),
783
+ FunctionDeclaration: skipNestedFunctions(node),
784
+ });
785
+
786
+ return !hasReturn || returnsNonNode;
787
+}
788
+
789
/*
790
* Gets the static name of a function AST node. For function declarations it is
791
* easy. For anonymous function expressions it is much harder. If you search for