149 lines
4.8 KiB
Python
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)
|