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
15 changes: 11 additions & 4 deletions codeflash/languages/javascript/mocha_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,21 @@ def _ensure_runtime_files(project_root: Path) -> None:
Installs codeflash package if not already present.
The package provides all runtime files needed for test instrumentation.

In monorepos, node_modules may be hoisted to the repo root, so we walk
upward from project_root to find an existing codeflash installation.

Args:
project_root: The project root directory.

"""
node_modules_pkg = project_root / "node_modules" / "codeflash"
if node_modules_pkg.exists():
logger.debug("codeflash already installed")
return
# Walk upward to find codeflash in any ancestor node_modules (monorepo hoisting)
current = project_root
while current != current.parent:
node_modules_pkg = current / "node_modules" / "codeflash"
if node_modules_pkg.exists():
logger.debug(f"codeflash already installed at {node_modules_pkg}")
return
current = current.parent

install_cmd = get_package_install_command(project_root, "codeflash", dev=True)
try:
Expand Down
7 changes: 7 additions & 0 deletions codeflash/languages/javascript/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -1951,6 +1951,13 @@ def setup_test_config(self, test_cfg: TestConfig, file_path: Path, current_workt
)
if original_node_modules.exists() and not worktree_node_modules.exists():
worktree_node_modules.symlink_to(original_node_modules)
# In monorepos, node_modules lives at the repo root, not the package level.
# Symlink the root-level node_modules into the worktree so Vitest/npx can resolve deps.
if not worktree_node_modules.exists():
worktree_root_node_modules = current_worktree / "node_modules"
original_root_node_modules = original_js_root / "node_modules"
if original_root_node_modules.exists() and not worktree_root_node_modules.exists():
worktree_root_node_modules.symlink_to(original_root_node_modules)
verify_js_requirements(test_cfg)

def adjust_test_config_for_discovery(self, test_cfg: TestConfig) -> None:
Expand Down
15 changes: 11 additions & 4 deletions codeflash/languages/javascript/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -699,14 +699,21 @@ def _ensure_runtime_files(project_root: Path) -> None:
The package provides all runtime files needed for test instrumentation.
Uses the project's detected package manager (npm, pnpm, yarn, or bun).

In monorepos, node_modules may be hoisted to the repo root, so we walk
upward from project_root to find an existing codeflash installation.

Args:
project_root: The project root directory.

"""
node_modules_pkg = project_root / "node_modules" / "codeflash"
if node_modules_pkg.exists():
logger.debug("codeflash already installed")
return
# Walk upward to find codeflash in any ancestor node_modules (monorepo hoisting)
current = project_root
while current != current.parent:
node_modules_pkg = current / "node_modules" / "codeflash"
if node_modules_pkg.exists():
logger.debug(f"codeflash already installed at {node_modules_pkg}")
return
current = current.parent

install_cmd = get_package_install_command(project_root, "codeflash", dev=True)
try:
Expand Down
38 changes: 34 additions & 4 deletions codeflash/languages/javascript/vitest_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from __future__ import annotations

import os
import subprocess
import time
from pathlib import Path
Expand Down Expand Up @@ -95,14 +96,21 @@ def _ensure_runtime_files(project_root: Path) -> None:
The package provides all runtime files needed for test instrumentation.
Uses the project's detected package manager (npm, pnpm, yarn, or bun).

In monorepos, node_modules may be hoisted to the repo root, so we walk
upward from project_root to find an existing codeflash installation.

Args:
project_root: The project root directory.

"""
node_modules_pkg = project_root / "node_modules" / "codeflash"
if node_modules_pkg.exists():
logger.debug("codeflash already installed")
return
# Walk upward to find codeflash in any ancestor node_modules (monorepo hoisting)
current = project_root
while current != current.parent:
node_modules_pkg = current / "node_modules" / "codeflash"
if node_modules_pkg.exists():
logger.debug(f"codeflash already installed at {node_modules_pkg}")
return
current = current.parent

install_cmd = get_package_install_command(project_root, "codeflash", dev=True)
try:
Expand Down Expand Up @@ -295,6 +303,18 @@ def _build_vitest_behavioral_command(
if codeflash_vitest_config:
cmd.append(f"--config={codeflash_vitest_config}")

# In monorepos, test files may be outside the project root (e.g., tests/ at repo root
# while project_root is packages/features/). Vitest only discovers files under its root
# directory, so we use --dir to widen the scan to the common ancestor.
if project_root and test_files:
resolved_root = project_root.resolve()
test_dirs = {f.resolve().parent for f in test_files}
if any(not d.is_relative_to(resolved_root) for d in test_dirs):
all_paths = [str(resolved_root)] + [str(d) for d in test_dirs]
common_ancestor = Path(os.path.commonpath(all_paths))
cmd.append(f"--dir={common_ancestor}")
logger.debug(f"Test files outside project root, using --dir={common_ancestor}")

if output_file:
# Use dot notation for junit reporter output file when multiple reporters are used
# Format: --outputFile.junit=/path/to/file.xml
Expand Down Expand Up @@ -343,6 +363,16 @@ def _build_vitest_benchmarking_command(
if codeflash_vitest_config:
cmd.append(f"--config={codeflash_vitest_config}")

# In monorepos, test files may be outside the project root. Widen the scan directory.
if project_root and test_files:
resolved_root = project_root.resolve()
test_dirs = {f.resolve().parent for f in test_files}
if any(not d.is_relative_to(resolved_root) for d in test_dirs):
all_paths = [str(resolved_root)] + [str(d) for d in test_dirs]
common_ancestor = Path(os.path.commonpath(all_paths))
cmd.append(f"--dir={common_ancestor}")
logger.debug(f"Test files outside project root, using --dir={common_ancestor}")

if output_file:
# Use dot notation for junit reporter output file when multiple reporters are used
cmd.append(f"--outputFile.junit={output_file}")
Expand Down
Loading