Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion codeflash/languages/javascript/frameworks/detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
import logging
from dataclasses import dataclass, field
from functools import lru_cache
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from pathlib import Path

logger = logging.getLogger(__name__)

Expand Down
25 changes: 23 additions & 2 deletions codeflash/languages/javascript/frameworks/react/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ def detect_optimization_opportunities(source: str, component_info: ReactComponen
start = component_info.start_line - 1
end = min(component_info.end_line, len(lines))
component_lines = lines[start:end]
component_source = "\n".join(component_lines)
# Avoid building a large joined string; detectors will inspect lines directly.
component_source = "" # kept for signature compatibility with helpers

# Check for inline objects in JSX props

# Check for inline objects in JSX props
_detect_inline_props(component_lines, start, opportunities)
Expand Down Expand Up @@ -94,6 +97,9 @@ def _detect_inline_props(lines: list[str], offset: int, opportunities: list[Opti
"""Detect inline object/array literals in JSX prop positions."""
for i, line in enumerate(lines):
line_num = offset + i + 1
# Quick check to avoid running regexes when no JSX prop assignment is present
if "={" not in line:
continue
if INLINE_OBJECT_IN_JSX_RE.search(line):
opportunities.append(
OptimizationOpportunity(
Expand All @@ -120,12 +126,23 @@ def _detect_missing_usecallback(
component_source: str, lines: list[str], offset: int, opportunities: list[OptimizationOpportunity]
) -> None:
"""Detect arrow functions or function expressions that could use useCallback."""
has_usecallback = bool(USECALLBACK_RE.search(component_source))
# Determine whether the component uses useCallback anywhere by scanning lines (avoid joining)
has_usecallback = False
for line in lines:
if "useCallback" in line:
# cheap substring check before regex to avoid unnecessary work
if USECALLBACK_RE.search(line):
has_usecallback = True
break

for i, line in enumerate(lines):
line_num = offset + i + 1
stripped = line.strip()
# Look for arrow function or function expression definitions inside the component
# Quick substring check: FUNCTION_DEF_RE targets lines with var/const/let/function
if "const" not in stripped and "let" not in stripped and "var" not in stripped and "function" not in stripped:
continue
# Look for arrow function or function expression definitions inside the component
if FUNCTION_DEF_RE.search(stripped) and "useCallback" not in stripped and "useMemo" not in stripped:
# Skip if the component already uses useCallback extensively
if not has_usecallback:
Expand All @@ -147,6 +164,10 @@ def _detect_missing_usememo(
for i, line in enumerate(lines):
line_num = offset + i + 1
stripped = line.strip()
# Quick exclusions to avoid running the expensive regex when impossible to match
# expensive ops are accessed via a dot call like arr.map(
if "." not in stripped:
continue
if EXPENSIVE_OPS_RE.search(stripped) and "useMemo" not in stripped:
opportunities.append(
OptimizationOpportunity(
Expand Down
3 changes: 2 additions & 1 deletion codeflash/languages/javascript/frameworks/react/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@

import logging
from dataclasses import dataclass, field
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from pathlib import Path

from tree_sitter import Node

from codeflash.languages.javascript.frameworks.react.analyzer import OptimizationOpportunity
Expand Down
9 changes: 3 additions & 6 deletions codeflash/languages/javascript/frameworks/react/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
import re
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from pathlib import Path

from tree_sitter import Node

from codeflash.languages.javascript.treesitter import FunctionNode, TreeSitterAnalyzer
Expand Down Expand Up @@ -191,11 +192,7 @@ def _node_contains_jsx(node: Node) -> bool:
if _node_contains_jsx(child):
return True

for child in node.children:
if _node_contains_jsx(child):
return True

return False
return any(_node_contains_jsx(child) for child in node.children)


def _extract_hooks_used(function_source: str) -> list[str]:
Expand Down
16 changes: 8 additions & 8 deletions codeflash/languages/javascript/frameworks/react/profiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@

import logging
import re
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from pathlib import Path

from tree_sitter import Node

from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer
Expand Down Expand Up @@ -76,9 +77,7 @@ def instrument_component_with_profiler(source: str, component_name: str, analyze
result = _insert_after_imports(result, counter_code, analyzer)

# Ensure React is imported
result = _ensure_react_import(result)

return result
return _ensure_react_import(result)


def instrument_all_components_for_tracing(source: str, file_path: Path, analyzer: TreeSitterAnalyzer) -> str:
Expand Down Expand Up @@ -161,11 +160,12 @@ def walk(node: Node) -> None:

def _contains_jsx(node: Node) -> bool:
"""Check if a tree-sitter node contains JSX elements."""
if node.type in ("jsx_element", "jsx_self_closing_element", "jsx_fragment"):
return True
for child in node.children:
if _contains_jsx(child):
stack = [node]
while stack:
current = stack.pop()
if current.type in ("jsx_element", "jsx_self_closing_element", "jsx_fragment"):
return True
stack.extend(current.children)
return False


Expand Down
4 changes: 2 additions & 2 deletions codeflash/languages/javascript/treesitter_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1580,9 +1580,9 @@ def get_analyzer_for_file(file_path: Path) -> TreeSitterAnalyzer:
"""
suffix = file_path.suffix.lower()

if suffix in (".ts",):
if suffix == ".ts":
return TreeSitterAnalyzer(TreeSitterLanguage.TYPESCRIPT)
if suffix in (".tsx",):
if suffix == ".tsx":
return TreeSitterAnalyzer(TreeSitterLanguage.TSX)
# Default to JavaScript for .js, .jsx, .mjs, .cjs
return TreeSitterAnalyzer(TreeSitterLanguage.JAVASCRIPT)