feat: implement unified DocumentNavigator with lazy loading for all modes
- Add DocumentNavigator UI element for document navigation across viewing and editing modes - Implement lazy loading approach where control appears immediately but navigation content builds on-demand - Position controls on left side following UI convention for consistent navigation experience - Add scroll spy functionality for current section detection - Include responsive design with mobile auto-hide - Create comprehensive development guardrails to prevent JavaScript corruption - Add JavaScript validation tool for syntax error detection 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
161
tools/validate_js.py
Executable file
161
tools/validate_js.py
Executable file
@@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
JavaScript Validation Tool
|
||||
|
||||
Extracts JavaScript from HTML files and validates syntax.
|
||||
Detects common issues like "Uncaught SyntaxError: unexpected token"
|
||||
"""
|
||||
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def extract_javascript_from_html(html_file):
|
||||
"""Extract all JavaScript code from an HTML file."""
|
||||
try:
|
||||
content = Path(html_file).read_text(encoding='utf-8')
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to read {html_file}: {e}")
|
||||
return []
|
||||
|
||||
# Find all <script> blocks
|
||||
script_pattern = r'<script[^>]*>(.*?)</script>'
|
||||
scripts = re.findall(script_pattern, content, re.DOTALL | re.IGNORECASE)
|
||||
|
||||
# Filter out empty scripts and external script tags
|
||||
js_blocks = []
|
||||
for script in scripts:
|
||||
script = script.strip()
|
||||
if script and not script.startswith('http'): # Skip external scripts
|
||||
js_blocks.append(script)
|
||||
|
||||
return js_blocks
|
||||
|
||||
|
||||
def validate_javascript_syntax(js_code):
|
||||
"""Validate JavaScript syntax using Node.js."""
|
||||
try:
|
||||
# Create temporary JS file
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False) as f:
|
||||
f.write(js_code)
|
||||
temp_file = f.name
|
||||
|
||||
# Try to parse with node --check
|
||||
result = subprocess.run(
|
||||
['node', '--check', temp_file],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
# Clean up
|
||||
Path(temp_file).unlink()
|
||||
|
||||
if result.returncode == 0:
|
||||
return True, "✓ Syntax OK"
|
||||
else:
|
||||
return False, f"❌ Syntax Error: {result.stderr.strip()}"
|
||||
|
||||
except FileNotFoundError:
|
||||
# Fallback: Try to detect obvious syntax errors
|
||||
return validate_javascript_basic(js_code)
|
||||
except Exception as e:
|
||||
return False, f"❌ Validation failed: {e}"
|
||||
|
||||
|
||||
def validate_javascript_basic(js_code):
|
||||
"""Basic JavaScript syntax validation without Node.js."""
|
||||
errors = []
|
||||
|
||||
# Check for common syntax issues
|
||||
if js_code.count('{') != js_code.count('}'):
|
||||
errors.append("Mismatched curly braces")
|
||||
|
||||
if js_code.count('(') != js_code.count(')'):
|
||||
errors.append("Mismatched parentheses")
|
||||
|
||||
if js_code.count('[') != js_code.count(']'):
|
||||
errors.append("Mismatched square brackets")
|
||||
|
||||
# Check for unescaped quotes in strings
|
||||
if re.search(r'["\']\s*["\']\s*["\']\s*["\']', js_code):
|
||||
errors.append("Possible quote escaping issue")
|
||||
|
||||
# Check for Python-style string formatting leftover
|
||||
if '${' in js_code and '"}' in js_code:
|
||||
errors.append("Possible Python string template leftover")
|
||||
|
||||
if errors:
|
||||
return False, f"❌ Potential issues: {', '.join(errors)}"
|
||||
else:
|
||||
return True, "✓ Basic validation passed (Node.js not available)"
|
||||
|
||||
|
||||
def validate_html_js(html_file):
|
||||
"""Validate all JavaScript in an HTML file."""
|
||||
print(f"🔍 Validating JavaScript in: {html_file}")
|
||||
|
||||
js_blocks = extract_javascript_from_html(html_file)
|
||||
|
||||
if not js_blocks:
|
||||
print("⚠️ No JavaScript found in HTML file")
|
||||
return True
|
||||
|
||||
print(f"📝 Found {len(js_blocks)} JavaScript blocks")
|
||||
|
||||
all_valid = True
|
||||
for i, js_block in enumerate(js_blocks, 1):
|
||||
print(f"\n--- Script Block {i} ({len(js_block)} chars) ---")
|
||||
|
||||
# Show first few lines for context
|
||||
lines = js_block.split('\n')[:3]
|
||||
preview = '\n'.join(lines)
|
||||
if len(lines) < len(js_block.split('\n')):
|
||||
preview += "\n..."
|
||||
print(f"Preview:\n{preview}")
|
||||
|
||||
is_valid, message = validate_javascript_syntax(js_block)
|
||||
print(message)
|
||||
|
||||
if not is_valid:
|
||||
all_valid = False
|
||||
# Show more context around potential error
|
||||
if 'line' in message.lower():
|
||||
try:
|
||||
line_num = re.search(r'line (\d+)', message, re.IGNORECASE)
|
||||
if line_num:
|
||||
line_no = int(line_num.group(1))
|
||||
lines = js_block.split('\n')
|
||||
start = max(0, line_no - 3)
|
||||
end = min(len(lines), line_no + 2)
|
||||
print("\nContext around error:")
|
||||
for j in range(start, end):
|
||||
marker = ">>> " if j == line_no - 1 else " "
|
||||
print(f"{marker}{j+1:3}: {lines[j]}")
|
||||
except:
|
||||
pass
|
||||
|
||||
print(f"\n{'✅' if all_valid else '❌'} Overall result: {'All JavaScript is valid' if all_valid else 'JavaScript validation failed'}")
|
||||
return all_valid
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python validate_js.py <html_file>")
|
||||
print("Example: python validate_js.py /tmp/test-edit.html")
|
||||
sys.exit(1)
|
||||
|
||||
html_file = sys.argv[1]
|
||||
|
||||
if not Path(html_file).exists():
|
||||
print(f"❌ File not found: {html_file}")
|
||||
sys.exit(1)
|
||||
|
||||
success = validate_html_js(html_file)
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user