Skip to content

Commit 1b1a41b

Browse files
committed
tweaks
1 parent 9b1638f commit 1b1a41b

File tree

1 file changed

+61
-192
lines changed

1 file changed

+61
-192
lines changed

codemods/v14-async-functions/scripts/codemod.ts

Lines changed: 61 additions & 192 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,11 @@ export default async function transform(
110110
functionCalls.push(...findFireEventMethodCalls(rootNode, importedFunctions, rntlImports));
111111
functionCalls.push(...findScreenMethodCalls(rootNode));
112112

113-
const rendererVariables = trackVariablesAssignedFromRender(rootNode, importedFunctions);
114-
functionCalls.push(...findRendererMethodCalls(rootNode, rendererVariables));
115-
116-
const { renderHookVariables, renderHookMethodVariables } = trackVariablesAssignedFromRenderHook(
113+
const { allVariables, renamedMethodVariables } = trackVariablesAssignedFromRenderAndRenderHook(
117114
rootNode,
118115
importedFunctions,
119116
);
120-
functionCalls.push(
121-
...findRenderHookMethodCalls(rootNode, renderHookVariables, renderHookMethodVariables),
122-
);
117+
functionCalls.push(...findResultMethodCalls(rootNode, allVariables, renamedMethodVariables));
123118

124119
if (functionCalls.length === 0 && finalCustomRenderFunctionsSet.size === 0) {
125120
if (edits.length === 0) {
@@ -558,193 +553,53 @@ function findScreenMethodCalls(rootNode: SgNode<TSX>): SgNode<TSX>[] {
558553
}
559554

560555
/**
561-
* Tracks variables assigned from render() calls to identify renderer result objects.
562-
* This helps identify calls like `renderer.rerender()` or `renderer.unmount()` that need to be made async.
563-
*
564-
* Handles various assignment patterns:
565-
* - Direct assignment: `const renderer = render(...)`
566-
* - Destructured assignment: `const { rerender } = render(...)`
567-
* - Assignment expressions: `renderer = render(...)`
568-
*
569-
* @param rootNode - The root AST node to search within
570-
* @param importedFunctions - Set of imported function names (must include 'render')
571-
* @returns Set of variable names that represent renderer results
572-
*/
573-
function trackVariablesAssignedFromRender(
574-
rootNode: SgNode<TSX>,
575-
importedFunctions: Set<string>,
576-
): Set<string> {
577-
const rendererVariables = new Set<string>();
578-
579-
if (importedFunctions.has('render')) {
580-
const renderCalls = rootNode.findAll({
581-
rule: {
582-
kind: 'call_expression',
583-
has: {
584-
field: 'function',
585-
kind: 'identifier',
586-
regex: '^render$',
587-
},
588-
},
589-
});
590-
591-
for (const renderCall of renderCalls) {
592-
let parent = renderCall.parent();
593-
const isAwaited = parent && parent.is('await_expression');
594-
595-
if (isAwaited) {
596-
parent = parent.parent();
597-
}
598-
599-
if (parent && parent.is('variable_declarator')) {
600-
const objectPattern = parent.find({
601-
rule: { kind: 'object_pattern' },
602-
});
603-
if (objectPattern) {
604-
const shorthandProps = objectPattern.findAll({
605-
rule: { kind: 'shorthand_property_identifier_pattern' },
606-
});
607-
for (const prop of shorthandProps) {
608-
const propName = prop.text();
609-
if (RESULT_METHODS_TO_MAKE_ASYNC.has(propName)) {
610-
rendererVariables.add(propName);
611-
}
612-
}
613-
} else {
614-
const nameNode = parent.find({
615-
rule: { kind: 'identifier' },
616-
});
617-
if (nameNode) {
618-
const varName = nameNode.text();
619-
rendererVariables.add(varName);
620-
}
621-
}
622-
} else if (parent && parent.is('assignment_expression')) {
623-
const left = parent.find({
624-
rule: { kind: 'identifier' },
625-
});
626-
if (left) {
627-
const varName = left.text();
628-
rendererVariables.add(varName);
629-
} else {
630-
const objectPattern = parent.find({
631-
rule: { kind: 'object_pattern' },
632-
});
633-
if (objectPattern) {
634-
const shorthandProps = objectPattern.findAll({
635-
rule: { kind: 'shorthand_property_identifier_pattern' },
636-
});
637-
for (const prop of shorthandProps) {
638-
const propName = prop.text();
639-
if (RESULT_METHODS_TO_MAKE_ASYNC.has(propName)) {
640-
rendererVariables.add(propName);
641-
}
642-
}
643-
}
644-
}
645-
}
646-
}
647-
}
648-
649-
return rendererVariables;
650-
}
651-
652-
function findRendererMethodCalls(
653-
rootNode: SgNode<TSX>,
654-
rendererVariables: Set<string>,
655-
): SgNode<TSX>[] {
656-
const functionCalls: SgNode<TSX>[] = [];
657-
658-
if (rendererVariables.size > 0) {
659-
const rendererMethodCalls = rootNode.findAll({
660-
rule: {
661-
kind: 'call_expression',
662-
has: {
663-
field: 'function',
664-
kind: 'member_expression',
665-
},
666-
},
667-
});
668-
669-
for (const call of rendererMethodCalls) {
670-
const funcNode = call.field('function');
671-
if (funcNode && funcNode.is('member_expression')) {
672-
try {
673-
const object = funcNode.field('object');
674-
const property = funcNode.field('property');
675-
if (object && property) {
676-
const objText = object.text();
677-
const propText = property.text();
678-
if (rendererVariables.has(objText) && RESULT_METHODS_TO_MAKE_ASYNC.has(propText)) {
679-
functionCalls.push(call);
680-
}
681-
}
682-
} catch {
683-
// Skip nodes where field() is not available or AST structure doesn't match expectations.
684-
// This is expected for malformed or edge-case AST structures and should be silently ignored.
685-
}
686-
}
687-
}
688-
689-
for (const varName of rendererVariables) {
690-
if (RESULT_METHODS_TO_MAKE_ASYNC.has(varName)) {
691-
const directCalls = rootNode.findAll({
692-
rule: {
693-
kind: 'call_expression',
694-
has: {
695-
field: 'function',
696-
kind: 'identifier',
697-
regex: `^${varName}$`,
698-
},
699-
},
700-
});
701-
functionCalls.push(...directCalls);
702-
}
703-
}
704-
}
705-
706-
return functionCalls;
707-
}
708-
709-
/**
710-
* Tracks variables assigned from renderHook() calls to identify hook result objects.
711-
* Similar to trackVariablesAssignedFromRender but handles renderHook-specific patterns.
556+
* Tracks variables assigned from render() and renderHook() calls to identify result objects.
557+
* This helps identify calls like `renderer.rerender()`, `renderer.unmount()`, `result.rerender()`, etc.
558+
* that need to be made async.
712559
*
713560
* Handles various assignment patterns:
714-
* - Direct assignment: `const result = renderHook(...)`
715-
* - Destructured assignment: `const { rerender, unmount } = renderHook(...)`
716-
* - Renamed destructuring: `const { rerender: rerenderHook } = renderHook(...)`
561+
* - Direct assignment: `const renderer = render(...)` or `const result = renderHook(...)`
562+
* - Destructured assignment: `const { rerender } = render(...)` or `const { rerender } = renderHook(...)`
563+
* - Renamed destructuring: `const { rerender: rerenderHook } = renderHook(...)` (renderHook only)
564+
* - Assignment expressions: `renderer = render(...)` or `result = renderHook(...)`
717565
*
718566
* @param rootNode - The root AST node to search within
719-
* @param importedFunctions - Set of imported function names (must include 'renderHook')
567+
* @param importedFunctions - Set of imported function names (should include 'render' and/or 'renderHook')
720568
* @returns Object containing:
721-
* - renderHookVariables: Set of all variable names representing hook results
722-
* - renderHookMethodVariables: Set of renamed method variables (e.g., rerenderHook)
569+
* - allVariables: Set of all variable names representing render/renderHook results
570+
* - renamedMethodVariables: Set of renamed method variables (e.g., rerenderHook from renderHook)
723571
*/
724-
function trackVariablesAssignedFromRenderHook(
572+
function trackVariablesAssignedFromRenderAndRenderHook(
725573
rootNode: SgNode<TSX>,
726574
importedFunctions: Set<string>,
727575
): {
728-
renderHookVariables: Set<string>;
729-
renderHookMethodVariables: Set<string>;
576+
allVariables: Set<string>;
577+
renamedMethodVariables: Set<string>;
730578
} {
731-
const renderHookVariables = new Set<string>();
732-
const renderHookMethodVariables = new Set<string>();
579+
const allVariables = new Set<string>();
580+
const renamedMethodVariables = new Set<string>();
581+
582+
// Track variables from both render() and renderHook() calls
583+
const functionsToTrack = ['render', 'renderHook'] as const;
733584

734-
if (importedFunctions.has('renderHook')) {
735-
const renderHookCalls = rootNode.findAll({
585+
for (const funcName of functionsToTrack) {
586+
if (!importedFunctions.has(funcName)) {
587+
continue;
588+
}
589+
590+
const functionCalls = rootNode.findAll({
736591
rule: {
737592
kind: 'call_expression',
738593
has: {
739594
field: 'function',
740595
kind: 'identifier',
741-
regex: '^renderHook$',
596+
regex: `^${funcName}$`,
742597
},
743598
},
744599
});
745600

746-
for (const renderHookCall of renderHookCalls) {
747-
let parent = renderHookCall.parent();
601+
for (const functionCall of functionCalls) {
602+
let parent = functionCall.parent();
748603
const isAwaited = parent && parent.is('await_expression');
749604

750605
if (isAwaited) {
@@ -762,9 +617,10 @@ function trackVariablesAssignedFromRenderHook(
762617
for (const prop of shorthandProps) {
763618
const propName = prop.text();
764619
if (RESULT_METHODS_TO_MAKE_ASYNC.has(propName)) {
765-
renderHookVariables.add(propName);
620+
allVariables.add(propName);
766621
}
767622
}
623+
// Handle renamed destructuring (only for renderHook, but we check for both to be safe)
768624
const pairPatterns = objectPattern.findAll({
769625
rule: { kind: 'pair_pattern' },
770626
});
@@ -779,8 +635,8 @@ function trackVariablesAssignedFromRenderHook(
779635
const keyName = key.text();
780636
const valueName = value.text();
781637
if (RESULT_METHODS_TO_MAKE_ASYNC.has(keyName)) {
782-
renderHookVariables.add(valueName);
783-
renderHookMethodVariables.add(valueName);
638+
allVariables.add(valueName);
639+
renamedMethodVariables.add(valueName);
784640
}
785641
}
786642
}
@@ -790,7 +646,7 @@ function trackVariablesAssignedFromRenderHook(
790646
});
791647
if (nameNode) {
792648
const varName = nameNode.text();
793-
renderHookVariables.add(varName);
649+
allVariables.add(varName);
794650
}
795651
}
796652
} else if (parent && parent.is('assignment_expression')) {
@@ -799,7 +655,7 @@ function trackVariablesAssignedFromRenderHook(
799655
});
800656
if (left) {
801657
const varName = left.text();
802-
renderHookVariables.add(varName);
658+
allVariables.add(varName);
803659
} else {
804660
const objectPattern = parent.find({
805661
rule: { kind: 'object_pattern' },
@@ -811,9 +667,10 @@ function trackVariablesAssignedFromRenderHook(
811667
for (const prop of shorthandProps) {
812668
const propName = prop.text();
813669
if (RESULT_METHODS_TO_MAKE_ASYNC.has(propName)) {
814-
renderHookVariables.add(propName);
670+
allVariables.add(propName);
815671
}
816672
}
673+
// Handle renamed destructuring in assignment expressions
817674
const pairPatterns = objectPattern.findAll({
818675
rule: { kind: 'pair_pattern' },
819676
});
@@ -828,8 +685,8 @@ function trackVariablesAssignedFromRenderHook(
828685
const keyName = key.text();
829686
const valueName = value.text();
830687
if (RESULT_METHODS_TO_MAKE_ASYNC.has(keyName)) {
831-
renderHookVariables.add(valueName);
832-
renderHookMethodVariables.add(valueName);
688+
allVariables.add(valueName);
689+
renamedMethodVariables.add(valueName);
833690
}
834691
}
835692
}
@@ -839,18 +696,27 @@ function trackVariablesAssignedFromRenderHook(
839696
}
840697
}
841698

842-
return { renderHookVariables, renderHookMethodVariables };
699+
return { allVariables, renamedMethodVariables };
843700
}
844701

845-
function findRenderHookMethodCalls(
702+
/**
703+
* Finds method calls on render/renderHook result variables (e.g., renderer.rerender(), result.unmount()).
704+
* Also finds direct calls to renamed method variables (e.g., rerenderHook()).
705+
*
706+
* @param rootNode - The root AST node to search within
707+
* @param allVariables - Set of all variable names from render/renderHook results
708+
* @param renamedMethodVariables - Set of renamed method variables (e.g., rerenderHook)
709+
* @returns Array of function call nodes that need to be made async
710+
*/
711+
function findResultMethodCalls(
846712
rootNode: SgNode<TSX>,
847-
renderHookVariables: Set<string>,
848-
renderHookMethodVariables: Set<string>,
713+
allVariables: Set<string>,
714+
renamedMethodVariables: Set<string>,
849715
): SgNode<TSX>[] {
850716
const functionCalls: SgNode<TSX>[] = [];
851717

852-
if (renderHookVariables.size > 0) {
853-
const renderHookMethodCalls = rootNode.findAll({
718+
if (allVariables.size > 0) {
719+
const resultMethodCalls = rootNode.findAll({
854720
rule: {
855721
kind: 'call_expression',
856722
has: {
@@ -860,7 +726,7 @@ function findRenderHookMethodCalls(
860726
},
861727
});
862728

863-
for (const call of renderHookMethodCalls) {
729+
for (const call of resultMethodCalls) {
864730
const funcNode = call.field('function');
865731
if (funcNode && funcNode.is('member_expression')) {
866732
try {
@@ -869,7 +735,7 @@ function findRenderHookMethodCalls(
869735
if (object && property) {
870736
const objText = object.text();
871737
const propText = property.text();
872-
if (renderHookVariables.has(objText) && RESULT_METHODS_TO_MAKE_ASYNC.has(propText)) {
738+
if (allVariables.has(objText) && RESULT_METHODS_TO_MAKE_ASYNC.has(propText)) {
873739
functionCalls.push(call);
874740
}
875741
}
@@ -880,8 +746,9 @@ function findRenderHookMethodCalls(
880746
}
881747
}
882748

883-
for (const varName of renderHookVariables) {
884-
if (RESULT_METHODS_TO_MAKE_ASYNC.has(varName) || renderHookMethodVariables.has(varName)) {
749+
// Find direct calls to method variables (e.g., rerender(), unmount(), rerenderHook())
750+
for (const varName of allVariables) {
751+
if (RESULT_METHODS_TO_MAKE_ASYNC.has(varName) || renamedMethodVariables.has(varName)) {
885752
const directCalls = rootNode.findAll({
886753
rule: {
887754
kind: 'call_expression',
@@ -900,6 +767,8 @@ function findRenderHookMethodCalls(
900767
return functionCalls;
901768
}
902769

770+
771+
903772
/**
904773
* Automatically detects custom render functions by analyzing the code structure.
905774
* A custom render function is identified as:

0 commit comments

Comments
 (0)