Files
b3492a6f-f062-43df-b560-ed7…/check_props.py
Nikolay Pecheniev c4674d4dab Initial commit
2026-02-16 17:29:30 +02:00

177 lines
6.1 KiB
Python

#!/usr/bin/env python3
"""
Script to check component props schemas in registry.json against actual component files.
This helps identify mismatches between registry.json and actual component implementations.
"""
import json
import re
import os
from pathlib import Path
from typing import Dict, List, Set, Tuple
def extract_props_from_tsx(file_path: str) -> Dict[str, str]:
"""Extract props from a TypeScript component file."""
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
except FileNotFoundError:
return {}
# Find interface or type definition for props
# Look for patterns like: interface ComponentNameProps { ... }
props_pattern = r'interface\s+(\w+Props)\s*\{([^}]+)\}'
match = re.search(props_pattern, content, re.DOTALL)
if not match:
# Try type alias pattern
props_pattern = r'type\s+(\w+Props)\s*=\s*\{([^}]+)\}'
match = re.search(props_pattern, content, re.DOTALL)
if not match:
return {}
props_content = match.group(2)
props = {}
# Extract prop definitions: propName?: type or propName: type
prop_pattern = r'(\w+)(\??)\s*:\s*([^;]+);'
for prop_match in re.finditer(prop_pattern, props_content):
prop_name = prop_match.group(1)
is_optional = prop_match.group(2) == '?'
prop_type = prop_match.group(3).strip()
# Clean up type (remove comments, extra whitespace)
prop_type = re.sub(r'//.*', '', prop_type).strip()
prop_type = ' '.join(prop_type.split())
props[prop_name] = {
'optional': is_optional,
'type': prop_type
}
return props
def normalize_path(path: str) -> str:
"""Convert @/ path to actual file path."""
if path.startswith('@/'):
return path.replace('@/', 'src/')
return path
def check_component(component_path: str, registry_props: Dict[str, str]) -> Tuple[List[str], List[str], List[str]]:
"""Check a single component's props against registry."""
actual_file = normalize_path(component_path)
if not actual_file.endswith('.tsx'):
actual_file += '.tsx'
actual_props = extract_props_from_tsx(actual_file)
if not actual_props:
return [], [], [f"Could not find props interface in {actual_file}"]
missing_in_registry = []
extra_in_registry = []
type_mismatches = []
# Check props in actual component
for prop_name, prop_info in actual_props.items():
if prop_name not in registry_props:
missing_in_registry.append(prop_name)
else:
# Check if optionality matches
registry_optional = '?' in registry_props[prop_name] or '(required)' in registry_props[prop_name]
if prop_info['optional'] != registry_optional:
type_mismatches.append(f"{prop_name}: optionality mismatch (actual: {'optional' if prop_info['optional'] else 'required'}, registry: {'optional' if registry_optional else 'required'})")
# Check props in registry that aren't in actual component
for prop_name in registry_props.keys():
if prop_name not in actual_props:
extra_in_registry.append(prop_name)
return missing_in_registry, extra_in_registry, type_mismatches
def main():
"""Main function to check all components."""
registry_path = Path('registry.json')
if not registry_path.exists():
print("registry.json not found!")
return
with open(registry_path, 'r', encoding='utf-8') as f:
registry = json.load(f)
issues = []
# Check componentRegistry
for category, components in registry.get('componentRegistry', {}).items():
print(f"\n=== Checking {category} components ===")
for component in components:
name = component.get('name')
path = component.get('path')
props_schema = component.get('propsSchema', {})
if not path:
continue
missing, extra, mismatches = check_component(path, props_schema)
if missing or extra or mismatches:
print(f"\n{name} ({path}):")
if missing:
print(f" Missing in registry: {', '.join(missing)}")
if extra:
print(f" Extra in registry: {', '.join(extra)}")
if mismatches:
print(f" Type mismatches: {', '.join(mismatches)}")
issues.append({
'name': name,
'path': path,
'missing': missing,
'extra': extra,
'mismatches': mismatches
})
# Check sectionRegistry
for category, sections in registry.get('sectionRegistry', {}).items():
print(f"\n=== Checking {category} sections ===")
for section in sections:
name = section.get('name')
path = section.get('path')
props_schema = section.get('propsSchema', {})
if not path:
continue
missing, extra, mismatches = check_component(path, props_schema)
if missing or extra or mismatches:
print(f"\n{name} ({path}):")
if missing:
print(f" Missing in registry: {', '.join(missing)}")
if extra:
print(f" Extra in registry: {', '.join(extra)}")
if mismatches:
print(f" Type mismatches: {', '.join(mismatches)}")
issues.append({
'name': name,
'path': path,
'missing': missing,
'extra': extra,
'mismatches': mismatches
})
print(f"\n\n=== Summary ===")
print(f"Total issues found: {len(issues)}")
if issues:
print("\nComponents with issues:")
for issue in issues:
print(f" - {issue['name']}")
if __name__ == '__main__':
main()