@@ -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