Skip to content

Commit 5c3a8dc

Browse files
authored
Merge pull request #1571 from codeflash-ai/codeflash/optimize-pr1561-2026-02-20T03.56.09
⚡️ Speed up function `extract_react_context` by 12% in PR #1561 (`add/support_react`)
2 parents 64bf671 + a579748 commit 5c3a8dc

1 file changed

Lines changed: 29 additions & 26 deletions

File tree

  • codeflash/languages/javascript/frameworks/react

codeflash/languages/javascript/frameworks/react/context.py

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
from __future__ import annotations
88

99
import logging
10+
import re
1011
from dataclasses import dataclass, field
1112
from typing import TYPE_CHECKING
1213

14+
from codeflash.languages.javascript.frameworks.react.analyzer import detect_optimization_opportunities
15+
1316
if TYPE_CHECKING:
1417
from pathlib import Path
1518

@@ -19,6 +22,12 @@
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+
2231
logger = 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

110117
def _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

154164
def _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

169175
def _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

177180
def _find_type_definition(type_name: str, source: str, analyzer: TreeSitterAnalyzer) -> str | None:

0 commit comments

Comments
 (0)