77from __future__ import annotations
88
99import logging
10+ import re
1011from dataclasses import dataclass , field
1112from typing import TYPE_CHECKING
1213
14+ from codeflash .languages .javascript .frameworks .react .analyzer import detect_optimization_opportunities
15+
1316if TYPE_CHECKING :
1417 from pathlib import Path
1518
1922 from codeflash .languages .javascript .frameworks .react .discovery import ReactComponentInfo
2023 from codeflash .languages .javascript .treesitter import TreeSitterAnalyzer
2124
25+ _HOOK_PATTERN = re .compile (r"\b(use[A-Z]\w*)\s*(?:<[^>]*>)?\s*\(" )
26+
27+ _JSX_COMPONENT_RE = re .compile (r"<([A-Z][a-zA-Z0-9.]*)" )
28+
29+ _CONTEXT_RE = re .compile (r"\buseContext\s*\(\s*(\w+)" )
30+
2231logger = logging .getLogger (__name__ )
2332
2433
@@ -83,8 +92,6 @@ def extract_react_context(
8392 Analyzes the component source to find props types, hooks, child components,
8493 and optimization opportunities.
8594 """
86- from codeflash .languages .javascript .frameworks .react .analyzer import detect_optimization_opportunities
87-
8895 context = ReactContext (props_interface = component_info .props_type , is_already_memoized = component_info .is_memoized )
8996
9097 # Extract hook usage details from the component source
@@ -109,56 +116,55 @@ def extract_react_context(
109116
110117def _extract_hook_usages (component_source : str ) -> list [HookUsage ]:
111118 """Parse hook calls and their dependency arrays from component source."""
112- import re
113-
114119 hooks : list [HookUsage ] = []
115- # Match useXxx( patterns
116- hook_pattern = re . compile ( r"\b(use[A-Z]\w*)\s*(?:<[^>]*>)?\s*\(" )
120+ cs = component_source
121+ n = len ( cs )
117122
118- for match in hook_pattern .finditer (component_source ):
123+ # Use precompiled pattern
124+ for match in _HOOK_PATTERN .finditer (cs ):
119125 hook_name = match .group (1 )
120- # Try to determine if there's a dependency array
121- # Look for ], [ pattern after the hook call (simplified heuristic)
122- rest_of_line = component_source [match .end () :]
123126 has_deps = False
124127 dep_count = 0
125128
126129 # Simple heuristic: count brackets to find dependency array
127130 bracket_depth = 1
128- for i , char in enumerate (rest_of_line ):
131+ j = match .end ()
132+ # Scan by index to avoid allocating a new substring for the rest_of_line on each iteration
133+ while j < n :
134+ char = cs [j ]
129135 if char == "(" :
130136 bracket_depth += 1
131137 elif char == ")" :
132138 bracket_depth -= 1
133139 if bracket_depth == 0 :
134- # Check if the last argument before closing paren is an array
135- preceding = rest_of_line [:i ].rstrip ()
136- if preceding .endswith ("]" ):
140+ # Find last non-space character before this closing paren
141+ k = j - 1
142+ while k >= match .end () and cs [k ].isspace ():
143+ k -= 1
144+ if k >= match .end () and cs [k ] == "]" :
137145 has_deps = True
138- # Count items in the array (rough: count commas + 1 for non-empty)
139- array_start = preceding .rfind ("[" )
146+ # Find the opening '[' for the dependency array within the search window
147+ array_start = cs .rfind ("[" , match . end (), k + 1 )
140148 if array_start >= 0 :
141- array_content = preceding [array_start + 1 : - 1 ].strip ()
149+ array_content = cs [array_start + 1 : k ].strip ()
142150 if array_content :
143151 dep_count = array_content .count ("," ) + 1
144152 else :
145153 dep_count = 0 # empty deps []
146154 has_deps = True
147155 break
148156
157+ j += 1
158+
149159 hooks .append (HookUsage (name = hook_name , has_dependency_array = has_deps , dependency_count = dep_count ))
150160
151161 return hooks
152162
153163
154164def _extract_child_components (component_source : str , analyzer : TreeSitterAnalyzer , full_source : str ) -> list [str ]:
155165 """Find child component names rendered in JSX."""
156- import re
157-
158- # Match JSX tags that start with uppercase (React components)
159- jsx_component_re = re .compile (r"<([A-Z][a-zA-Z0-9.]*)" )
160166 children = set ()
161- for match in jsx_component_re .finditer (component_source ):
167+ for match in _JSX_COMPONENT_RE .finditer (component_source ):
162168 name = match .group (1 )
163169 # Skip React built-ins like React.Fragment
164170 if name not in ("React.Fragment" , "Fragment" , "Suspense" , "React.Suspense" ):
@@ -168,10 +174,7 @@ def _extract_child_components(component_source: str, analyzer: TreeSitterAnalyze
168174
169175def _extract_context_subscriptions (component_source : str ) -> list [str ]:
170176 """Find React context subscriptions via useContext calls."""
171- import re
172-
173- context_re = re .compile (r"\buseContext\s*\(\s*(\w+)" )
174- return [match .group (1 ) for match in context_re .finditer (component_source )]
177+ return [match .group (1 ) for match in _CONTEXT_RE .finditer (component_source )]
175178
176179
177180def _find_type_definition (type_name : str , source : str , analyzer : TreeSitterAnalyzer ) -> str | None :
0 commit comments