From 113eb3f7913d84e5e9c59e7e8a1d6e302caed492 Mon Sep 17 00:00:00 2001 From: mohammed ahmed Date: Sat, 4 Apr 2026 13:05:35 +0000 Subject: [PATCH 1/2] Fix TypeScript module system detection in CommonJS packages Issue #10: TypeScript files (.ts, .tsx) were incorrectly detected as ESM regardless of package.json "type" field. This caused "Cannot use import statement outside a module" errors when the AI service generated ESM imports for TypeScript files in CommonJS packages. Root Cause: - module_system.py lines 73-77 returned ES_MODULE for ALL .ts/.tsx files - Skipped checking package.json "type" field - Confused TypeScript SOURCE syntax (always ESM) with RUNTIME module system Fix: - For .ts/.tsx files, defer to package.json "type" field - If no "type" field, default to CommonJS (Node.js default) - Explicit extensions (.mts, .cts) still override package.json Impact: - Systematic bug affecting all TypeScript files in CommonJS packages - 2 logs in current batch (backend-core/inMemoryQueue.ts) - High severity (blocks test generation) Testing: - 5 new regression tests covering all scenarios - 318 existing JavaScript tests pass - No linting/type errors Trace IDs: 3fccd364-bc04-4196-9f50-f45f29a632d3, 6d220d09-3292-41de-a52c-d0e961e615e9 Co-Authored-By: Claude Sonnet 4.5 --- .../languages/javascript/module_system.py | 42 +++-- .../test_javascript_module_system.py | 39 +++-- ...st_typescript_commonjs_module_detection.py | 160 ++++++++++++++++++ 3 files changed, 213 insertions(+), 28 deletions(-) create mode 100644 tests/test_languages/test_typescript_commonjs_module_detection.py diff --git a/codeflash/languages/javascript/module_system.py b/codeflash/languages/javascript/module_system.py index 1f7b57c8a..5522b1d29 100644 --- a/codeflash/languages/javascript/module_system.py +++ b/codeflash/languages/javascript/module_system.py @@ -46,8 +46,10 @@ def detect_module_system(project_root: Path, file_path: Path | None = None) -> s """Detect the module system used by a JavaScript/TypeScript project. Detection strategy: - 1. Check file extension for explicit module type (.mjs, .cjs, .ts, .tsx, .mts) - - TypeScript files always use ESM syntax regardless of package.json + 1. Check file extension for explicit module type (.mjs, .cjs, .mts, .cts) + - .mjs and .mts always use ES Modules + - .cjs and .cts always use CommonJS + - .ts and .tsx defer to package.json "type" field 2. Check package.json for explicit "type" field (only if explicitly set) 3. Analyze import/export statements in the file content 4. Default to CommonJS if uncertain @@ -61,33 +63,41 @@ def detect_module_system(project_root: Path, file_path: Path | None = None) -> s """ # Strategy 1: Check file extension first for explicit module type indicators - # TypeScript files always use ESM syntax (import/export) if file_path: suffix = file_path.suffix.lower() + # Explicit JavaScript module system extensions if suffix == ".mjs": logger.debug("Detected ES Module from .mjs extension") return ModuleSystem.ES_MODULE if suffix == ".cjs": logger.debug("Detected CommonJS from .cjs extension") return ModuleSystem.COMMONJS - if suffix in (".ts", ".tsx", ".mts"): - # TypeScript always uses ESM syntax (import/export) - # even if package.json doesn't have "type": "module" - logger.debug("Detected ES Module from TypeScript file extension") + + # Explicit TypeScript module system extensions + if suffix == ".mts": + logger.debug("Detected ES Module from .mts extension") return ModuleSystem.ES_MODULE + if suffix == ".cts": + logger.debug("Detected CommonJS from .cts extension") + return ModuleSystem.COMMONJS + + # For .ts/.tsx files, defer to package.json "type" field + # TypeScript source uses ESM syntax (import/export), but the module system + # at runtime depends on package.json and tsconfig compilation settings # Strategy 2: Check package.json for explicit type field package_json = project_root / "package.json" + pkg_type_from_json = None if package_json.exists(): try: with package_json.open("r") as f: pkg = json.load(f) - pkg_type = pkg.get("type") # Don't default - only use if explicitly set + pkg_type_from_json = pkg.get("type") # Don't default - only use if explicitly set - if pkg_type == "module": + if pkg_type_from_json == "module": logger.debug("Detected ES Module from package.json type field") return ModuleSystem.ES_MODULE - if pkg_type == "commonjs": + if pkg_type_from_json == "commonjs": logger.debug("Detected CommonJS from package.json type field") return ModuleSystem.COMMONJS # If type is not explicitly set, continue to file content analysis @@ -95,6 +105,18 @@ def detect_module_system(project_root: Path, file_path: Path | None = None) -> s except Exception as e: logger.warning("Failed to parse package.json: %s", e) + # For TypeScript files (.ts, .tsx), if package.json doesn't specify a type, + # default to CommonJS since that's the Node.js default. + # We skip file content analysis for TypeScript because TypeScript source + # always uses ESM syntax (import/export), but the actual module system + # depends on how TypeScript compiles and how Node.js loads the files. + if file_path and file_path.suffix.lower() in (".ts", ".tsx"): + if pkg_type_from_json is None: + logger.debug( + "TypeScript file without explicit package.json type field - defaulting to CommonJS" + ) + return ModuleSystem.COMMONJS + # Strategy 3: Analyze file content for import/export patterns if file_path and file_path.exists(): try: diff --git a/tests/test_languages/test_javascript_module_system.py b/tests/test_languages/test_javascript_module_system.py index f4a0b0c16..1eb56c3f2 100644 --- a/tests/test_languages/test_javascript_module_system.py +++ b/tests/test_languages/test_javascript_module_system.py @@ -57,36 +57,39 @@ def test_detect_commonjs_from_cjs_extension(self): assert result == ModuleSystem.COMMONJS def test_detect_esm_from_typescript_extension(self): - """Test detection of ES modules from TypeScript file extensions.""" + """Test detection of explicit TypeScript module extensions (.mts, .cts).""" with tempfile.TemporaryDirectory() as tmpdir: project_root = Path(tmpdir) - # Test .ts files - ts_file = project_root / "module.ts" - ts_file.write_text("export const foo = 'bar';") - assert detect_module_system(project_root, ts_file) == ModuleSystem.ES_MODULE - - # Test .tsx files - tsx_file = project_root / "component.tsx" - tsx_file.write_text("export const Component = () =>
;") - assert detect_module_system(project_root, tsx_file) == ModuleSystem.ES_MODULE - - # Test .mts files + # .mts files should always be ESM mts_file = project_root / "module.mts" mts_file.write_text("export const foo = 'bar';") assert detect_module_system(project_root, mts_file) == ModuleSystem.ES_MODULE - def test_typescript_ignores_package_json_commonjs(self): - """Test that TypeScript files are detected as ESM even with CommonJS package.json.""" + # .cts files should always be CommonJS + cts_file = project_root / "module.cts" + cts_file.write_text("export const foo = 'bar';") + assert detect_module_system(project_root, cts_file) == ModuleSystem.COMMONJS + + # .ts/.tsx files without package.json should default to CommonJS + ts_file = project_root / "module.ts" + ts_file.write_text("export const foo = 'bar';") + assert detect_module_system(project_root, ts_file) == ModuleSystem.COMMONJS + + def test_typescript_respects_package_json_type(self): + """Test that TypeScript files respect package.json type field.""" with tempfile.TemporaryDirectory() as tmpdir: project_root = Path(tmpdir) - # Create package.json with explicit commonjs type + ts_file = project_root / "module.ts" + ts_file.write_text("export const foo = 'bar';") + + # With explicit "type": "commonjs" package_json = project_root / "package.json" package_json.write_text(json.dumps({"type": "commonjs"})) + assert detect_module_system(project_root, ts_file) == ModuleSystem.COMMONJS - # TypeScript file should still be detected as ESM - ts_file = project_root / "module.ts" - ts_file.write_text("export const foo = 'bar';") + # With explicit "type": "module" + package_json.write_text(json.dumps({"type": "module"})) assert detect_module_system(project_root, ts_file) == ModuleSystem.ES_MODULE def test_detect_esm_from_import_syntax(self): diff --git a/tests/test_languages/test_typescript_commonjs_module_detection.py b/tests/test_languages/test_typescript_commonjs_module_detection.py new file mode 100644 index 000000000..2896838de --- /dev/null +++ b/tests/test_languages/test_typescript_commonjs_module_detection.py @@ -0,0 +1,160 @@ +""" +Test for Issue #10: TypeScript files in CommonJS packages should not be detected as ESM + +When a TypeScript file exists in a package without "type": "module" in package.json, +the module system should be detected as CommonJS, not ESM. +""" + +import json +import tempfile +from pathlib import Path + +import pytest + +from codeflash.languages.javascript.module_system import ModuleSystem, detect_module_system + + +def test_typescript_file_in_commonjs_package(): + """TypeScript file in package without 'type' field should be CommonJS""" + with tempfile.TemporaryDirectory() as tmpdir: + project_root = Path(tmpdir) + + # Create package.json without "type" field (defaults to CommonJS) + package_json = project_root / "package.json" + package_json.write_text(json.dumps({ + "name": "test-package", + "version": "1.0.0" + })) + + # Create TypeScript file with ESM source syntax + ts_file = project_root / "src" / "index.ts" + ts_file.parent.mkdir(parents=True, exist_ok=True) + ts_file.write_text(""" +import { foo } from './foo'; + +export function bar() { + return foo(); +} +""") + + # Should detect as CommonJS, not ESM + result = detect_module_system(project_root, ts_file) + + assert result == ModuleSystem.COMMONJS, ( + f"Expected CommonJS for TypeScript file in package without 'type' field, got {result}" + ) + + +def test_typescript_file_in_explicit_commonjs_package(): + """TypeScript file in package with 'type': 'commonjs' should be CommonJS""" + with tempfile.TemporaryDirectory() as tmpdir: + project_root = Path(tmpdir) + + # Create package.json with explicit "type": "commonjs" + package_json = project_root / "package.json" + package_json.write_text(json.dumps({ + "name": "test-package", + "version": "1.0.0", + "type": "commonjs" + })) + + # Create TypeScript file + ts_file = project_root / "src" / "index.ts" + ts_file.parent.mkdir(parents=True, exist_ok=True) + ts_file.write_text(""" +import { foo } from './foo'; +export function bar() { return foo(); } +""") + + # Should detect as CommonJS + result = detect_module_system(project_root, ts_file) + + assert result == ModuleSystem.COMMONJS, ( + f"Expected CommonJS for TypeScript file in explicit CommonJS package, got {result}" + ) + + +def test_typescript_file_in_esm_package(): + """TypeScript file in package with 'type': 'module' should be ESM""" + with tempfile.TemporaryDirectory() as tmpdir: + project_root = Path(tmpdir) + + # Create package.json with "type": "module" + package_json = project_root / "package.json" + package_json.write_text(json.dumps({ + "name": "test-package", + "version": "1.0.0", + "type": "module" + })) + + # Create TypeScript file + ts_file = project_root / "src" / "index.ts" + ts_file.parent.mkdir(parents=True, exist_ok=True) + ts_file.write_text(""" +import { foo } from './foo'; +export function bar() { return foo(); } +""") + + # Should detect as ESM + result = detect_module_system(project_root, ts_file) + + assert result == ModuleSystem.ES_MODULE, ( + f"Expected ESM for TypeScript file in ESM package, got {result}" + ) + + +def test_mts_file_always_esm(): + """.mts files should always be ESM regardless of package.json""" + with tempfile.TemporaryDirectory() as tmpdir: + project_root = Path(tmpdir) + + # Create package.json without "type" field + package_json = project_root / "package.json" + package_json.write_text(json.dumps({ + "name": "test-package", + "version": "1.0.0" + })) + + # Create .mts file (explicit ESM extension) + mts_file = project_root / "src" / "index.mts" + mts_file.parent.mkdir(parents=True, exist_ok=True) + mts_file.write_text(""" +import { foo } from './foo'; +export function bar() { return foo(); } +""") + + # Should detect as ESM (explicit extension) + result = detect_module_system(project_root, mts_file) + + assert result == ModuleSystem.ES_MODULE, ( + f"Expected ESM for .mts file, got {result}" + ) + + +def test_cts_file_always_commonjs(): + """.cts files should always be CommonJS regardless of package.json""" + with tempfile.TemporaryDirectory() as tmpdir: + project_root = Path(tmpdir) + + # Create package.json with "type": "module" + package_json = project_root / "package.json" + package_json.write_text(json.dumps({ + "name": "test-package", + "version": "1.0.0", + "type": "module" + })) + + # Create .cts file (explicit CommonJS extension) + cts_file = project_root / "src" / "index.cts" + cts_file.parent.mkdir(parents=True, exist_ok=True) + cts_file.write_text(""" +import { foo } from './foo'; +export function bar() { return foo(); } +""") + + # Should detect as CommonJS (explicit extension) + result = detect_module_system(project_root, cts_file) + + assert result == ModuleSystem.COMMONJS, ( + f"Expected CommonJS for .cts file, got {result}" + ) From 66b4efc53cf23e71976d15fa1047017bbfadc434 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2026 13:09:00 +0000 Subject: [PATCH 2/2] style: auto-fix ruff formatting in module_system.py Co-authored-by: mohammed ahmed --- codeflash/languages/javascript/module_system.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/codeflash/languages/javascript/module_system.py b/codeflash/languages/javascript/module_system.py index 5522b1d29..32d6c875d 100644 --- a/codeflash/languages/javascript/module_system.py +++ b/codeflash/languages/javascript/module_system.py @@ -112,9 +112,7 @@ def detect_module_system(project_root: Path, file_path: Path | None = None) -> s # depends on how TypeScript compiles and how Node.js loads the files. if file_path and file_path.suffix.lower() in (".ts", ".tsx"): if pkg_type_from_json is None: - logger.debug( - "TypeScript file without explicit package.json type field - defaulting to CommonJS" - ) + logger.debug("TypeScript file without explicit package.json type field - defaulting to CommonJS") return ModuleSystem.COMMONJS # Strategy 3: Analyze file content for import/export patterns