""" State Management Analyzer Analyzes React state management against Bulletproof React principles: - Appropriate tool for each state type (component, app, server, form, URL) - State localized when possible - Server cache separated (React Query/SWR) - No global state overuse """ import json import re from pathlib import Path from typing import Dict, List def analyze(codebase_path: Path, metadata: Dict) -> List[Dict]: """ Analyze state management patterns. Args: codebase_path: Path to React codebase metadata: Project metadata from discovery phase Returns: List of findings with severity and migration guidance """ findings = [] tech_stack = metadata.get('tech_stack', {}) src_dir = codebase_path / 'src' if not src_dir.exists(): return findings # Check for appropriate state management tools findings.extend(check_state_management_tools(tech_stack)) # Check for data fetching library (server cache state) findings.extend(check_data_fetching_library(tech_stack)) # Check for form state management findings.extend(check_form_state_management(src_dir, tech_stack)) # Check for potential state management issues findings.extend(check_state_patterns(src_dir)) return findings def check_state_management_tools(tech_stack: Dict) -> List[Dict]: """Check for presence of appropriate state management tools.""" findings = [] # Check if any global state management is present has_state_mgmt = any([ tech_stack.get('redux'), tech_stack.get('zustand'), tech_stack.get('jotai'), tech_stack.get('mobx') ]) # If app has many features but no state management, might need it # (This is a heuristic - could be Context-based which is fine) if not has_state_mgmt: findings.append({ 'severity': 'low', 'category': 'state', 'title': 'No explicit global state management detected', 'current_state': 'No Redux, Zustand, Jotai, or MobX found', 'target_state': 'Consider Zustand or Jotai for global state if Context becomes complex. Start with Context + hooks.', 'migration_steps': [ 'Evaluate if Context API is sufficient for your needs', 'If Context becomes complex, consider Zustand (simple) or Jotai (atomic)', 'Avoid Redux unless you need its ecosystem (Redux Toolkit simplifies it)', 'Keep state as local as possible before going global' ], 'effort': 'low', }) return findings def check_data_fetching_library(tech_stack: Dict) -> List[Dict]: """Check for React Query, SWR, or similar for server state.""" findings = [] has_data_fetching = any([ tech_stack.get('react-query'), tech_stack.get('swr'), tech_stack.get('apollo'), tech_stack.get('rtk-query') ]) if not has_data_fetching: findings.append({ 'severity': 'high', 'category': 'state', 'title': 'No data fetching library detected', 'current_state': 'No React Query, SWR, Apollo Client, or RTK Query found', 'target_state': 'Use React Query or SWR for server state management (caching, refetching, optimistic updates)', 'migration_steps': [ 'Install React Query (@tanstack/react-query) or SWR', 'Wrap app with QueryClientProvider (React Query) or SWRConfig (SWR)', 'Convert fetch calls to useQuery hooks', 'Replace manual loading/error states with library patterns', 'Add staleTime, cacheTime configurations as needed' ], 'effort': 'medium', }) return findings def check_form_state_management(src_dir: Path, tech_stack: Dict) -> List[Dict]: """Check for form state management.""" findings = [] has_form_lib = any([ tech_stack.get('react-hook-form'), tech_stack.get('formik') ]) # Look for form components without form library if not has_form_lib: form_files = [] for file_path in src_dir.rglob('*.{tsx,jsx}'): try: with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: content = f.read() # Look for