Skip to content

Commit 5020757

Browse files
authored
Merge pull request #1238 from codeflash-ai/pre-check_on_environment_config_dependencies
Pre check on env config dependencies in JS before calling aiservices API
2 parents 1b2463e + ce6c742 commit 5020757

4 files changed

Lines changed: 355 additions & 53 deletions

File tree

codeflash/languages/javascript/support.py

Lines changed: 75 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
HelperFunction,
2020
Language,
2121
ParentInfo,
22-
ReferenceInfo,
2322
TestInfo,
2423
TestResult,
2524
)
@@ -29,6 +28,7 @@
2928
if TYPE_CHECKING:
3029
from collections.abc import Sequence
3130

31+
from codeflash.languages.base import ReferenceInfo
3232
from codeflash.languages.treesitter_utils import TypeDefinition
3333

3434
logger = logging.getLogger(__name__)
@@ -966,11 +966,7 @@ def find_helper_functions(self, function: FunctionInfo, project_root: Path) -> l
966966
return []
967967

968968
def find_references(
969-
self,
970-
function: FunctionInfo,
971-
project_root: Path,
972-
tests_root: Path | None = None,
973-
max_files: int = 500,
969+
self, function: FunctionInfo, project_root: Path, tests_root: Path | None = None, max_files: int = 500
974970
) -> list[ReferenceInfo]:
975971
"""Find all references (call sites) to a function across the codebase.
976972
@@ -1835,51 +1831,98 @@ def get_module_path(self, source_file: Path, project_root: Path, tests_root: Pat
18351831
rel_path = source_file.relative_to(project_root)
18361832
return "../" + rel_path.with_suffix("").as_posix()
18371833

1834+
def verify_requirements(self, project_root: Path, test_framework: str = "jest") -> tuple[bool, list[str]]:
1835+
"""Verify that all JavaScript requirements are met.
1836+
1837+
Checks for:
1838+
1. Node.js installation
1839+
2. npm availability
1840+
3. Test framework (jest/vitest) installation
1841+
4. node_modules existence
1842+
1843+
Args:
1844+
project_root: The project root directory.
1845+
test_framework: The test framework to check for ("jest" or "vitest").
1846+
1847+
Returns:
1848+
Tuple of (success, list of error messages).
1849+
1850+
"""
1851+
errors: list[str] = []
1852+
1853+
# Check Node.js
1854+
try:
1855+
result = subprocess.run(["node", "--version"], check=False, capture_output=True, text=True, timeout=10)
1856+
if result.returncode != 0:
1857+
errors.append("Node.js is not installed. Please install Node.js 18+ from https://nodejs.org/")
1858+
except FileNotFoundError:
1859+
errors.append("Node.js is not installed. Please install Node.js 18+ from https://nodejs.org/")
1860+
except Exception as e:
1861+
errors.append(f"Failed to check Node.js: {e}")
1862+
1863+
# Check npm
1864+
try:
1865+
result = subprocess.run(["npm", "--version"], check=False, capture_output=True, text=True, timeout=10)
1866+
if result.returncode != 0:
1867+
errors.append("npm is not available. Please ensure npm is installed with Node.js.")
1868+
except FileNotFoundError:
1869+
errors.append("npm is not available. Please ensure npm is installed with Node.js.")
1870+
except Exception as e:
1871+
errors.append(f"Failed to check npm: {e}")
1872+
1873+
# Check node_modules exists
1874+
node_modules = project_root / "node_modules"
1875+
if not node_modules.exists():
1876+
errors.append(
1877+
f"node_modules not found in {project_root}. Please run 'npm install' to install dependencies."
1878+
)
1879+
else:
1880+
# Check test framework is installed
1881+
framework_path = node_modules / test_framework
1882+
if not framework_path.exists():
1883+
errors.append(
1884+
f"{test_framework} is not installed. "
1885+
f"Please run 'npm install --save-dev {test_framework}' to install it."
1886+
)
1887+
1888+
return len(errors) == 0, errors
1889+
18381890
def ensure_runtime_environment(self, project_root: Path) -> bool:
18391891
"""Ensure codeflash npm package is installed.
18401892
18411893
Attempts to install the npm package for test instrumentation.
1842-
Falls back to copying files if npm install fails.
18431894
18441895
Args:
18451896
project_root: The project root directory.
18461897
18471898
Returns:
1848-
True if npm package is installed, False if falling back to file copy.
1899+
True if npm package is installed, False otherwise.
18491900
18501901
"""
1851-
import subprocess
1852-
18531902
from codeflash.cli_cmds.console import logger
18541903

1855-
# Check if package is already installed
18561904
node_modules_pkg = project_root / "node_modules" / "codeflash"
18571905
if node_modules_pkg.exists():
18581906
logger.debug("codeflash already installed")
18591907
return True
18601908

1861-
# Try to install from local package first (for development)
1862-
local_package_path = Path(__file__).parent.parent.parent.parent / "packages" / "cli"
1863-
if local_package_path.exists():
1864-
try:
1865-
result = subprocess.run(
1866-
["npm", "install", "--save-dev", str(local_package_path)],
1867-
check=False,
1868-
cwd=project_root,
1869-
capture_output=True,
1870-
text=True,
1871-
timeout=120,
1872-
)
1873-
if result.returncode == 0:
1874-
logger.debug("Installed codeflash from local package")
1875-
return True
1876-
logger.warning(f"Failed to install local package: {result.stderr}")
1877-
except Exception as e:
1878-
logger.warning(f"Error installing local package: {e}")
1879-
1880-
# Could try npm registry here in the future:
1881-
# subprocess.run(["npm", "install", "--save-dev", "codeflash"], ...)
1909+
try:
1910+
result = subprocess.run(
1911+
["npm", "install", "--save-dev", "codeflash"],
1912+
check=False,
1913+
cwd=project_root,
1914+
capture_output=True,
1915+
text=True,
1916+
timeout=120,
1917+
)
1918+
if result.returncode == 0:
1919+
logger.debug("Installed codeflash from npm registry")
1920+
return True
1921+
logger.warning(f"Failed to install codeflash: {result.stderr}")
1922+
except Exception as e:
1923+
logger.warning(f"Error installing codeflash: {e}")
18821924

1925+
logger.error("Could not install codeflash. Please run: npm install --save-dev codeflash")
18831926
return False
18841927

18851928
def instrument_existing_test(

codeflash/languages/javascript/test_runner.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -150,32 +150,11 @@ def _ensure_runtime_files(project_root: Path) -> None:
150150
project_root: The project root directory.
151151
152152
"""
153-
# Check if package is already installed
154153
node_modules_pkg = project_root / "node_modules" / "codeflash"
155154
if node_modules_pkg.exists():
156155
logger.debug("codeflash already installed")
157156
return
158157

159-
# Try to install from local package first (for development)
160-
local_package_path = Path(__file__).parent.parent.parent.parent / "packages" / "codeflash"
161-
if local_package_path.exists():
162-
try:
163-
result = subprocess.run(
164-
["npm", "install", "--save-dev", str(local_package_path)],
165-
check=False,
166-
cwd=project_root,
167-
capture_output=True,
168-
text=True,
169-
timeout=120,
170-
)
171-
if result.returncode == 0:
172-
logger.debug("Installed codeflash from local package")
173-
return
174-
logger.warning(f"Failed to install local package: {result.stderr}")
175-
except Exception as e:
176-
logger.warning(f"Error installing local package: {e}")
177-
178-
# Try to install from npm registry
179158
try:
180159
result = subprocess.run(
181160
["npm", "install", "--save-dev", "codeflash"],

codeflash/optimization/optimizer.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,33 @@ def _find_js_project_root(file_path: Path) -> Path | None:
9090
current = current.parent
9191
return None
9292

93+
def _verify_js_requirements(self) -> None:
94+
"""Verify JavaScript/TypeScript requirements before optimization.
95+
96+
Checks that Node.js, npm, and the test framework are available.
97+
Logs warnings if requirements are not met but does not abort.
98+
99+
"""
100+
from codeflash.languages import get_language_support
101+
from codeflash.languages.base import Language
102+
from codeflash.languages.test_framework import get_js_test_framework_or_default
103+
104+
js_project_root = self.test_cfg.js_project_root
105+
if not js_project_root:
106+
return
107+
108+
try:
109+
js_support = get_language_support(Language.JAVASCRIPT)
110+
test_framework = get_js_test_framework_or_default()
111+
success, errors = js_support.verify_requirements(js_project_root, test_framework)
112+
113+
if not success:
114+
logger.warning("JavaScript requirements check found issues:")
115+
for error in errors:
116+
logger.warning(f" - {error}")
117+
except Exception as e:
118+
logger.debug(f"Failed to verify JS requirements: {e}")
119+
93120
def run_benchmarks(
94121
self, file_to_funcs_to_optimize: dict[Path, list[FunctionToOptimize]], num_optimizable_functions: int
95122
) -> tuple[dict[str, dict[BenchmarkKey, float]], dict[BenchmarkKey, float]]:
@@ -466,6 +493,8 @@ def run(self) -> None:
466493
# For JavaScript, also set js_project_root for test execution
467494
if is_javascript():
468495
self.test_cfg.js_project_root = self._find_js_project_root(file_path)
496+
# Verify JS requirements before proceeding
497+
self._verify_js_requirements()
469498
break
470499

471500
if self.args.all:

0 commit comments

Comments
 (0)