Initial commit: Clean DSS implementation

Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm

Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)

Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability

Migration completed: $(date)
🤖 Clean migration with full functionality preserved
This commit is contained in:
Digital Production Factory
2025-12-09 18:45:48 -03:00
commit 276ed71f31
884 changed files with 373737 additions and 0 deletions

441
tools/analyze/react.py Normal file
View File

@@ -0,0 +1,441 @@
"""
React Project Analyzer
Analyzes React codebases to extract component information,
detect patterns, and identify style usage.
"""
import re
from pathlib import Path
from typing import List, Dict, Any, Optional, Set, Tuple
from dataclasses import dataclass, field
from .base import (
ComponentInfo,
Location,
StylePattern,
StylingApproach,
)
# Patterns for React component detection
FUNCTIONAL_COMPONENT = re.compile(
r'(?:export\s+)?(?:const|let|var|function)\s+([A-Z][A-Za-z0-9]*)\s*(?::\s*(?:React\.)?FC)?'
r'\s*(?:=\s*(?:\([^)]*\)|[a-zA-Z_]\w*)\s*=>|\()',
re.MULTILINE
)
CLASS_COMPONENT = re.compile(
r'class\s+([A-Z][A-Za-z0-9]*)\s+extends\s+(?:React\.)?(?:Component|PureComponent)',
re.MULTILINE
)
FORWARD_REF = re.compile(
r'(?:export\s+)?(?:const|let)\s+([A-Z][A-Za-z0-9]*)\s*=\s*(?:React\.)?forwardRef',
re.MULTILINE
)
MEMO_COMPONENT = re.compile(
r'(?:export\s+)?(?:const|let)\s+([A-Z][A-Za-z0-9]*)\s*=\s*(?:React\.)?memo\(',
re.MULTILINE
)
# Import patterns
IMPORT_PATTERN = re.compile(
r'import\s+(?:\{[^}]+\}|\*\s+as\s+\w+|\w+)\s+from\s+["\']([^"\']+)["\']',
re.MULTILINE
)
STYLE_IMPORT = re.compile(
r'import\s+(?:(\w+)\s+from\s+)?["\']([^"\']+\.(?:css|scss|sass|less|styl))["\']',
re.MULTILINE
)
# Inline style patterns
INLINE_STYLE_OBJECT = re.compile(
r'style\s*=\s*\{\s*\{([^}]+)\}\s*\}',
re.MULTILINE | re.DOTALL
)
INLINE_STYLE_VAR = re.compile(
r'style\s*=\s*\{(\w+)\}',
re.MULTILINE
)
# Props extraction
PROPS_DESTRUCTURE = re.compile(
r'\(\s*\{\s*([^}]+)\s*\}\s*(?::\s*[^)]+)?\)',
re.MULTILINE
)
PROPS_INTERFACE = re.compile(
r'interface\s+\w*Props\s*\{([^}]+)\}',
re.MULTILINE | re.DOTALL
)
PROPS_TYPE = re.compile(
r'type\s+\w*Props\s*=\s*\{([^}]+)\}',
re.MULTILINE | re.DOTALL
)
class ReactAnalyzer:
"""
Analyzes React projects for component structure and style usage.
"""
def __init__(self, root_path: str):
self.root = Path(root_path).resolve()
async def analyze(
self,
component_files: Optional[List[Path]] = None
) -> List[ComponentInfo]:
"""
Analyze React components in the project.
Args:
component_files: Optional list of files to analyze.
If None, scans the project.
Returns:
List of ComponentInfo for each detected component.
"""
if component_files is None:
component_files = self._find_component_files()
components = []
for file_path in component_files:
try:
file_components = await self._analyze_file(file_path)
components.extend(file_components)
except Exception as e:
# Log error but continue
continue
return components
def _find_component_files(self) -> List[Path]:
"""Find all potential React component files."""
skip_dirs = {'node_modules', '.git', 'dist', 'build', '.next'}
component_files = []
for ext in ['*.jsx', '*.tsx']:
for path in self.root.rglob(ext):
if not any(skip in path.parts for skip in skip_dirs):
component_files.append(path)
# Also check .js/.ts files that look like components
for ext in ['*.js', '*.ts']:
for path in self.root.rglob(ext):
if any(skip in path.parts for skip in skip_dirs):
continue
# Skip config and utility files
if any(x in path.name.lower() for x in ['config', 'util', 'helper', 'hook', 'context']):
continue
# Check if PascalCase (likely component)
if path.stem[0].isupper():
component_files.append(path)
return component_files
async def _analyze_file(self, file_path: Path) -> List[ComponentInfo]:
"""Analyze a single file for React components."""
content = file_path.read_text(encoding='utf-8', errors='ignore')
components = []
# Find all components in the file
component_matches = []
# Functional components
for match in FUNCTIONAL_COMPONENT.finditer(content):
name = match.group(1)
if self._is_valid_component_name(name):
component_matches.append((name, 'functional', match.start()))
# Class components
for match in CLASS_COMPONENT.finditer(content):
name = match.group(1)
component_matches.append((name, 'class', match.start()))
# forwardRef components
for match in FORWARD_REF.finditer(content):
name = match.group(1)
component_matches.append((name, 'forwardRef', match.start()))
# memo components
for match in MEMO_COMPONENT.finditer(content):
name = match.group(1)
component_matches.append((name, 'memo', match.start()))
# Dedupe by name (keep first occurrence)
seen_names = set()
unique_matches = []
for name, comp_type, pos in component_matches:
if name not in seen_names:
seen_names.add(name)
unique_matches.append((name, comp_type, pos))
# Extract imports (shared across all components in file)
imports = self._extract_imports(content)
style_files = self._extract_style_imports(content)
inline_styles = self._find_inline_styles(content)
# Create ComponentInfo for each
for name, comp_type, pos in unique_matches:
# Extract props for this component
props = self._extract_props(content, name)
# Find child components used
children = self._find_child_components(content, seen_names)
# Check if component has styles
has_styles = bool(style_files) or bool(inline_styles)
components.append(ComponentInfo(
name=name,
path=str(file_path.relative_to(self.root)),
type=comp_type,
props=props,
has_styles=has_styles,
style_files=style_files,
inline_style_count=len(inline_styles),
imports=imports,
exports=self._find_exports(content, name),
children=children,
line_count=content.count('\n') + 1,
))
return components
def _is_valid_component_name(self, name: str) -> bool:
"""Check if a name is a valid React component name."""
# Must be PascalCase
if not name[0].isupper():
return False
# Filter out common non-component patterns
invalid_names = {
'React', 'Component', 'PureComponent', 'Fragment',
'Suspense', 'Provider', 'Consumer', 'Context',
'Error', 'ErrorBoundary', 'Wrapper', 'Container',
'Props', 'State', 'Type', 'Interface',
}
return name not in invalid_names
def _extract_imports(self, content: str) -> List[str]:
"""Extract import paths from file."""
imports = []
for match in IMPORT_PATTERN.finditer(content):
import_path = match.group(1)
# Skip node_modules style imports for brevity
if not import_path.startswith('.') and '/' not in import_path:
continue
imports.append(import_path)
return imports
def _extract_style_imports(self, content: str) -> List[str]:
"""Extract style file imports."""
style_files = []
for match in STYLE_IMPORT.finditer(content):
style_path = match.group(2)
style_files.append(style_path)
return style_files
def _find_inline_styles(self, content: str) -> List[Location]:
"""Find inline style usage locations."""
locations = []
# style={{ ... }}
for match in INLINE_STYLE_OBJECT.finditer(content):
line = content[:match.start()].count('\n') + 1
locations.append(Location(
file_path="", # Will be set by caller
line=line,
))
return locations
def _extract_props(self, content: str, component_name: str) -> List[str]:
"""Extract props for a component."""
props = set()
# Look for destructured props
for match in PROPS_DESTRUCTURE.finditer(content):
props_str = match.group(1)
# Extract prop names from destructuring
for prop in re.findall(r'(\w+)(?:\s*[=:])?', props_str):
if prop and not prop[0].isupper(): # Skip types
props.add(prop)
# Look for Props interface/type
for pattern in [PROPS_INTERFACE, PROPS_TYPE]:
for match in pattern.finditer(content):
props_str = match.group(1)
# Extract prop names
for line in props_str.split('\n'):
prop_match = re.match(r'\s*(\w+)\s*[?:]', line)
if prop_match:
props.add(prop_match.group(1))
return list(props)
def _find_child_components(
self,
content: str,
current_components: Set[str]
) -> List[str]:
"""Find child components used in JSX."""
children = set()
# Find JSX elements that look like components (PascalCase)
jsx_pattern = re.compile(r'<([A-Z][A-Za-z0-9]*)')
for match in jsx_pattern.finditer(content):
component_name = match.group(1)
# Skip current file's components and React built-ins
if component_name not in current_components:
if component_name not in {'Fragment', 'Suspense', 'Provider'}:
children.add(component_name)
return list(children)
def _find_exports(self, content: str, component_name: str) -> List[str]:
"""Find export type for component."""
exports = []
# Default export
if re.search(rf'export\s+default\s+{component_name}\b', content):
exports.append('default')
if re.search(rf'export\s+default\s+(?:function|const)\s+{component_name}\b', content):
exports.append('default')
# Named export
if re.search(rf'export\s+(?:const|function|class)\s+{component_name}\b', content):
exports.append('named')
if re.search(r'export\s*\{[^}]*\b' + re.escape(component_name) + r'\b[^}]*\}', content):
exports.append('named')
return exports
async def find_inline_styles(self, path: Optional[str] = None) -> List[Dict[str, Any]]:
"""
Find all inline style usage in the project.
Returns list of inline style occurrences with:
- file path
- line number
- style content
- component name (if detectable)
"""
search_path = Path(path) if path else self.root
results = []
for ext in ['*.jsx', '*.tsx', '*.js', '*.ts']:
for file_path in search_path.rglob(ext):
if any(skip in file_path.parts for skip in
{'node_modules', '.git', 'dist', 'build'}):
continue
try:
content = file_path.read_text(encoding='utf-8', errors='ignore')
# Find style={{ ... }}
for match in INLINE_STYLE_OBJECT.finditer(content):
line = content[:match.start()].count('\n') + 1
style_content = match.group(1).strip()
results.append({
'file': str(file_path.relative_to(self.root)),
'line': line,
'content': style_content[:200],
'type': 'object',
})
# Find style={variable}
for match in INLINE_STYLE_VAR.finditer(content):
line = content[:match.start()].count('\n') + 1
var_name = match.group(1)
results.append({
'file': str(file_path.relative_to(self.root)),
'line': line,
'content': f'style={{{var_name}}}',
'type': 'variable',
'variable': var_name,
})
except Exception:
continue
return results
async def get_component_tree(self) -> Dict[str, List[str]]:
"""
Build component dependency tree.
Returns dict mapping component names to their child components.
"""
components = await self.analyze()
tree = {}
for comp in components:
tree[comp.name] = comp.children
return tree
async def find_style_patterns(self) -> Dict[str, List[Dict]]:
"""
Find different styling patterns used across the project.
Returns dict with pattern types and their occurrences.
"""
patterns = {
'inline_styles': [],
'css_modules': [],
'styled_components': [],
'emotion': [],
'tailwind': [],
'css_classes': [],
}
component_files = self._find_component_files()
for file_path in component_files:
try:
content = file_path.read_text(encoding='utf-8', errors='ignore')
rel_path = str(file_path.relative_to(self.root))
# CSS Modules
if re.search(r'import\s+\w+\s+from\s+["\'].*\.module\.', content):
patterns['css_modules'].append({'file': rel_path})
# styled-components
if re.search(r'styled\.|from\s+["\']styled-components', content):
patterns['styled_components'].append({'file': rel_path})
# Emotion
if re.search(r'@emotion|css`', content):
patterns['emotion'].append({'file': rel_path})
# Tailwind (className with utility classes)
if re.search(r'className\s*=\s*["\'][^"\']*(?:flex|grid|p-\d|m-\d|bg-)', content):
patterns['tailwind'].append({'file': rel_path})
# Regular CSS classes
if re.search(r'className\s*=\s*["\'][a-zA-Z]', content):
patterns['css_classes'].append({'file': rel_path})
# Inline styles
for match in INLINE_STYLE_OBJECT.finditer(content):
line = content[:match.start()].count('\n') + 1
patterns['inline_styles'].append({
'file': rel_path,
'line': line,
})
except Exception:
continue
return patterns