codeflash-internal/django/aiservice/aiservice/validators/javascript_validator.py
2026-01-26 15:19:24 +02:00

149 lines
4.8 KiB
Python

"""JavaScript/TypeScript syntax validation.
Uses Node.js for validation when available, with a basic regex fallback.
"""
from __future__ import annotations
import json
import re
import subprocess
from functools import lru_cache
@lru_cache(maxsize=100)
def validate_javascript_syntax(code: str) -> tuple[bool, str | None]:
# TODO(claude): DON'T do this, use some pytohn lib for this instead of spawning a new subprocess
return True, None
"""Validate JavaScript syntax using Node.js.
Args:
code: The JavaScript code to validate
Returns:
A tuple of (is_valid, error_message)
- is_valid: True if the code is syntactically valid
- error_message: None if valid, otherwise the error description
"""
try:
# Use Node.js to parse the code
# We use acorn parser via a small script that reports syntax errors
validation_script = f"""
const acorn = require('acorn');
try {{
acorn.parse({json.dumps(code)}, {{
ecmaVersion: 'latest',
sourceType: 'module',
allowHashBang: true,
allowAwaitOutsideFunction: true,
allowImportExportEverywhere: true,
}});
console.log('VALID');
}} catch (e) {{
console.error(e.message);
process.exit(1);
}}
"""
result = subprocess.run(["node", "-e", validation_script], capture_output=True, text=True, timeout=5)
if result.returncode != 0:
error_msg = result.stderr.strip() or result.stdout.strip()
# Check if the error is due to missing acorn module
if "Cannot find module 'acorn'" in error_msg:
return _fallback_validation(code)
return False, error_msg
return True, None
except subprocess.TimeoutExpired:
return False, "Syntax validation timed out"
except FileNotFoundError:
# Node.js not available, try fallback
return _fallback_validation(code)
except Exception:
# Fallback for any other error
return _fallback_validation(code)
def _fallback_validation(code: str) -> tuple[bool, str | None]:
"""Fallback validation using basic checks when Node.js is not available.
This performs simple bracket matching and basic syntax checks.
"""
try:
# Check for obvious syntax errors
# 1. Bracket matching
brackets = {"(": ")", "[": "]", "{": "}"}
stack = []
in_string = False
string_char = None
escape_next = False
for i, char in enumerate(code):
if escape_next:
escape_next = False
continue
if char == "\\":
escape_next = True
continue
# Handle strings
if char in "'\"`":
if not in_string:
in_string = True
string_char = char
elif char == string_char:
# Check for template literal
if char == "`" or (i + 1 < len(code) and code[i + 1] != string_char):
in_string = False
string_char = None
if in_string:
continue
# Check brackets
if char in brackets:
stack.append((brackets[char], i))
elif char in brackets.values():
if not stack:
return False, f"Unexpected closing bracket '{char}' at position {i}"
expected, _ = stack.pop()
if expected != char:
return False, f"Mismatched bracket '{char}' at position {i}"
if stack:
return False, f"Unclosed bracket '{stack[-1][0]}'"
# 2. Check for common invalid patterns
invalid_patterns = [
(r"function\s*\(\s*\)\s*\{[^}]*\breturn\b[^;]*$", "Missing semicolon after return"),
(r"\bconst\s+\d", "Invalid const declaration - cannot start with number"),
(r"\blet\s+\d", "Invalid let declaration - cannot start with number"),
(r"\bvar\s+\d", "Invalid var declaration - cannot start with number"),
]
for pattern, error_msg in invalid_patterns:
if re.search(pattern, code):
return False, error_msg
return True, None
except Exception as e:
return False, f"Validation error: {e}"
def validate_typescript_syntax(code: str) -> tuple[bool, str | None]:
"""Validate TypeScript syntax.
Since acorn doesn't support TypeScript type annotations, we use the
fallback validation which checks for basic syntax errors like bracket
matching. For complete TypeScript validation, tsc would be needed.
"""
# Acorn doesn't support TypeScript syntax (type annotations, interfaces, etc.)
# Use the fallback validation which is more lenient
return _fallback_validation(code)