Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
d4aeb64
[WIP] react framework initial commit
Saga4 Feb 20, 2026
3601a18
style: auto-fix linting issues and resolve mypy type errors
github-actions[bot] Feb 20, 2026
07ab618
fix: add missing type parameter to dict return type in testgen
github-actions[bot] Feb 20, 2026
257ed8c
add benchmarking
Saga4 Feb 20, 2026
63fff65
fix regex
Saga4 Feb 20, 2026
64bf671
style: auto-fix linting issues and resolve ruff format
claude[bot] Feb 20, 2026
f797218
Optimize extract_react_context
codeflash-ai[bot] Feb 20, 2026
a579748
style: auto-fix linting issues
github-actions[bot] Feb 20, 2026
5c3a8dc
Merge pull request #1571 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 20, 2026
6cc200f
Optimize _node_contains_jsx
codeflash-ai[bot] Feb 20, 2026
a947e43
style: auto-fix linting issues
github-actions[bot] Feb 20, 2026
5ae497c
Optimize instrument_component_with_profiler
codeflash-ai[bot] Feb 20, 2026
57673f2
style: auto-fix linting issues and resolve mypy type errors
github-actions[bot] Feb 20, 2026
89afe44
Optimize generate_render_counter_code
codeflash-ai[bot] Feb 20, 2026
ad37953
style: auto-fix linting issues and resolve mypy type errors
github-actions[bot] Feb 20, 2026
7cc2ced
Optimize _wrap_return_with_profiler
codeflash-ai[bot] Feb 20, 2026
80ff1de
style: auto-fix linting issues
github-actions[bot] Feb 20, 2026
3b5a5c7
Merge pull request #1585 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 20, 2026
7bde4e6
Optimize _insert_after_imports
codeflash-ai[bot] Feb 20, 2026
f9e4ea2
style: auto-fix linting issues
github-actions[bot] Feb 20, 2026
2832845
Merge pull request #1588 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 20, 2026
8038006
Optimize JavaScriptSupport._format_js_line_profile_output
codeflash-ai[bot] Feb 20, 2026
39b67c6
style: auto-fix linting issues
github-actions[bot] Feb 20, 2026
818f1f4
Optimize TreeSitterAnalyzer.find_functions
codeflash-ai[bot] Feb 20, 2026
10fde7c
style: auto-fix linting issues
github-actions[bot] Feb 20, 2026
0f6d793
Optimize TreeSitterAnalyzer.find_imports
codeflash-ai[bot] Feb 20, 2026
2733e96
style: auto-fix linting issues
github-actions[bot] Feb 20, 2026
07b5405
add architecture doc file
Saga4 Feb 20, 2026
a909762
Optimize TreeSitterAnalyzer._extract_import_info
codeflash-ai[bot] Feb 20, 2026
6fdf22e
style: auto-fix linting issues
github-actions[bot] Feb 20, 2026
f483d69
fix: resolve mypy type errors in JavaScript support modules
github-actions[bot] Feb 20, 2026
2c2c71b
Merge pull request #1599 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 20, 2026
51f874c
Optimize JavaScriptSupport._extract_types_from_definition
codeflash-ai[bot] Feb 20, 2026
11203ae
style: auto-fix linting issues
github-actions[bot] Feb 20, 2026
2c33ed4
Merge pull request #1604 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 20, 2026
b6c258d
Merge pull request #1600 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 20, 2026
609a80c
Merge pull request #1582 from codeflash-ai/codeflash/optimize-pr1581-…
claude[bot] Feb 20, 2026
4585bc1
Merge pull request #1593 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 20, 2026
edd1c04
Merge pull request #1592 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 20, 2026
51b1379
Merge pull request #1581 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 20, 2026
feca98d
Merge pull request #1578 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 20, 2026
73584bc
Optimize JavaScriptSupport._extract_types_from_definition
codeflash-ai[bot] Feb 20, 2026
85b6680
style: remove duplicate parser property in TreeSitterAnalyzer
github-actions[bot] Feb 20, 2026
6d93dd8
Optimize JavaScriptSupport._build_runtime_map
codeflash-ai[bot] Feb 20, 2026
b0afcce
style: auto-fix linting issues
github-actions[bot] Feb 20, 2026
af52024
Optimize TreeSitterAnalyzer.is_function_exported
codeflash-ai[bot] Feb 20, 2026
61d6547
Merge pull request #1612 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 20, 2026
cb711d5
Optimize TreeSitterAnalyzer.has_return_statement
codeflash-ai[bot] Feb 20, 2026
4023b73
style: auto-fix linting issues
github-actions[bot] Feb 20, 2026
9fd1565
Merge pull request #1615 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 20, 2026
b470219
Merge pull request #1614 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 21, 2026
01b146f
Merge pull request #1608 from codeflash-ai/codeflash/optimize-pr1561-…
claude[bot] Feb 21, 2026
3bb21e2
add tsx, jsx validation support
Saga4 Feb 23, 2026
8236394
tsx parser and import merge changes
Saga4 Feb 23, 2026
fbff5d6
feat: add React test instrumentation, captureRender runtime, and impo…
mohammedahmed18 Feb 24, 2026
9066cc0
experiment and test lock file
mohammedahmed18 Feb 24, 2026
2c95f05
fix test runner on jest 30 in monorepo cwd directory from root path
Saga4 Feb 24, 2026
98eb58e
style: auto-fix linting issues
github-actions[bot] Feb 24, 2026
f95dc37
benchmarking for react components with samples
mohammedahmed18 Feb 25, 2026
e0de8d4
fix: detect foreign config when optimizing external JS/TS projects
Saga4 Feb 26, 2026
d96c2d8
fix: enable JS package test directory detection for monorepos
Saga4 Feb 26, 2026
fd891c9
fix: handle multi-project Jest configs for generated tests
Saga4 Feb 26, 2026
250d4b1
fix: add TS/TSX test patterns and co-located test discovery
Saga4 Feb 26, 2026
a46b79b
fix: pass git_repo for branch detection and handle staging response
Saga4 Feb 26, 2026
6ee3458
feat: language-aware PR descriptions for JS/TS
Saga4 Feb 26, 2026
cbf2f5b
Optimize RenderCallTransformer.transform
codeflash-ai[bot] Feb 27, 2026
20c6a53
Merge pull request #1683 from codeflash-ai/codeflash/optimize-pr1561-…
misrasaurabh1 Feb 27, 2026
8af07af
style: fix linting and type annotation issues
github-actions[bot] Feb 27, 2026
5c1aed3
minor changes
mohammedahmed18 Mar 4, 2026
165b673
Merge branch 'add/support_react' of github.com:codeflash-ai/codeflash…
mohammedahmed18 Mar 4, 2026
4d92728
render count vs mount count instrumentation
Saga4 Mar 10, 2026
1c3d8aa
render profile add to all nodes of pipeline
Saga4 Mar 10, 2026
9df446b
update to render duration
Saga4 Mar 16, 2026
4bc89f2
react changes for interactive pattern
Saga4 Mar 17, 2026
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
2 changes: 1 addition & 1 deletion codeflash-benchmark/codeflash_benchmark/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# These version placeholders will be replaced by uv-dynamic-versioning during build.
__version__ = "0.3.0"
__version__ = "0.20.1.post127.dev0+c276ff32"
8 changes: 8 additions & 0 deletions codeflash/api/aiservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ def optimize_code(
is_async: bool = False,
n_candidates: int = 5,
is_numerical_code: bool | None = None,
is_react_component: bool = False,
react_context: str | None = None,
) -> list[OptimizedCandidate]:
"""Optimize the given code for performance by making a request to the Django endpoint.

Expand Down Expand Up @@ -188,6 +190,12 @@ def optimize_code(
if module_system:
payload["module_system"] = module_system

# React-specific fields
if is_react_component:
payload["is_react_component"] = True
if react_context:
payload["react_context"] = react_context

# DEBUG: Print payload language field
logger.debug(
f"Sending optimize request with language='{payload['language']}' (type: {type(payload['language'])})"
Expand Down
17 changes: 17 additions & 0 deletions codeflash/api/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ class OptimizeRequest:
repo_name: str | None = None
current_username: str | None = None

# === React-specific ===
is_react_component: bool = False
react_context: str = ""

def to_payload(self) -> dict[str, Any]:
"""Convert to API payload dict, maintaining backward compatibility."""
payload = {
Expand Down Expand Up @@ -150,6 +154,12 @@ def to_payload(self) -> dict[str, Any]:
if self.language_info.module_system != ModuleSystem.UNKNOWN:
payload["module_system"] = self.language_info.module_system.value

# React-specific fields
if self.is_react_component:
payload["is_react_component"] = True
if self.react_context:
payload["react_context"] = self.react_context

return payload


Expand Down Expand Up @@ -187,6 +197,9 @@ class TestGenRequest:
# === Metadata ===
codeflash_version: str = ""

# === React-specific ===
is_react_component: bool = False

def to_payload(self) -> dict[str, Any]:
"""Convert to API payload dict, maintaining backward compatibility."""
payload = {
Expand Down Expand Up @@ -218,6 +231,10 @@ def to_payload(self) -> dict[str, Any]:
if self.language_info.module_system != ModuleSystem.UNKNOWN:
payload["module_system"] = self.language_info.module_system.value

# React-specific fields
if self.is_react_component:
payload["is_react_component"] = True

return payload


Expand Down
1 change: 1 addition & 0 deletions codeflash/languages/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class CodeContext:
read_only_context: str = ""
imports: list[str] = field(default_factory=list)
language: Language = Language.PYTHON
react_context: str | None = None


@dataclass
Expand Down
1 change: 1 addition & 0 deletions codeflash/languages/javascript/frameworks/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Framework detection and support for JavaScript/TypeScript projects."""
97 changes: 97 additions & 0 deletions codeflash/languages/javascript/frameworks/detector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""Framework detection for JavaScript/TypeScript projects.

Detects React (and potentially other frameworks) by inspecting package.json
dependencies. Results are cached per project root.
"""

from __future__ import annotations

import json
import logging
from dataclasses import dataclass, field
from functools import lru_cache
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from pathlib import Path

logger = logging.getLogger(__name__)


@dataclass(frozen=True)
class FrameworkInfo:
"""Information about the frontend framework used in a project."""

name: str # "react", "vue", "angular", "none"
version: str | None = None # e.g., "18.2.0"
react_version_major: int | None = None # e.g., 18
has_testing_library: bool = False # @testing-library/react installed
has_react_compiler: bool = False # React 19+ compiler detected
dev_dependencies: frozenset[str] = field(default_factory=frozenset)


_EMPTY_FRAMEWORK = FrameworkInfo(name="none")


@lru_cache(maxsize=32)
def detect_framework(project_root: Path) -> FrameworkInfo:
"""Detect the frontend framework from package.json.

Reads dependencies and devDependencies to identify React and its ecosystem.
Results are cached per project root path.
"""
package_json_path = project_root / "package.json"
if not package_json_path.exists():
return _EMPTY_FRAMEWORK

try:
package_data = json.loads(package_json_path.read_text(encoding="utf-8"))
except (json.JSONDecodeError, OSError) as e:
logger.debug("Failed to read package.json at %s: %s", package_json_path, e)
return _EMPTY_FRAMEWORK

deps = package_data.get("dependencies", {})
dev_deps = package_data.get("devDependencies", {})
all_deps = {**deps, **dev_deps}

# Detect React
react_version_str = deps.get("react") or dev_deps.get("react")
if not react_version_str:
return _EMPTY_FRAMEWORK

version = _parse_version_string(react_version_str)
major = _parse_major_version(version)

has_testing_library = "@testing-library/react" in all_deps
has_react_compiler = (
"babel-plugin-react-compiler" in all_deps
or "react-compiler-runtime" in all_deps
or (major is not None and major >= 19)
)

return FrameworkInfo(
name="react",
version=version,
react_version_major=major,
has_testing_library=has_testing_library,
has_react_compiler=has_react_compiler,
dev_dependencies=frozenset(all_deps.keys()),
)


def _parse_version_string(version_spec: str) -> str | None:
"""Extract a clean version from a semver range like ^18.2.0 or ~17.0.0."""
stripped = version_spec.lstrip("^~>=<! ")
if stripped and stripped[0].isdigit():
return stripped
return None


def _parse_major_version(version: str | None) -> int | None:
"""Extract major version number from a version string."""
if not version:
return None
try:
return int(version.split(".")[0])
except (ValueError, IndexError):
return None
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""React framework support for component discovery, profiling, and optimization."""
159 changes: 159 additions & 0 deletions codeflash/languages/javascript/frameworks/react/analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
"""Static analysis for React optimization opportunities.

Detects common performance anti-patterns in React components:
- Inline object/array creation in JSX props
- Functions defined inside render body (missing useCallback)
- Expensive computations without useMemo
- Components receiving referentially unstable props
"""

from __future__ import annotations

import re
from dataclasses import dataclass
from enum import Enum
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from codeflash.languages.javascript.frameworks.react.discovery import ReactComponentInfo


class OpportunitySeverity(str, Enum):
HIGH = "high"
MEDIUM = "medium"
LOW = "low"


class OpportunityType(str, Enum):
INLINE_OBJECT_PROP = "inline_object_prop"
INLINE_ARRAY_PROP = "inline_array_prop"
MISSING_USECALLBACK = "missing_usecallback"
MISSING_USEMEMO = "missing_usememo"
MISSING_REACT_MEMO = "missing_react_memo"
UNSTABLE_REFERENCE = "unstable_reference"


@dataclass(frozen=True)
class OptimizationOpportunity:
"""A detected optimization opportunity in a React component."""

type: OpportunityType
line: int
description: str
severity: OpportunitySeverity


# Patterns for expensive operations inside render body
EXPENSIVE_OPS_RE = re.compile(r"\.(filter|map|sort|reduce|flatMap|find|findIndex|every|some)\s*\(")
INLINE_OBJECT_IN_JSX_RE = re.compile(r"=\{\s*\{") # ={{ ... }} in JSX
INLINE_ARRAY_IN_JSX_RE = re.compile(r"=\{\s*\[") # ={[ ... ]} in JSX
FUNCTION_DEF_RE = re.compile(
r"(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?(?:\([^)]*\)|[a-zA-Z_]\w*)\s*=>"
r"|function\s+\w+\s*\("
)
USECALLBACK_RE = re.compile(r"\buseCallback\s*\(")
USEMEMO_RE = re.compile(r"\buseMemo\s*\(")


def detect_optimization_opportunities(source: str, component_info: ReactComponentInfo) -> list[OptimizationOpportunity]:
"""Detect optimization opportunities in a React component."""
opportunities: list[OptimizationOpportunity] = []
lines = source.splitlines()

# Only analyze the component's own lines
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)

# Check for inline objects in JSX props
_detect_inline_props(component_lines, start, opportunities)

# Check for functions defined in render body without useCallback
_detect_missing_usecallback(component_source, component_lines, start, opportunities)

# Check for expensive computations without useMemo
_detect_missing_usememo(component_source, component_lines, start, opportunities)

# Check if component should be wrapped in React.memo
if not component_info.is_memoized:
opportunities.append(
OptimizationOpportunity(
type=OpportunityType.MISSING_REACT_MEMO,
line=component_info.start_line,
description=f"Component '{component_info.function_name}' is not wrapped in React.memo(). "
"If it receives stable props, wrapping can prevent unnecessary re-renders.",
severity=OpportunitySeverity.MEDIUM,
)
)

return opportunities


def _detect_inline_props(lines: list[str], offset: int, opportunities: list[OptimizationOpportunity]) -> None:
"""Detect inline object/array literals in JSX prop positions."""
for i, line in enumerate(lines):
line_num = offset + i + 1
if INLINE_OBJECT_IN_JSX_RE.search(line):
opportunities.append(
OptimizationOpportunity(
type=OpportunityType.INLINE_OBJECT_PROP,
line=line_num,
description="Inline object literal in JSX prop creates a new reference on every render. "
"Extract to useMemo or a module-level constant.",
severity=OpportunitySeverity.HIGH,
)
)
if INLINE_ARRAY_IN_JSX_RE.search(line):
opportunities.append(
OptimizationOpportunity(
type=OpportunityType.INLINE_ARRAY_PROP,
line=line_num,
description="Inline array literal in JSX prop creates a new reference on every render. "
"Extract to useMemo or a module-level constant.",
severity=OpportunitySeverity.HIGH,
)
)


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

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
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:
opportunities.append(
OptimizationOpportunity(
type=OpportunityType.MISSING_USECALLBACK,
line=line_num,
description="Function defined inside render body creates a new reference on every render. "
"Wrap with useCallback() if passed as a prop to child components.",
severity=OpportunitySeverity.MEDIUM,
)
)


def _detect_missing_usememo(
component_source: str, lines: list[str], offset: int, opportunities: list[OptimizationOpportunity]
) -> None:
"""Detect expensive computations that could benefit from useMemo."""
for i, line in enumerate(lines):
line_num = offset + i + 1
stripped = line.strip()
if EXPENSIVE_OPS_RE.search(stripped) and "useMemo" not in stripped:
opportunities.append(
OptimizationOpportunity(
type=OpportunityType.MISSING_USEMEMO,
line=line_num,
description="Expensive array operation in render body runs on every render. "
"Wrap with useMemo() and specify dependencies.",
severity=OpportunitySeverity.HIGH,
)
)
Loading
Loading