Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:16:43 +08:00
commit 5fce0eef2b
46 changed files with 8067 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
# Changelog
## 0.2.0
- Refactored to Anthropic progressive disclosure pattern
- Updated description with "Use PROACTIVELY when..." format
- Extracted detailed content to workflow/, reference/, examples/ directories
## 0.1.0
- Initial skill release
- React codebase auditing against Bulletproof React architecture
- Anti-pattern detection and migration planning

View File

@@ -0,0 +1,388 @@
# Bulletproof React Auditor Skill
> Comprehensive audit tool for React/TypeScript codebases based on Bulletproof React architecture principles
An Anthropic Skill that analyzes React applications for architectural issues, component anti-patterns, state management problems, and generates prioritized migration plans for adopting Bulletproof React patterns.
## Features
- **Progressive Disclosure**: Three-phase analysis (Discovery → Deep Analysis → Migration Plan)
- **React-Specific**: Tailored for React 16.8+ (hooks-based applications)
- **Comprehensive Analysis**:
- Project Structure (feature-based vs flat)
- Component Architecture (colocation, composition, size)
- State Management (appropriate tools for each state type)
- API Layer (centralized, type-safe patterns)
- Testing Strategy (testing trophy compliance)
- Styling Patterns (component libraries, utility CSS)
- Error Handling (boundaries, interceptors, tracking)
- Performance (code splitting, memoization, optimization)
- Security (authentication, authorization, XSS prevention)
- Standards Compliance (ESLint, TypeScript, naming conventions)
- **Multiple Report Formats**: Markdown, JSON, migration roadmaps
- **Prioritized Migration Plans**: P0-P3 severity with effort estimates
- **ASCII Structure Diagrams**: Visual before/after comparisons
- **Industry Standards**: Based on Bulletproof React best practices
## Installation
### Option 1: Claude Code (Recommended)
1. Clone or copy the `bulletproof-react-auditor` directory to your Claude skills directory
2. Ensure Python 3.8+ is installed
3. No additional dependencies required (uses Python standard library)
### Option 2: Manual Installation
```bash
cd ~/.claude/skills
git clone https://github.com/your-org/bulletproof-react-auditor.git
```
## Usage with Claude Code
### Basic Audit
```
Audit this React codebase using the bulletproof-react-auditor skill.
```
### Structure-Focused Audit
```
Run a structure audit on this React app against Bulletproof React patterns.
```
### Generate Migration Plan
```
Audit this React app and generate a migration plan to Bulletproof React architecture.
```
### Custom Scope
```
Audit this React codebase focusing on:
- Project structure and feature organization
- Component architecture patterns
- State management approach
```
## Direct Script Usage
```bash
# Full audit with Markdown report
python scripts/audit_engine.py /path/to/react-app --output audit.md
# Structure-focused audit
python scripts/audit_engine.py /path/to/react-app --scope structure,components --output report.md
# Generate migration plan
python scripts/audit_engine.py /path/to/react-app --migration-plan --output migration.md
# JSON output for CI/CD integration
python scripts/audit_engine.py /path/to/react-app --format json --output audit.json
# Quick health check only (Phase 1)
python scripts/audit_engine.py /path/to/react-app --phase quick
```
## Output Formats
### Markdown (Default)
Human-readable report with:
- ASCII structure diagrams (before/after)
- Detailed findings with code examples
- Step-by-step migration guidance
- Suitable for PRs, documentation, team reviews
### JSON
Machine-readable format for CI/CD integration:
```json
{
"summary": {
"compliance_score": 72,
"grade": "C",
"critical_issues": 3,
"migration_effort_days": 15
},
"findings": [...],
"metrics": {...},
"migration_plan": [...]
}
```
### Migration Plan
Prioritized roadmap with:
- P0-P3 priority levels
- Effort estimates per task
- Dependency chains
- Before/after code examples
- ADR templates
## Audit Criteria
The skill audits based on 10 Bulletproof React categories:
### 1. Project Structure
- Feature-based organization (80%+ code in features/)
- Unidirectional dependencies (shared → features → app)
- No cross-feature imports
- Proper feature boundaries
### 2. Component Architecture
- Component colocation (near usage)
- Limited props (< 7-10 per component)
- No large components (< 300 LOC)
- No nested render functions
- Proper abstraction (identify repetition first)
### 3. State Management
- Appropriate tool for each state type
- Local state preferred over global
- Server cache separated (React Query/SWR)
- Form state managed (React Hook Form)
- URL state utilized
### 4. API Layer
- Centralized API client
- Type-safe request declarations
- Colocated in features/
- Data fetching hooks
- Error handling
### 5. Testing Strategy
- Testing trophy (70% integration, 20% unit, 10% E2E)
- Semantic queries (getByRole preferred)
- User behavior testing (not implementation)
- 80%+ coverage on critical paths
### 6. Styling Patterns
- Consistent approach (component library or utility CSS)
- Colocated styles
- Design system usage
### 7. Error Handling
- API error interceptors
- Multiple error boundaries
- Error tracking service
- User-friendly messages
### 8. Performance
- Code splitting at routes
- Memoization patterns
- State localization
- Image optimization
- Bundle size monitoring
### 9. Security
- JWT with HttpOnly cookies
- Authorization (RBAC/PBAC)
- Input sanitization
- XSS prevention
### 10. Standards Compliance
- ESLint configured
- TypeScript strict mode
- Prettier setup
- Git hooks (Husky)
- Absolute imports
- Kebab-case naming
See [`reference/audit_criteria.md`](reference/audit_criteria.md) for complete checklist.
## Severity Levels
- **Critical (P0)**: Fix immediately (within 24 hours)
- Security vulnerabilities, breaking architectural patterns
- **High (P1)**: Fix this sprint (within 2 weeks)
- Major architectural violations, significant refactoring needed
- **Medium (P2)**: Fix next quarter (within 3 months)
- Component design issues, state management improvements
- **Low (P3)**: Backlog
- Styling consistency, minor optimizations
See [`reference/severity_matrix.md`](reference/severity_matrix.md) for detailed criteria.
## Migration Approach
### Phase 1: Foundation (Week 1-2)
1. Create feature folders structure
2. Move shared utilities to proper locations
3. Set up absolute imports
4. Configure ESLint for architecture rules
### Phase 2: Feature Extraction (Week 3-6)
1. Identify feature boundaries
2. Move components to features/
3. Colocate API calls with features
4. Extract feature-specific state
### Phase 3: Refinement (Week 7-10)
1. Refactor large components
2. Implement proper state management
3. Add error boundaries
4. Optimize performance
### Phase 4: Polish (Week 11-12)
1. Improve test coverage
2. Add documentation
3. Implement remaining patterns
4. Final review
## Examples
See the [`examples/`](examples/) directory for:
- Sample audit report (React app before Bulletproof)
- Complete migration plan with timeline
- Before/after structure comparisons
- Code transformation examples
## Architecture
```
bulletproof-react-auditor/
├── SKILL.md # Skill definition (Claude loads this)
├── README.md # This file
├── scripts/
│ ├── audit_engine.py # Core orchestrator
│ ├── analyzers/ # Specialized analyzers
│ │ ├── project_structure.py # Folder organization
│ │ ├── component_architecture.py # Component patterns
│ │ ├── state_management.py # State analysis
│ │ ├── api_layer.py # API patterns
│ │ ├── testing_strategy.py # Test quality
│ │ ├── styling_patterns.py # Styling approach
│ │ ├── error_handling.py # Error boundaries
│ │ ├── performance_patterns.py # React performance
│ │ ├── security_practices.py # React security
│ │ └── standards_compliance.py # ESLint, TS, Prettier
│ ├── report_generator.py # Multi-format reports
│ └── migration_planner.py # Prioritized roadmaps
├── reference/
│ ├── bulletproof_principles.md # Complete BR guide
│ ├── audit_criteria.md # Full checklist
│ ├── severity_matrix.md # Issue prioritization
│ └── migration_patterns.md # Common refactorings
└── examples/
├── sample_audit_report.md
├── migration_plan.md
└── before_after_structure.md
```
## Extending the Skill
### Adding a New Analyzer
1. Create `scripts/analyzers/your_analyzer.py`
2. Implement `analyze(codebase_path, metadata)` function
3. Add to `ANALYZERS` dict in `audit_engine.py`
Example:
```python
def analyze(codebase_path: Path, metadata: Dict) -> List[Dict]:
"""Analyze specific Bulletproof React pattern."""
findings = []
# Your analysis logic here
findings.append({
'severity': 'high',
'category': 'your_category',
'title': 'Issue title',
'current_state': 'What exists now',
'target_state': 'Bulletproof recommendation',
'migration_steps': ['Step 1', 'Step 2'],
'effort': 'medium',
})
return findings
```
## CI/CD Integration
### GitHub Actions Example
```yaml
name: Bulletproof React Audit
on: [pull_request]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Bulletproof Audit
run: |
python bulletproof-react-auditor/scripts/audit_engine.py . \
--format json \
--output audit-report.json
- name: Check Compliance Score
run: |
SCORE=$(jq '.summary.compliance_score' audit-report.json)
if [ "$SCORE" -lt 70 ]; then
echo "❌ Compliance score $SCORE below threshold (70)"
exit 1
fi
```
## Best Practices
1. **Audit Before Major Refactoring**: Establish baseline before starting
2. **Incremental Migration**: Don't refactor everything at once
3. **Feature-by-Feature**: Migrate one feature at a time
4. **Test Coverage First**: Ensure tests before restructuring
5. **Team Alignment**: Share Bulletproof React principles with team
6. **Document Decisions**: Create ADRs for architectural changes
7. **Track Progress**: Re-run audits weekly to measure improvement
## Connor's Standards Integration
This skill enforces Connor's specific requirements:
- **TypeScript Strict Mode**: No `any` types allowed
- **Test Coverage**: 80%+ minimum on all code
- **Testing Trophy**: 70% integration, 20% unit, 10% E2E
- **Modern Testing**: Semantic queries (getByRole) preferred
- **No Brittle Tests**: Avoid testing implementation details
- **Code Quality**: No console.log, no `var`, strict equality
- **Git Standards**: Conventional commits, proper branch naming
## Limitations
- Static analysis only (no runtime profiling)
- React 16.8+ required (hooks-based)
- Best suited for SPA/SSG patterns
- Next.js apps may have additional patterns
- Large codebases may need scoped analysis
- Does not execute tests (analyzes test files)
## Version
**1.0.0** - Initial release
## Standards Compliance
Based on:
- Bulletproof React Official Guide
- Kent C. Dodds Testing Trophy
- React Best Practices 2024-25
- TypeScript Strict Mode Guidelines
- Connor's Development Standards
## License
Apache 2.0 (example skill for demonstration)
---
**Built with**: Python 3.8+
**Anthropic Skill Version**: 1.0
**Last Updated**: 2024-10-25
**Bulletproof React Version**: Based on v2024 guidelines

View File

@@ -0,0 +1,130 @@
---
name: bulletproof-react-auditor
description: Use PROACTIVELY when users ask about React project structure, Bulletproof React patterns, or need architecture guidance. Covers structure setup, codebase auditing, anti-pattern detection, and feature-based migration planning. Triggers on "bulletproof react", "React structure help", "organize React app", or "audit my architecture".
---
# Bulletproof React Auditor
Audits React/TypeScript codebases against Bulletproof React architecture with migration planning.
## When to Use
**Natural Language Triggers** (semantic matching, not keywords):
- Questions about React project structure or organization
- Mentions of "bulletproof react" or feature-based architecture
- Requests to audit, review, or improve React codebase
- Planning migrations or refactoring React applications
- Seeking guidance on component patterns or folder structure
**Use Cases**:
- Setting up new React project structure
- Reorganizing existing flat codebase
- Auditing architecture against Bulletproof standards
- Planning migration to feature-based patterns
- Code review for structural anti-patterns
- Generating refactoring guidance and ADRs
## Bulletproof Structure Target
```
src/
├── app/ # Routes, providers
├── components/ # Shared components ONLY
├── config/ # Global config
├── features/ # Feature modules (most code)
│ └── feature/
│ ├── api/
│ ├── components/
│ ├── hooks/
│ ├── stores/
│ └── types/
├── hooks/ # Shared hooks
├── lib/ # Third-party configs
├── stores/ # Global state
├── testing/ # Test utilities
├── types/ # Shared types
└── utils/ # Shared utilities
```
## Audit Categories
| Category | Key Checks |
|----------|------------|
| Structure | Feature folders, cross-feature imports, boundaries |
| Components | Size (<300 LOC), props (<10), composition |
| State | Appropriate categories, localization, server cache |
| API Layer | Centralized client, types, React Query/SWR |
| Testing | Trophy (70/20/10), semantic queries, behavior |
| Styling | Consistent approach, component library |
| Errors | Boundaries, interceptors, tracking |
| Performance | Code splitting, memoization, bundle size |
| Security | JWT cookies, RBAC, XSS prevention |
| Standards | ESLint, Prettier, TS strict, Husky |
## Usage Examples
```
# Basic audit
Audit this React codebase using bulletproof-react-auditor.
# Structure focus
Run structure audit against Bulletproof React patterns.
# Migration plan
Generate migration plan to Bulletproof architecture.
# Custom scope
Audit focusing on structure, components, and state management.
```
## Output Formats
1. **Markdown Report** - ASCII diagrams, code examples
2. **JSON Report** - Machine-readable for CI/CD
3. **Migration Plan** - Roadmap with effort estimates
## Priority Levels
| Priority | Examples | Timeline |
|----------|----------|----------|
| P0 Critical | Security vulns, breaking issues | Immediate |
| P1 High | Feature folder creation, reorg | This sprint |
| P2 Medium | State refactor, API layer | Next quarter |
| P3 Low | Styling, docs, polish | Backlog |
## Connor's Standards Enforced
- TypeScript strict mode (no `any`)
- 80%+ test coverage
- Testing trophy: 70% integration, 20% unit, 10% E2E
- No console.log in production
- Semantic queries (getByRole preferred)
## Best Practices
1. Fix folder organization before component refactoring
2. Extract features before other changes
3. Maintain test coverage during migration
4. Incremental migration, not all at once
5. Document decisions with ADRs
## Limitations
- Static analysis only
- Requires React 16.8+ (hooks)
- Best for SPA/SSG (Next.js differs)
- Large codebases need scoped analysis
## Resources
- [Bulletproof React Guide](https://github.com/alan2207/bulletproof-react)
- [Project Structure](https://github.com/alan2207/bulletproof-react/blob/master/docs/project-structure.md)
- [Sample App](https://github.com/alan2207/bulletproof-react/tree/master/apps/react-vite)
## References
See `reference/` for:
- Complete Bulletproof principles guide
- Detailed audit criteria checklist
- Migration patterns and examples
- ADR templates

View File

@@ -0,0 +1,353 @@
# Bulletproof React Audit Report
**Generated**: 2024-10-25 15:30:00
**Codebase**: `/Users/developer/projects/my-react-app`
**Tech Stack**: React, TypeScript, Vite, Redux, Jest
**Structure Type**: Flat
**Total Files**: 287
**Lines of Code**: 18,420
---
## Executive Summary
### Overall Bulletproof Compliance: **62/100** (Grade: D)
### Category Scores
- **Structure**: 45/100 ⚠️ (Needs major refactoring)
- **Components**: 68/100 ⚠️ (Some improvements needed)
- **State Management**: 55/100 ⚠️ (Missing server cache)
- **API Layer**: 50/100 ⚠️ (Scattered fetch calls)
- **Testing**: 72/100 ⚠️ (Below 80% coverage)
- **Styling**: 80/100 ✅ (Good - using Tailwind)
- **Error Handling**: 40/100 ⚠️ (Missing error boundaries)
- **Performance**: 65/100 ⚠️ (No code splitting)
- **Security**: 58/100 ⚠️ (Tokens in localStorage)
- **Standards**: 85/100 ✅ (Good compliance)
### Issue Summary
- **Critical Issues**: 3
- **High Issues**: 12
- **Medium Issues**: 24
- **Low Issues**: 8
- **Total Issues**: 47
**Estimated Migration Effort**: 18.5 person-days (~4 weeks for 1 developer)
---
## Detailed Findings
### 🚨 CRITICAL (3 issues)
#### 1. Tokens stored in localStorage (Security)
**Current State**: Authentication tokens stored in localStorage in 3 files
**Target State**: Use HttpOnly cookies for JWT storage
**Files Affected**:
- `src/utils/auth.ts`
- `src/hooks/useAuth.ts`
- `src/api/client.ts`
**Impact**: localStorage is vulnerable to XSS attacks. If attacker injects JavaScript, they can steal authentication tokens.
**Migration Steps**:
1. Configure API backend to set JWT in HttpOnly cookie
2. Remove `localStorage.setItem('token', ...)` calls
3. Use `credentials: 'include'` in fetch requests
4. Implement CSRF protection
5. Test authentication flow
**Effort**: MEDIUM
---
#### 2. No features/ directory - flat structure (Structure)
**Current State**: All 287 files in flat src/ directory structure
**Target State**: 80%+ code organized in feature-based modules
**Impact**:
- Difficult to scale beyond current size
- No clear feature boundaries
- High coupling between unrelated code
- Difficult to test in isolation
- New developers struggle to find code
**Migration Steps**:
1. Create `src/features/` directory
2. Identify distinct features (e.g., authentication, dashboard, profile, settings)
3. Create directories for each feature
4. Move feature-specific code to respective features/
5. Organize each feature with api/, components/, hooks/, stores/ subdirectories
6. Update all import paths
7. Test thoroughly after each feature migration
**Effort**: HIGH (plan for 2 weeks)
---
#### 3. No testing framework detected (Testing)
**Current State**: Jest found but no @testing-library/react
**Target State**: Use Testing Library for user-centric React testing
**Impact**:
- Testing components requires low-level implementation testing
- Tests are brittle and break on refactoring
- Cannot follow testing trophy distribution
- Poor test quality
**Migration Steps**:
1. Install @testing-library/react
2. Install @testing-library/jest-dom
3. Configure test setup file
4. Write example tests using Testing Library patterns
5. Train team on Testing Library principles
**Effort**: LOW
---
### ⚠️ HIGH (12 issues - showing top 5)
#### 4. No data fetching library (State Management)
**Current State**: Manual API state management with Redux
**Target State**: Use React Query or SWR for server cache state
**Migration Steps**:
1. Install @tanstack/react-query
2. Wrap app with QueryClientProvider
3. Convert Redux API slices to React Query hooks
4. Remove manual loading/error state management
5. Configure caching strategies
**Effort**: MEDIUM
---
#### 5. Test coverage at 65.3% (Testing)
**Current State**: Line coverage: 65.3%, Branch coverage: 58.2%
**Target State**: Maintain 80%+ test coverage
**Critical Untested Paths**:
- Authentication flow
- Payment processing
- User profile updates
**Migration Steps**:
1. Generate coverage report with uncovered files
2. Prioritize critical paths (authentication, payments)
3. Write integration tests first (70% of tests)
4. Add unit tests for business logic
5. Configure coverage thresholds in jest.config.js
**Effort**: HIGH
---
#### 6. Large component: UserDashboard.tsx (468 LOC) (Components)
**Current State**: `src/components/UserDashboard.tsx` has 468 lines
**Target State**: Components should be < 300 lines
**Migration Steps**:
1. Identify distinct UI sections in dashboard
2. Extract sections to separate components (DashboardHeader, DashboardStats, DashboardActivity)
3. Move business logic to custom hooks (useDashboardData)
4. Extract complex calculations to utility functions
5. Update tests to test new components independently
**Effort**: MEDIUM
---
#### 7. Cross-feature imports detected (Structure)
**Current State**: 8 files import from other features
**Violations**:
- `features/dashboard → features/profile`
- `features/settings → features/authentication`
**Target State**: Features should be independent. Shared code belongs in src/components/ or src/utils/
**Migration Steps**:
1. Identify shared code being imported across features
2. Move truly shared components to src/components/
3. Move shared utilities to src/utils/
4. If code is feature-specific, duplicate it or refactor feature boundaries
**Effort**: MEDIUM
---
#### 8. No error boundaries detected (Error Handling)
**Current State**: No ErrorBoundary components found
**Target State**: Multiple error boundaries at route and feature levels
**Migration Steps**:
1. Create src/components/ErrorBoundary.tsx
2. Wrap each route with ErrorBoundary
3. Add feature-level error boundaries
4. Display user-friendly error messages
5. Log errors to Sentry
**Effort**: LOW
---
### 📊 MEDIUM (24 issues - showing top 3)
#### 9. Too many shared components (Structure)
**Current State**: 62.3% of components in src/components/ (shared)
**Target State**: Most components should be feature-specific
**Migration Steps**:
1. Review each shared component
2. Identify components used by only one feature
3. Move feature-specific components to their features
4. Keep only truly shared components in src/components/
**Effort**: MEDIUM
---
#### 10. Component with 12 props: UserProfileForm (Components)
**Current State**: `UserProfileForm` accepts 12 props
**Target State**: Components should accept < 7-10 props
**Migration Steps**:
1. Group related props into configuration object
2. Use composition (children) instead of render props
3. Extract sub-components with their own props
4. Consider Context for deeply shared state
**Effort**: LOW
---
#### 11. No code splitting detected (Performance)
**Current State**: No React.lazy() usage found
**Target State**: Use code splitting for routes and large components
**Migration Steps**:
1. Wrap route components with React.lazy()
2. Add Suspense boundaries with loading states
3. Split large features into separate chunks
4. Analyze bundle size with vite-bundle-analyzer
**Effort**: LOW
---
## Recommendations
### Immediate Action Required (This Week)
1. **Security**: Move tokens from localStorage to HttpOnly cookies
2. **Structure**: Create features/ directory and plan migration
3. **Testing**: Install Testing Library and write example tests
### This Sprint (Next 2 Weeks)
4. **Structure**: Begin feature extraction (start with 1-2 features)
5. **State**: Add React Query for API calls
6. **Testing**: Increase coverage to 70%+
7. **Components**: Refactor largest components (> 400 LOC)
8. **Errors**: Add error boundaries
### Next Quarter (3 Months)
9. **Structure**: Complete feature-based migration
10. **Testing**: Achieve 80%+ coverage
11. **Performance**: Implement code splitting
12. **State**: Evaluate Redux necessity (might not need with React Query)
### Backlog
13. **Standards**: Add git hooks (Husky) for pre-commit checks
14. **Components**: Improve component colocation
15. **Styling**: Document design system
16. **Naming**: Enforce kebab-case file naming
---
## Migration Priority Roadmap
### Week 1-2: Foundation
- [ ] Fix security issues (localStorage tokens)
- [ ] Create features/ structure
- [ ] Install Testing Library
- [ ] Add error boundaries
- [ ] Configure React Query
### Week 3-4: Feature Extraction Phase 1
- [ ] Extract authentication feature
- [ ] Extract dashboard feature
- [ ] Update imports and test
- [ ] Improve test coverage to 70%
### Week 5-8: Feature Extraction Phase 2
- [ ] Extract remaining features
- [ ] Refactor large components
- [ ] Add comprehensive error handling
- [ ] Achieve 80%+ test coverage
### Week 9-12: Optimization
- [ ] Implement code splitting
- [ ] Performance optimizations
- [ ] Security hardening
- [ ] Documentation updates
---
## Architecture Comparison
### Current Structure
```
src/
├── components/ (180 components - too many!)
├── hooks/ (12 hooks)
├── utils/ (15 utility files)
├── store/ (Redux slices)
├── api/ (API calls)
└── pages/ (Route components)
```
### Target Bulletproof Structure
```
src/
├── app/
│ ├── routes/
│ ├── app.tsx
│ └── provider.tsx
├── features/
│ ├── authentication/
│ │ ├── api/
│ │ ├── components/
│ │ ├── hooks/
│ │ └── stores/
│ ├── dashboard/
│ │ └── ...
│ └── profile/
│ └── ...
├── components/ (Only truly shared - ~20 components)
├── hooks/ (Shared hooks)
├── lib/ (API client, configs)
├── utils/ (Shared utilities)
└── types/ (Shared types)
```
---
*Report generated by Bulletproof React Auditor Skill v1.0*
*Based on Bulletproof React principles and Connor's development standards*

View File

@@ -0,0 +1,250 @@
# Bulletproof React Audit Criteria
Complete checklist for auditing React/TypeScript applications against Bulletproof React architecture principles.
## 1. Project Structure
### Feature-Based Organization
- [ ] 80%+ of code organized in src/features/
- [ ] Each feature has its own directory
- [ ] Features are independent (no cross-feature imports)
- [ ] Feature subdirectories: api/, components/, hooks/, stores/, types/, utils/
### Top-Level Directories
- [ ] src/app/ exists (application layer)
- [ ] src/features/ exists and contains features
- [ ] src/components/ for truly shared components only
- [ ] src/hooks/ for shared custom hooks
- [ ] src/lib/ for third-party configurations
- [ ] src/utils/ for shared utilities
- [ ] src/types/ for shared TypeScript types
- [ ] src/stores/ for global state (if needed)
### Unidirectional Dependencies
- [ ] No cross-feature imports
- [ ] Shared code imported into features (not vice versa)
- [ ] App layer imports from features
- [ ] Clean dependency flow: shared → features → app
## 2. Component Architecture
### Component Design
- [ ] Components < 300 lines of code
- [ ] No large components (> 500 LOC)
- [ ] Components accept < 7-10 props
- [ ] No nested render functions
- [ ] Component colocation (near where used)
- [ ] Proper use of composition over excessive props
### File Organization
- [ ] Kebab-case file naming
- [ ] Components colocated with tests
- [ ] Styles colocated with components
- [ ] Feature-specific components in features/
- [ ] Only truly shared components in src/components/
### Abstraction
- [ ] No premature abstractions
- [ ] Repetition identified before creating abstractions
- [ ] Components are focused and single-purpose
## 3. State Management
### State Categories
- [ ] Component state with useState/useReducer
- [ ] Global state with Context, Zustand, or Jotai
- [ ] Server cache state with React Query or SWR
- [ ] Form state with React Hook Form or Formik
- [ ] URL state with React Router
### State Localization
- [ ] State as local as possible
- [ ] Global state only when necessary
- [ ] No single massive global state object
- [ ] Context split into multiple focused providers
### Server State
- [ ] React Query or SWR for API data
- [ ] Proper caching configuration
- [ ] No manual loading/error state for API calls
- [ ] Optimistic updates where appropriate
## 4. API Layer
### Centralized Configuration
- [ ] Single API client instance
- [ ] Configured in src/lib/
- [ ] Base URL configuration
- [ ] Request/response interceptors
- [ ] Error handling interceptors
### Request Organization
- [ ] API calls colocated in features/*/api/
- [ ] Type-safe request declarations
- [ ] Custom hooks for each endpoint
- [ ] Validation schemas with types
- [ ] Proper error handling
## 5. Testing Strategy
### Coverage
- [ ] 80%+ line coverage
- [ ] 75%+ branch coverage
- [ ] 100% coverage on critical paths
- [ ] Coverage reports generated
### Testing Trophy Distribution
- [ ] ~70% integration tests
- [ ] ~20% unit tests
- [ ] ~10% E2E tests
### Test Quality
- [ ] Tests named "should X when Y"
- [ ] Semantic queries (getByRole, getByLabelText)
- [ ] Testing user behavior, not implementation
- [ ] No brittle tests (exact counts, element ordering)
- [ ] Tests isolated and independent
- [ ] No flaky tests
### Testing Tools
- [ ] Vitest or Jest configured
- [ ] @testing-library/react installed
- [ ] @testing-library/jest-dom for assertions
- [ ] Playwright or Cypress for E2E (optional)
## 6. Styling Patterns
### Styling Approach
- [ ] Consistent styling method chosen
- [ ] Component library (Chakra, Radix, MUI) OR
- [ ] Utility CSS (Tailwind) OR
- [ ] CSS-in-JS (Emotion, styled-components)
- [ ] Styles colocated with components
### Design System
- [ ] Design tokens defined
- [ ] Color palette established
- [ ] Typography scale defined
- [ ] Spacing system consistent
## 7. Error Handling
### Error Boundaries
- [ ] Multiple error boundaries at strategic locations
- [ ] Route-level error boundaries
- [ ] Feature-level error boundaries
- [ ] User-friendly error messages
- [ ] Error recovery mechanisms
### API Errors
- [ ] API error interceptors configured
- [ ] User notifications for errors
- [ ] Automatic retry logic where appropriate
- [ ] Unauthorized user logout
### Error Tracking
- [ ] Sentry or similar service configured
- [ ] User context added to errors
- [ ] Environment-specific error handling
- [ ] Source maps configured for production
## 8. Performance
### Code Splitting
- [ ] React.lazy() for route components
- [ ] Suspense boundaries with loading states
- [ ] Large features split into chunks
- [ ] Bundle size monitored and optimized
### React Performance
- [ ] State localized to prevent re-renders
- [ ] React.memo for expensive components
- [ ] useMemo for expensive calculations
- [ ] useCallback for stable function references
- [ ] Children prop optimization patterns
### Asset Optimization
- [ ] Images lazy loaded
- [ ] Images in modern formats (WebP)
- [ ] Responsive images with srcset
- [ ] Images < 500KB
- [ ] Videos lazy loaded or streamed
## 9. Security
### Authentication
- [ ] JWT stored in HttpOnly cookies (not localStorage)
- [ ] Secure session management
- [ ] Token refresh logic
- [ ] Logout functionality
### Authorization
- [ ] RBAC or PBAC implemented
- [ ] Protected routes
- [ ] Permission checks on actions
- [ ] API-level authorization
### XSS Prevention
- [ ] Input sanitization (DOMPurify)
- [ ] No dangerouslySetInnerHTML without sanitization
- [ ] Output encoding
- [ ] Content Security Policy
### CSRF Protection
- [ ] CSRF tokens for state-changing requests
- [ ] SameSite cookie attribute
- [ ] Verify origin headers
## 10. Standards Compliance
### ESLint
- [ ] .eslintrc or eslint.config.js configured
- [ ] React rules enabled
- [ ] TypeScript rules enabled
- [ ] Accessibility rules (jsx-a11y)
- [ ] Architectural rules (import restrictions)
### TypeScript
- [ ] strict: true in tsconfig.json
- [ ] No `any` types
- [ ] Explicit return types
- [ ] Type definitions for third-party libraries
- [ ] Types colocated with features
### Prettier
- [ ] Prettier configured
- [ ] Format on save enabled
- [ ] Consistent code style
- [ ] .prettierrc configuration
### Git Hooks
- [ ] Husky configured
- [ ] Pre-commit linting
- [ ] Pre-commit type checking
- [ ] Pre-commit tests (optional)
### File Naming
- [ ] Kebab-case for files and directories
- [ ] Consistent naming conventions
- [ ] ESLint rule to enforce naming
### Absolute Imports
- [ ] TypeScript paths configured (@/ prefix)
- [ ] Imports use @/ instead of relative paths
- [ ] Easier refactoring and moving files
## Compliance Scoring
### Grade Scale
- **A (90-100)**: Excellent Bulletproof React compliance
- **B (80-89)**: Good compliance, minor improvements needed
- **C (70-79)**: Moderate compliance, significant refactoring recommended
- **D (60-69)**: Poor compliance, major architectural changes needed
- **F (<60)**: Non-compliant, complete restructuring required
### Category Weights
All categories weighted equally for overall score.
---
**Note**: This checklist represents the ideal Bulletproof React architecture. Adapt based on your project's specific needs and constraints while maintaining the core principles.

View File

@@ -0,0 +1,248 @@
# Severity Matrix
Priority levels and response times for Bulletproof React audit findings.
## Severity Levels
### Critical (P0)
**Fix immediately (within 24 hours)**
#### Criteria
- Security vulnerabilities (tokens in localStorage, XSS risks)
- Breaking architectural violations that prevent scalability
- No testing framework in production app
- TypeScript strict mode disabled with widespread `any` usage
#### Examples
- Authentication tokens stored in localStorage
- No error boundaries in production app
- Zero test coverage on critical paths
- Multiple cross-feature dependencies creating circular imports
#### Impact
- Security breaches possible
- Application instability
- Cannot safely refactor or add features
- Technical debt compounds rapidly
---
### High (P1)
**Fix this sprint (within 2 weeks)**
#### Criteria
- Major architectural misalignment with Bulletproof React
- No data fetching library (manual API state management)
- Test coverage < 80%
- Large components (> 400 LOC) with multiple responsibilities
- No features/ directory with >50 components
#### Examples
- Flat structure instead of feature-based
- Scattered fetch calls throughout components
- No React Query/SWR for server state
- Components with 15+ props
- No error tracking service (Sentry)
#### Impact
- Difficult to maintain and extend
- Poor developer experience
- Slow feature development
- Bugs hard to track and fix
- Testing becomes increasingly difficult
---
### Medium (P2)
**Fix next quarter (within 3 months)**
#### Criteria
- Component design anti-patterns
- State management could be improved
- Missing recommended directories
- Some cross-feature imports
- No code splitting
- Inconsistent styling approaches
#### Examples
- Components 200-400 LOC
- Context with 5+ state values
- Too many shared components (should be feature-specific)
- Nested render functions instead of components
- Multiple styling systems in use
- Large images not optimized
#### Impact
- Code is maintainable but could be better
- Some technical debt accumulating
- Refactoring is more difficult than it should be
- Performance could be better
- Developer onboarding takes longer
---
### Low (P3)
**Backlog (schedule when convenient)**
#### Criteria
- Minor deviations from Bulletproof React patterns
- Stylistic improvements
- Missing nice-to-have features
- Small optimizations
#### Examples
- Files not using kebab-case naming
- No Prettier configured
- No git hooks (Husky)
- Missing some recommended directories
- Test naming doesn't follow "should X when Y"
- Some components could be better colocated
#### Impact
- Minimal impact on development
- Minor inconsistencies
- Small developer experience improvements possible
- Low-priority technical debt
---
## Effort Estimation
### Low Effort (< 1 day)
- Installing dependencies
- Creating configuration files
- Renaming files
- Adding error boundaries
- Setting up Prettier/ESLint
- Configuring git hooks
### Medium Effort (1-5 days)
- Creating features/ structure
- Organizing existing code into features
- Refactoring large components
- Adding React Query/SWR
- Setting up comprehensive error handling
- Improving test coverage to 80%
### High Effort (1-3 weeks)
- Complete architecture restructuring
- Migrating from flat to feature-based structure
- Comprehensive security improvements
- Building out full test suite
- Large-scale refactoring
- Multiple concurrent improvements
---
## Priority Decision Matrix
| Severity | Effort Low | Effort Medium | Effort High |
|----------|------------|---------------|-------------|
| **Critical** | P0 - Do Now | P0 - Do Now | P0 - Plan & Start |
| **High** | P1 - This Sprint | P1 - This Sprint | P1 - This Quarter |
| **Medium** | P2 - Next Sprint | P2 - Next Quarter | P2 - This Year |
| **Low** | P3 - Backlog | P3 - Backlog | P3 - Nice to Have |
---
## Response Time Guidelines
### Critical (P0)
- **Notification**: Immediate (Slack/email alert)
- **Acknowledgment**: Within 1 hour
- **Plan**: Within 4 hours
- **Fix**: Within 24 hours
- **Verification**: Immediately after fix
- **Documentation**: ADR created
### High (P1)
- **Notification**: Within 1 day
- **Acknowledgment**: Within 1 day
- **Plan**: Within 2 days
- **Fix**: Within current sprint (2 weeks)
- **Verification**: Before sprint end
- **Documentation**: Updated in sprint retrospective
### Medium (P2)
- **Notification**: Within 1 week
- **Acknowledgment**: Within 1 week
- **Plan**: Within sprint planning
- **Fix**: Within quarter (3 months)
- **Verification**: Quarterly review
- **Documentation**: Included in quarterly planning
### Low (P3)
- **Notification**: Added to backlog
- **Acknowledgment**: During backlog refinement
- **Plan**: When capacity available
- **Fix**: Opportunistic
- **Verification**: As completed
- **Documentation**: Optional
---
## Category-Specific Severity Guidelines
### Structure Issues
- **Critical**: No features/, flat structure with 100+ components
- **High**: Missing features/, cross-feature dependencies
- **Medium**: Some organizational issues
- **Low**: Minor folder organization improvements
### Component Issues
- **Critical**: Components > 1000 LOC, widespread violations
- **High**: Many components > 400 LOC, 15+ props
- **Medium**: Some large components, nested renders
- **Low**: Minor design improvements needed
### State Management
- **Critical**: No proper state management in complex app
- **High**: No data fetching library, manual API state
- **Medium**: State could be better localized
- **Low**: Could use better state management tool
### Testing Issues
- **Critical**: No testing framework, 0% coverage
- **High**: Coverage < 50%, wrong test distribution
- **Medium**: Coverage 50-79%, some brittle tests
- **Low**: Coverage > 80%, minor test improvements
### Security Issues
- **Critical**: Tokens in localStorage, XSS vulnerabilities
- **High**: No error tracking, missing CSRF protection
- **Medium**: Minor security improvements needed
- **Low**: Security best practices could be better
---
## Migration Planning
### Phase 1: Critical (Week 1)
1. Fix all P0 security issues
2. Establish basic architecture (features/)
3. Set up testing framework
4. Configure error tracking
### Phase 2: High Priority (Weeks 2-6)
1. Migrate to feature-based structure
2. Add React Query/SWR
3. Improve test coverage to 80%
4. Refactor large components
5. Add error boundaries
### Phase 3: Medium Priority (Months 2-3)
1. Optimize component architecture
2. Implement code splitting
3. Improve state management
4. Add comprehensive testing
5. Performance optimizations
### Phase 4: Low Priority (Ongoing)
1. Stylistic improvements
2. Developer experience enhancements
3. Documentation updates
4. Minor refactoring
---
**Note**: These guidelines should be adapted based on your team size, release cadence, and business priorities. Always balance technical debt reduction with feature development.

View File

@@ -0,0 +1,5 @@
"""
Bulletproof React Analyzers
Specialized analyzers for different aspects of Bulletproof React compliance.
"""

View File

@@ -0,0 +1,72 @@
"""
API Layer Analyzer
Analyzes API organization against Bulletproof React patterns:
- Centralized API client
- Type-safe request declarations
- Colocated in features/
- Data fetching hooks
"""
from pathlib import Path
from typing import Dict, List
import re
def analyze(codebase_path: Path, metadata: Dict) -> List[Dict]:
"""Analyze API layer architecture."""
findings = []
src_dir = codebase_path / 'src'
if not src_dir.exists():
return findings
# Check for centralized API client
has_api_config = (src_dir / 'lib').exists() or any(src_dir.rglob('**/api-client.*'))
if not has_api_config:
findings.append({
'severity': 'medium',
'category': 'api',
'title': 'No centralized API client detected',
'current_state': 'No api-client configuration found in src/lib/',
'target_state': 'Create single configured API client instance',
'migration_steps': [
'Create src/lib/api-client.ts with axios or fetch wrapper',
'Configure base URL, headers, interceptors',
'Export configured client',
'Use in all API calls'
],
'effort': 'low',
})
# Check for scattered fetch calls
scattered_fetches = []
for file in src_dir.rglob('*.{ts,tsx,js,jsx}'):
if 'test' in str(file) or 'spec' in str(file):
continue
try:
with open(file, 'r') as f:
content = f.read()
if re.search(r'\bfetch\s*\(', content) and 'api' not in str(file).lower():
scattered_fetches.append(str(file.relative_to(src_dir)))
except:
pass
if len(scattered_fetches) > 3:
findings.append({
'severity': 'high',
'category': 'api',
'title': f'Scattered fetch calls in {len(scattered_fetches)} files',
'current_state': 'fetch() calls throughout components',
'target_state': 'Centralize API calls in feature api/ directories',
'migration_steps': [
'Create api/ directory in each feature',
'Move API calls to dedicated functions',
'Create custom hooks wrapping API calls',
'Use React Query or SWR for data fetching'
],
'effort': 'high',
'affected_files': scattered_fetches[:5],
})
return findings

View File

@@ -0,0 +1,323 @@
"""
Component Architecture Analyzer
Analyzes React component design against Bulletproof React principles:
- Component colocation (near where they're used)
- Limited props (< 7-10)
- Reasonable component size (< 300 LOC)
- No nested render functions
- Proper composition over excessive props
- Consistent naming (kebab-case files)
"""
import re
from pathlib import Path
from typing import Dict, List, Tuple
def analyze(codebase_path: Path, metadata: Dict) -> List[Dict]:
"""
Analyze component architecture for Bulletproof React compliance.
Args:
codebase_path: Path to React codebase
metadata: Project metadata from discovery phase
Returns:
List of findings with severity and migration guidance
"""
findings = []
src_dir = codebase_path / 'src'
if not src_dir.exists():
return findings
# Analyze all React component files
findings.extend(check_component_sizes(src_dir))
findings.extend(check_component_props(src_dir))
findings.extend(check_nested_render_functions(src_dir))
findings.extend(check_file_naming_conventions(src_dir))
findings.extend(check_component_colocation(src_dir))
return findings
def check_component_sizes(src_dir: Path) -> List[Dict]:
"""Check for overly large components."""
findings = []
exclude_dirs = {'node_modules', 'dist', 'build', '.next', 'coverage'}
large_components = []
for component_file in src_dir.rglob('*.{tsx,jsx}'):
if any(excluded in component_file.parts for excluded in exclude_dirs):
continue
try:
with open(component_file, 'r', encoding='utf-8', errors='ignore') as f:
lines = f.readlines()
loc = len([line for line in lines if line.strip() and not line.strip().startswith('//')])
if loc > 300:
large_components.append({
'file': str(component_file.relative_to(src_dir)),
'lines': loc,
'severity': 'critical' if loc > 500 else 'high' if loc > 400 else 'medium'
})
except:
pass
if large_components:
# Report the worst offenders
large_components.sort(key=lambda x: x['lines'], reverse=True)
for comp in large_components[:10]: # Top 10 largest
findings.append({
'severity': comp['severity'],
'category': 'components',
'title': f'Large component ({comp["lines"]} LOC)',
'current_state': f'{comp["file"]} has {comp["lines"]} lines',
'target_state': 'Components should be < 300 lines. Large components are hard to understand and test.',
'migration_steps': [
'Identify distinct responsibilities in the component',
'Extract smaller components for each UI section',
'Move business logic to custom hooks',
'Extract complex rendering logic to separate components',
'Consider splitting into multiple feature components'
],
'effort': 'high' if comp['lines'] > 400 else 'medium',
'file': comp['file'],
})
return findings
def check_component_props(src_dir: Path) -> List[Dict]:
"""Check for components with excessive props."""
findings = []
exclude_dirs = {'node_modules', 'dist', 'build', '.next', 'coverage'}
components_with_many_props = []
for component_file in src_dir.rglob('*.{tsx,jsx}'):
if any(excluded in component_file.parts for excluded in exclude_dirs):
continue
try:
with open(component_file, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# Find component definitions with props
# Pattern matches: function Component({ prop1, prop2, ... })
# and: const Component = ({ prop1, prop2, ... }) =>
props_pattern = re.compile(
r'(?:function|const)\s+(\w+)\s*(?:=\s*)?\(\s*\{([^}]+)\}',
re.MULTILINE
)
matches = props_pattern.findall(content)
for component_name, props_str in matches:
# Count props (split by comma)
props = [p.strip() for p in props_str.split(',') if p.strip()]
# Filter out destructured nested props
actual_props = [p for p in props if not p.startswith('...')]
prop_count = len(actual_props)
if prop_count > 10:
components_with_many_props.append({
'file': str(component_file.relative_to(src_dir)),
'component': component_name,
'prop_count': prop_count,
})
except:
pass
if components_with_many_props:
for comp in components_with_many_props:
findings.append({
'severity': 'critical' if comp['prop_count'] > 15 else 'high',
'category': 'components',
'title': f'Component with {comp["prop_count"]} props: {comp["component"]}',
'current_state': f'{comp["file"]} has {comp["prop_count"]} props',
'target_state': 'Components should accept < 7-10 props. Too many props indicates insufficient composition.',
'migration_steps': [
'Group related props into configuration objects',
'Use composition (children prop) instead of render props',
'Extract sub-components with their own props',
'Consider using Context for deeply shared state',
'Use compound component pattern for complex UIs'
],
'effort': 'medium',
'file': comp['file'],
})
return findings
def check_nested_render_functions(src_dir: Path) -> List[Dict]:
"""Check for nested render functions inside components."""
findings = []
exclude_dirs = {'node_modules', 'dist', 'build', '.next', 'coverage'}
nested_render_functions = []
for component_file in src_dir.rglob('*.{tsx,jsx}'):
if any(excluded in component_file.parts for excluded in exclude_dirs):
continue
try:
with open(component_file, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
lines = content.split('\n')
# Look for patterns like: const renderSomething = () => { ... }
# or: function renderSomething() { ... }
nested_render_pattern = re.compile(r'(?:const|function)\s+(render\w+)\s*[=:]?\s*\([^)]*\)\s*(?:=>)?\s*\{')
for line_num, line in enumerate(lines, start=1):
if nested_render_pattern.search(line):
nested_render_functions.append({
'file': str(component_file.relative_to(src_dir)),
'line': line_num,
})
except:
pass
if nested_render_functions:
# Group by file
files_with_nested = {}
for item in nested_render_functions:
file = item['file']
if file not in files_with_nested:
files_with_nested[file] = []
files_with_nested[file].append(item['line'])
for file, lines in files_with_nested.items():
findings.append({
'severity': 'medium',
'category': 'components',
'title': f'Nested render functions detected ({len(lines)} instances)',
'current_state': f'{file} contains render functions inside component',
'target_state': 'Extract nested render functions into separate components for better reusability and testing.',
'migration_steps': [
'Identify each render function and its dependencies',
'Extract to separate component file',
'Pass necessary props to new component',
'Update tests to test new component in isolation',
'Remove render function from parent component'
],
'effort': 'low',
'file': file,
'affected_lines': lines[:5], # Show first 5
})
return findings
def check_file_naming_conventions(src_dir: Path) -> List[Dict]:
"""Check for consistent kebab-case file naming."""
findings = []
exclude_dirs = {'node_modules', 'dist', 'build', '.next', 'coverage'}
non_kebab_files = []
for file_path in src_dir.rglob('*.{ts,tsx,js,jsx}'):
if any(excluded in file_path.parts for excluded in exclude_dirs):
continue
filename = file_path.stem # filename without extension
# Check if filename is kebab-case (lowercase with hyphens)
# Allow: kebab-case.tsx, lowercase.tsx
# Disallow: PascalCase.tsx, camelCase.tsx, snake_case.tsx
is_kebab_or_lowercase = re.match(r'^[a-z][a-z0-9]*(-[a-z0-9]+)*$', filename)
if not is_kebab_or_lowercase and filename not in ['index', 'App']: # Allow common exceptions
non_kebab_files.append(str(file_path.relative_to(src_dir)))
if len(non_kebab_files) > 5: # Only report if it's a pattern (>5 files)
findings.append({
'severity': 'low',
'category': 'components',
'title': f'Inconsistent file naming ({len(non_kebab_files)} files)',
'current_state': f'{len(non_kebab_files)} files not using kebab-case naming',
'target_state': 'Bulletproof React recommends kebab-case for all files (e.g., user-profile.tsx)',
'migration_steps': [
'Rename files to kebab-case format',
'Update all import statements',
'Run tests to ensure nothing broke',
'Add ESLint rule to enforce kebab-case (unicorn/filename-case)'
],
'effort': 'low',
'affected_files': non_kebab_files[:10], # Show first 10
})
return findings
def check_component_colocation(src_dir: Path) -> List[Dict]:
"""Check if components are colocated near where they're used."""
findings = []
components_dir = src_dir / 'components'
if not components_dir.exists():
return findings
# Find components in shared components/ that are only used once
single_use_components = []
for component_file in components_dir.rglob('*.{tsx,jsx}'):
try:
component_name = component_file.stem
# Search for imports of this component across codebase
import_pattern = re.compile(rf'import.*{component_name}.*from.*[\'"]/|@/')
usage_count = 0
used_in_feature = None
for search_file in src_dir.rglob('*.{ts,tsx,js,jsx}'):
if search_file == component_file:
continue
try:
with open(search_file, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
if import_pattern.search(content):
usage_count += 1
# Check if used in a feature
if 'features' in search_file.parts:
features_index = search_file.parts.index('features')
if features_index + 1 < len(search_file.parts):
feature_name = search_file.parts[features_index + 1]
if used_in_feature is None:
used_in_feature = feature_name
elif used_in_feature != feature_name:
used_in_feature = 'multiple'
except:
pass
# If used only in one feature, it should be colocated there
if usage_count == 1 and used_in_feature and used_in_feature != 'multiple':
single_use_components.append({
'file': str(component_file.relative_to(src_dir)),
'component': component_name,
'feature': used_in_feature,
})
except:
pass
if single_use_components:
for comp in single_use_components[:5]: # Top 5
findings.append({
'severity': 'low',
'category': 'components',
'title': f'Component used in only one feature: {comp["component"]}',
'current_state': f'{comp["file"]} is in shared components/ but only used in {comp["feature"]} feature',
'target_state': 'Components used by only one feature should be colocated in that feature directory.',
'migration_steps': [
f'Move {comp["file"]} to src/features/{comp["feature"]}/components/',
'Update import in the feature',
'Run tests to verify',
'Remove from shared components/'
],
'effort': 'low',
'file': comp['file'],
})
return findings

View File

@@ -0,0 +1,62 @@
"""
Error Handling Analyzer
Analyzes error handling patterns:
- Error boundaries present
- API error interceptors
- Error tracking (Sentry)
"""
from pathlib import Path
from typing import Dict, List
import re
def analyze(codebase_path: Path, metadata: Dict) -> List[Dict]:
"""Analyze error handling patterns."""
findings = []
src_dir = codebase_path / 'src'
tech_stack = metadata.get('tech_stack', {})
if not src_dir.exists():
return findings
# Check for error boundaries
error_boundaries = list(src_dir.rglob('**/error-boundary.*')) + \
list(src_dir.rglob('**/ErrorBoundary.*'))
if not error_boundaries:
findings.append({
'severity': 'high',
'category': 'errors',
'title': 'No error boundaries detected',
'current_state': 'No ErrorBoundary components found',
'target_state': 'Implement multiple error boundaries at strategic locations',
'migration_steps': [
'Create ErrorBoundary component with componentDidCatch',
'Wrap route components with ErrorBoundary',
'Add feature-level error boundaries',
'Display user-friendly error messages'
],
'effort': 'low',
})
# Check for error tracking
if not tech_stack.get('sentry'):
findings.append({
'severity': 'medium',
'category': 'errors',
'title': 'No error tracking service detected',
'current_state': 'No Sentry or similar error tracking',
'target_state': 'Use Sentry for production error monitoring',
'migration_steps': [
'Sign up for Sentry',
'Install @sentry/react',
'Configure Sentry.init() in app entry',
'Add user context and tags',
'Set up error alerts'
],
'effort': 'low',
})
return findings

View File

@@ -0,0 +1,76 @@
"""
Performance Patterns Analyzer
Analyzes React performance optimizations:
- Code splitting at routes
- Memoization patterns
- Image optimization
"""
from pathlib import Path
from typing import Dict, List
import re
def analyze(codebase_path: Path, metadata: Dict) -> List[Dict]:
"""Analyze performance patterns."""
findings = []
src_dir = codebase_path / 'src'
if not src_dir.exists():
return findings
# Check for lazy loading
has_lazy_loading = False
for file in src_dir.rglob('*.{ts,tsx,js,jsx}'):
try:
with open(file, 'r') as f:
content = f.read()
if 'React.lazy' in content or 'lazy(' in content:
has_lazy_loading = True
break
except:
pass
if not has_lazy_loading:
findings.append({
'severity': 'medium',
'category': 'performance',
'title': 'No code splitting detected',
'current_state': 'No React.lazy() usage found',
'target_state': 'Use code splitting for routes and large components',
'migration_steps': [
'Wrap route components with React.lazy()',
'Add Suspense boundaries with loading states',
'Split large features into separate chunks',
'Analyze bundle size with build tools'
],
'effort': 'low',
})
# Check for large images
assets_dir = codebase_path / 'public' / 'assets'
if assets_dir.exists():
large_images = []
for img in assets_dir.rglob('*.{jpg,jpeg,png,gif}'):
size_mb = img.stat().st_size / (1024 * 1024)
if size_mb > 0.5: # Larger than 500KB
large_images.append((str(img.name), size_mb))
if large_images:
findings.append({
'severity': 'medium',
'category': 'performance',
'title': f'{len(large_images)} large images detected',
'current_state': f'Images larger than 500KB',
'target_state': 'Optimize images with modern formats and lazy loading',
'migration_steps': [
'Convert to WebP format',
'Add lazy loading with loading="lazy"',
'Use srcset for responsive images',
'Compress images with tools like sharp'
],
'effort': 'low',
})
return findings

View File

@@ -0,0 +1,369 @@
"""
Project Structure Analyzer
Analyzes React project structure against Bulletproof React patterns:
- Feature-based organization (src/features/)
- Unidirectional dependencies (shared → features → app)
- No cross-feature imports
- Proper folder hierarchy
"""
import re
from pathlib import Path
from typing import Dict, List, Set
def analyze(codebase_path: Path, metadata: Dict) -> List[Dict]:
"""
Analyze project structure for Bulletproof React compliance.
Args:
codebase_path: Path to React codebase
metadata: Project metadata from discovery phase
Returns:
List of findings with severity and migration guidance
"""
findings = []
src_dir = codebase_path / 'src'
if not src_dir.exists():
findings.append({
'severity': 'critical',
'category': 'structure',
'title': 'Missing src/ directory',
'current_state': 'No src/ directory found',
'target_state': 'All source code should be in src/ directory',
'migration_steps': [
'Create src/ directory',
'Move all source files to src/',
'Update import paths',
'Update build configuration'
],
'effort': 'medium',
})
return findings
# Check for Bulletproof structure
findings.extend(check_bulletproof_structure(src_dir))
# Check for cross-feature imports
findings.extend(check_cross_feature_imports(src_dir))
# Analyze features/ organization
findings.extend(analyze_features_directory(src_dir))
# Check shared code organization
findings.extend(check_shared_code_organization(src_dir))
# Check for architectural violations
findings.extend(check_architectural_violations(src_dir))
return findings
def check_bulletproof_structure(src_dir: Path) -> List[Dict]:
"""Check for presence of Bulletproof React folder structure."""
findings = []
# Required top-level directories for Bulletproof React
bulletproof_dirs = {
'app': 'Application layer (routes, app.tsx, provider.tsx, router.tsx)',
'features': 'Feature modules (80%+ of code should be here)',
}
# Recommended directories
recommended_dirs = {
'components': 'Shared components used across multiple features',
'hooks': 'Shared custom hooks',
'lib': 'Third-party library configurations',
'utils': 'Shared utility functions',
'types': 'Shared TypeScript types',
}
# Check required directories
for dir_name, description in bulletproof_dirs.items():
dir_path = src_dir / dir_name
if not dir_path.exists():
findings.append({
'severity': 'critical' if dir_name == 'features' else 'high',
'category': 'structure',
'title': f'Missing {dir_name}/ directory',
'current_state': f'No {dir_name}/ directory found',
'target_state': f'{dir_name}/ directory should exist: {description}',
'migration_steps': [
f'Create src/{dir_name}/ directory',
f'Organize code according to Bulletproof React {dir_name} pattern',
'Update imports to use new structure'
],
'effort': 'high' if dir_name == 'features' else 'medium',
})
# Check recommended directories (lower severity)
missing_recommended = []
for dir_name, description in recommended_dirs.items():
dir_path = src_dir / dir_name
if not dir_path.exists():
missing_recommended.append(f'{dir_name}/ ({description})')
if missing_recommended:
findings.append({
'severity': 'medium',
'category': 'structure',
'title': 'Missing recommended directories',
'current_state': f'Missing: {", ".join([d.split("/")[0] for d in missing_recommended])}',
'target_state': 'Bulletproof React recommends these directories for shared code',
'migration_steps': [
'Create missing directories as needed',
'Move shared code to appropriate directories',
'Ensure proper separation between shared and feature-specific code'
],
'effort': 'low',
})
return findings
def check_cross_feature_imports(src_dir: Path) -> List[Dict]:
"""Detect cross-feature imports (architectural violation)."""
findings = []
features_dir = src_dir / 'features'
if not features_dir.exists():
return findings
# Get all feature directories
feature_dirs = [d for d in features_dir.iterdir() if d.is_dir() and not d.name.startswith('.')]
violations = []
for feature_dir in feature_dirs:
# Find all TypeScript/JavaScript files in this feature
for file_path in feature_dir.rglob('*.{ts,tsx,js,jsx}'):
try:
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# Check for imports from other features
import_pattern = re.compile(r'from\s+[\'"]([^\'\"]+)[\'"]')
imports = import_pattern.findall(content)
for imp in imports:
# Check if importing from another feature
if imp.startswith('../') or imp.startswith('@/features/'):
# Extract feature name from import path
if '@/features/' in imp:
imported_feature = imp.split('@/features/')[1].split('/')[0]
elif '../' in imp:
# Handle relative imports
parts = imp.split('/')
if 'features' in parts:
idx = parts.index('features')
if idx + 1 < len(parts):
imported_feature = parts[idx + 1]
else:
continue
else:
continue
else:
continue
# Check if importing from different feature
current_feature = feature_dir.name
if imported_feature != current_feature and imported_feature in [f.name for f in feature_dirs]:
violations.append({
'file': str(file_path.relative_to(src_dir)),
'from_feature': current_feature,
'to_feature': imported_feature,
'import': imp
})
except:
pass
if violations:
# Group violations by feature
grouped = {}
for v in violations:
key = f"{v['from_feature']}{v['to_feature']}"
if key not in grouped:
grouped[key] = []
grouped[key].append(v['file'])
for import_path, files in grouped.items():
findings.append({
'severity': 'high',
'category': 'structure',
'title': f'Cross-feature import: {import_path}',
'current_state': f'{len(files)} file(s) import from another feature',
'target_state': 'Features should be independent. Shared code belongs in src/components/, src/hooks/, or src/utils/',
'migration_steps': [
'Identify what code is being shared between features',
'Move truly shared code to src/components/, src/hooks/, or src/utils/',
'If code is feature-specific, duplicate it or refactor feature boundaries',
'Update imports to use shared code location'
],
'effort': 'medium',
'affected_files': files[:5], # Show first 5 files
})
return findings
def analyze_features_directory(src_dir: Path) -> List[Dict]:
"""Analyze features/ directory structure."""
findings = []
features_dir = src_dir / 'features'
if not features_dir.exists():
return findings
feature_dirs = [d for d in features_dir.iterdir() if d.is_dir() and not d.name.startswith('.')]
if len(feature_dirs) == 0:
findings.append({
'severity': 'high',
'category': 'structure',
'title': 'Empty features/ directory',
'current_state': 'features/ directory exists but contains no features',
'target_state': '80%+ of application code should be organized in feature modules',
'migration_steps': [
'Identify distinct features in your application',
'Create a directory for each feature in src/features/',
'Move feature-specific code to appropriate feature directories',
'Organize each feature with api/, components/, hooks/, stores/, types/, utils/ as needed'
],
'effort': 'high',
})
return findings
# Check each feature for proper internal structure
for feature_dir in feature_dirs:
feature_name = feature_dir.name
# Recommended feature subdirectories
feature_subdirs = ['api', 'components', 'hooks', 'stores', 'types', 'utils']
has_subdirs = any((feature_dir / subdir).exists() for subdir in feature_subdirs)
# Count files in feature root
root_files = [f for f in feature_dir.iterdir() if f.is_file() and f.suffix in {'.ts', '.tsx', '.js', '.jsx'}]
if len(root_files) > 5 and not has_subdirs:
findings.append({
'severity': 'medium',
'category': 'structure',
'title': f'Feature "{feature_name}" lacks internal organization',
'current_state': f'{len(root_files)} files in feature root without subdirectories',
'target_state': 'Features should be organized with api/, components/, hooks/, stores/, types/, utils/ subdirectories',
'migration_steps': [
f'Create subdirectories in src/features/{feature_name}/',
'Move API calls to api/',
'Move components to components/',
'Move hooks to hooks/',
'Move types to types/',
'Move utilities to utils/'
],
'effort': 'low',
})
return findings
def check_shared_code_organization(src_dir: Path) -> List[Dict]:
"""Check if shared code is properly organized."""
findings = []
components_dir = src_dir / 'components'
features_dir = src_dir / 'features'
if not components_dir.exists():
return findings
# Count components
shared_components = list(components_dir.rglob('*.{tsx,jsx}'))
shared_count = len(shared_components)
# Count feature components
feature_count = 0
if features_dir.exists():
feature_count = len(list(features_dir.rglob('**/components/**/*.{tsx,jsx}')))
total_components = shared_count + feature_count
if total_components > 0:
shared_percentage = (shared_count / total_components) * 100
# Bulletproof React recommends 80%+ code in features
if shared_percentage > 40:
findings.append({
'severity': 'medium',
'category': 'structure',
'title': 'Too many shared components',
'current_state': f'{shared_percentage:.1f}% of components are in src/components/ (shared)',
'target_state': 'Most components should be feature-specific. Only truly shared components belong in src/components/',
'migration_steps': [
'Review each component in src/components/',
'Identify components used by only one feature',
'Move feature-specific components to their feature directories',
'Keep only truly shared components in src/components/'
],
'effort': 'medium',
})
return findings
def check_architectural_violations(src_dir: Path) -> List[Dict]:
"""Check for common architectural violations."""
findings = []
# Check for business logic in components/
components_dir = src_dir / 'components'
if components_dir.exists():
large_components = []
for component_file in components_dir.rglob('*.{tsx,jsx}'):
try:
with open(component_file, 'r', encoding='utf-8', errors='ignore') as f:
lines = len(f.readlines())
if lines > 200:
large_components.append((str(component_file.relative_to(src_dir)), lines))
except:
pass
if large_components:
findings.append({
'severity': 'medium',
'category': 'structure',
'title': 'Large components in shared components/',
'current_state': f'{len(large_components)} component(s) > 200 lines in src/components/',
'target_state': 'Shared components should be simple and reusable. Complex components likely belong in features/',
'migration_steps': [
'Review large shared components',
'Extract business logic to feature-specific hooks or utilities',
'Consider moving complex components to features/ if feature-specific',
'Keep shared components simple and focused'
],
'effort': 'medium',
'affected_files': [f[0] for f in large_components[:5]],
})
# Check for proper app/ structure
app_dir = src_dir / 'app'
if app_dir.exists():
expected_app_files = ['app.tsx', 'provider.tsx', 'router.tsx']
has_routing = any((app_dir / f).exists() or (app_dir / 'routes').exists() for f in ['router.tsx', 'routes.tsx'])
if not has_routing:
findings.append({
'severity': 'low',
'category': 'structure',
'title': 'Missing routing configuration in app/',
'current_state': 'No router.tsx or routes/ found in src/app/',
'target_state': 'Bulletproof React recommends centralizing routing in src/app/router.tsx or src/app/routes/',
'migration_steps': [
'Create src/app/router.tsx or src/app/routes/',
'Define all application routes in one place',
'Use code splitting for route-level lazy loading'
],
'effort': 'low',
})
return findings

View File

@@ -0,0 +1,79 @@
"""
Security Practices Analyzer
Analyzes React security patterns:
- JWT with HttpOnly cookies
- Input sanitization
- XSS prevention
"""
from pathlib import Path
from typing import Dict, List
import re
def analyze(codebase_path: Path, metadata: Dict) -> List[Dict]:
"""Analyze security practices."""
findings = []
src_dir = codebase_path / 'src'
if not src_dir.exists():
return findings
# Check for localStorage token storage (security risk)
localstorage_auth = []
for file in src_dir.rglob('*.{ts,tsx,js,jsx}'):
try:
with open(file, 'r') as f:
content = f.read()
if re.search(r'localStorage\.(get|set)Item\s*\(\s*[\'"].*token.*[\'"]\s*\)', content, re.IGNORECASE):
localstorage_auth.append(str(file.relative_to(src_dir)))
except:
pass
if localstorage_auth:
findings.append({
'severity': 'high',
'category': 'security',
'title': f'Tokens stored in localStorage ({len(localstorage_auth)} files)',
'current_state': 'Authentication tokens in localStorage (XSS vulnerable)',
'target_state': 'Use HttpOnly cookies for JWT storage',
'migration_steps': [
'Configure API to set tokens in HttpOnly cookies',
'Remove localStorage token storage',
'Use credentials: "include" in fetch requests',
'Implement CSRF protection'
],
'effort': 'medium',
'affected_files': localstorage_auth[:3],
})
# Check for dangerouslySetInnerHTML
dangerous_html = []
for file in src_dir.rglob('*.{tsx,jsx}'):
try:
with open(file, 'r') as f:
content = f.read()
if 'dangerouslySetInnerHTML' in content:
dangerous_html.append(str(file.relative_to(src_dir)))
except:
pass
if dangerous_html:
findings.append({
'severity': 'high',
'category': 'security',
'title': f'dangerouslySetInnerHTML usage ({len(dangerous_html)} files)',
'current_state': 'Using dangerouslySetInnerHTML (XSS risk)',
'target_state': 'Sanitize HTML input with DOMPurify',
'migration_steps': [
'Install dompurify',
'Sanitize HTML before rendering',
'Prefer safe alternatives when possible',
'Add security review for HTML rendering'
],
'effort': 'low',
'affected_files': dangerous_html[:3],
})
return findings

View File

@@ -0,0 +1,105 @@
"""
Standards Compliance Analyzer
Analyzes project standards:
- ESLint configuration
- TypeScript strict mode
- Prettier setup
- Git hooks (Husky)
- Naming conventions
"""
from pathlib import Path
from typing import Dict, List
import json
def analyze(codebase_path: Path, metadata: Dict) -> List[Dict]:
"""Analyze standards compliance."""
findings = []
tech_stack = metadata.get('tech_stack', {})
# Check ESLint
eslint_config = any([
(codebase_path / '.eslintrc.js').exists(),
(codebase_path / '.eslintrc.json').exists(),
(codebase_path / 'eslint.config.js').exists(),
])
if not eslint_config:
findings.append({
'severity': 'high',
'category': 'standards',
'title': 'No ESLint configuration',
'current_state': 'No .eslintrc or eslint.config found',
'target_state': 'Configure ESLint with React and TypeScript rules',
'migration_steps': [
'Install eslint and plugins',
'Create .eslintrc.js configuration',
'Add recommended rules for React and TS',
'Add lint script to package.json',
'Fix existing violations'
],
'effort': 'low',
})
# Check TypeScript strict mode
tsconfig = codebase_path / 'tsconfig.json'
if tsconfig.exists():
try:
with open(tsconfig, 'r') as f:
config = json.load(f)
strict = config.get('compilerOptions', {}).get('strict', False)
if not strict:
findings.append({
'severity': 'high',
'category': 'standards',
'title': 'TypeScript strict mode disabled',
'current_state': 'strict: false in tsconfig.json',
'target_state': 'Enable strict mode for better type safety',
'migration_steps': [
'Set "strict": true in compilerOptions',
'Fix type errors incrementally',
'Add explicit return types',
'Remove any types'
],
'effort': 'high',
})
except:
pass
# Check Prettier
if not tech_stack.get('prettier'):
findings.append({
'severity': 'low',
'category': 'standards',
'title': 'No Prettier detected',
'current_state': 'Prettier not in dependencies',
'target_state': 'Use Prettier for consistent code formatting',
'migration_steps': [
'Install prettier',
'Create .prettierrc configuration',
'Enable "format on save" in IDE',
'Run prettier on all files'
],
'effort': 'low',
})
# Check Husky
if not tech_stack.get('husky'):
findings.append({
'severity': 'low',
'category': 'standards',
'title': 'No git hooks (Husky) detected',
'current_state': 'No pre-commit hooks',
'target_state': 'Use Husky for pre-commit linting and testing',
'migration_steps': [
'Install husky and lint-staged',
'Set up pre-commit hook',
'Run lint and type-check before commits',
'Prevent bad code from entering repo'
],
'effort': 'low',
})
return findings

View File

@@ -0,0 +1,199 @@
"""
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 <form> tags
if re.search(r'<form[>\s]', content, re.IGNORECASE):
form_files.append(str(file_path.relative_to(src_dir)))
except:
pass
if len(form_files) > 3: # More than 3 forms suggests need for form library
findings.append({
'severity': 'medium',
'category': 'state',
'title': f'No form library but {len(form_files)} forms detected',
'current_state': f'{len(form_files)} form components without React Hook Form or Formik',
'target_state': 'Use React Hook Form for performant form state management',
'migration_steps': [
'Install react-hook-form',
'Replace controlled form state with useForm() hook',
'Use register() for input registration',
'Handle validation with yup or zod schemas',
'Reduce re-renders with uncontrolled inputs'
],
'effort': 'medium',
'affected_files': form_files[:5],
})
return findings
def check_state_patterns(src_dir: Path) -> List[Dict]:
"""Check for common state management anti-patterns."""
findings = []
# Look for large Context providers (potential performance issue)
large_contexts = []
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 Context creation with many values
if 'createContext' in content:
# Count useState hooks in the provider
state_count = len(re.findall(r'useState\s*\(', content))
if state_count > 5:
large_contexts.append({
'file': str(file_path.relative_to(src_dir)),
'state_count': state_count
})
except:
pass
if large_contexts:
for ctx in large_contexts:
findings.append({
'severity': 'medium',
'category': 'state',
'title': f'Large Context with {ctx["state_count"]} state values',
'current_state': f'{ctx["file"]} has many state values in one Context',
'target_state': 'Split large Contexts into smaller, focused Contexts to prevent unnecessary re-renders',
'migration_steps': [
'Identify which state values change together',
'Create separate Contexts for independent state',
'Use Context composition for related state',
'Consider Zustand/Jotai for complex global state'
],
'effort': 'medium',
'file': ctx['file'],
})
return findings

View File

@@ -0,0 +1,59 @@
"""
Styling Patterns Analyzer
Analyzes styling approach against Bulletproof React:
- Consistent styling method
- Component library usage
- Colocated styles
"""
from pathlib import Path
from typing import Dict, List
def analyze(codebase_path: Path, metadata: Dict) -> List[Dict]:
"""Analyze styling patterns."""
findings = []
tech_stack = metadata.get('tech_stack', {})
# Check for styling approach
styling_tools = []
if tech_stack.get('tailwind'): styling_tools.append('Tailwind CSS')
if tech_stack.get('styled-components'): styling_tools.append('styled-components')
if tech_stack.get('emotion'): styling_tools.append('Emotion')
if tech_stack.get('chakra-ui'): styling_tools.append('Chakra UI')
if tech_stack.get('mui'): styling_tools.append('Material UI')
if tech_stack.get('radix-ui'): styling_tools.append('Radix UI')
if not styling_tools:
findings.append({
'severity': 'low',
'category': 'styling',
'title': 'No component library or utility CSS detected',
'current_state': 'No Tailwind, Chakra UI, Radix UI, or other styling system',
'target_state': 'Use component library (Chakra, Radix) or utility CSS (Tailwind)',
'migration_steps': [
'Choose styling approach based on needs',
'Install Tailwind CSS (utility-first) or Chakra UI (component library)',
'Configure theme and design tokens',
'Migrate components gradually'
],
'effort': 'medium',
})
elif len(styling_tools) > 2:
findings.append({
'severity': 'medium',
'category': 'styling',
'title': f'Multiple styling approaches ({len(styling_tools)})',
'current_state': f'Using: {", ".join(styling_tools)}',
'target_state': 'Standardize on single styling approach',
'migration_steps': [
'Choose primary styling system',
'Create migration plan',
'Update style guide',
'Refactor components incrementally'
],
'effort': 'high',
})
return findings

View File

@@ -0,0 +1,313 @@
"""
Testing Strategy Analyzer
Analyzes React testing against Bulletproof React and Connor's standards:
- Testing trophy distribution (70% integration, 20% unit, 10% E2E)
- 80%+ coverage requirement
- Semantic queries (getByRole preferred)
- User behavior testing (not implementation details)
- Test naming ("should X when Y")
"""
import json
import re
from pathlib import Path
from typing import Dict, List
def analyze(codebase_path: Path, metadata: Dict) -> List[Dict]:
"""
Analyze testing strategy and quality.
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 testing framework
findings.extend(check_testing_framework(tech_stack))
# Check test coverage
findings.extend(check_test_coverage(codebase_path))
# Analyze test distribution (unit vs integration vs E2E)
findings.extend(analyze_test_distribution(codebase_path))
# Check test quality patterns
findings.extend(check_test_quality(codebase_path))
return findings
def check_testing_framework(tech_stack: Dict) -> List[Dict]:
"""Check for modern testing setup."""
findings = []
has_test_framework = tech_stack.get('vitest') or tech_stack.get('jest')
has_testing_library = tech_stack.get('testing-library')
if not has_test_framework:
findings.append({
'severity': 'critical',
'category': 'testing',
'title': 'No testing framework detected',
'current_state': 'No Vitest or Jest found',
'target_state': 'Use Vitest (modern, fast) or Jest for testing',
'migration_steps': [
'Install Vitest (recommended for Vite) or Jest',
'Install @testing-library/react',
'Configure test setup file',
'Add test scripts to package.json',
'Set up coverage reporting'
],
'effort': 'medium',
})
if not has_testing_library:
findings.append({
'severity': 'high',
'category': 'testing',
'title': 'Testing Library not found',
'current_state': 'No @testing-library/react detected',
'target_state': 'Use Testing Library for user-centric testing',
'migration_steps': [
'Install @testing-library/react',
'Install @testing-library/jest-dom for assertions',
'Use render() and semantic queries (getByRole)',
'Follow testing-library principles (test behavior, not implementation)'
],
'effort': 'low',
})
return findings
def check_test_coverage(codebase_path: Path) -> List[Dict]:
"""Check test coverage if available."""
findings = []
# Look for coverage reports
coverage_file = codebase_path / 'coverage' / 'coverage-summary.json'
if coverage_file.exists():
try:
with open(coverage_file, 'r') as f:
coverage_data = json.load(f)
total_coverage = coverage_data.get('total', {})
line_coverage = total_coverage.get('lines', {}).get('pct', 0)
branch_coverage = total_coverage.get('branches', {}).get('pct', 0)
if line_coverage < 80:
findings.append({
'severity': 'high',
'category': 'testing',
'title': f'Test coverage below 80% ({line_coverage:.1f}%)',
'current_state': f'Line coverage: {line_coverage:.1f}%, Branch coverage: {branch_coverage:.1f}%',
'target_state': 'Maintain 80%+ test coverage on all code',
'migration_steps': [
'Identify untested files and functions',
'Prioritize testing critical paths (authentication, payment, data processing)',
'Write integration tests first (70% of tests)',
'Add unit tests for complex business logic',
'Configure coverage thresholds in test config'
],
'effort': 'high',
})
elif line_coverage < 90:
findings.append({
'severity': 'medium',
'category': 'testing',
'title': f'Test coverage at {line_coverage:.1f}%',
'current_state': f'Coverage is good but could be excellent (current: {line_coverage:.1f}%)',
'target_state': 'Aim for 90%+ coverage for production-ready code',
'migration_steps': [
'Identify remaining untested code paths',
'Focus on edge cases and error handling',
'Ensure all critical features have 100% coverage'
],
'effort': 'medium',
})
except:
pass
else:
findings.append({
'severity': 'high',
'category': 'testing',
'title': 'No coverage report found',
'current_state': 'Cannot find coverage/coverage-summary.json',
'target_state': 'Generate coverage reports to track test coverage',
'migration_steps': [
'Configure coverage in vitest.config.ts or jest.config.js',
'Add --coverage flag to test script',
'Set coverage thresholds (lines: 80, branches: 75)',
'Add coverage/ to .gitignore',
'Review coverage reports regularly'
],
'effort': 'low',
})
return findings
def analyze_test_distribution(codebase_path: Path) -> List[Dict]:
"""Analyze testing trophy distribution."""
findings = []
# Count test files by type
unit_tests = 0
integration_tests = 0
e2e_tests = 0
test_patterns = {
'e2e': ['e2e/', '.e2e.test.', '.e2e.spec.', 'playwright/', 'cypress/'],
'integration': ['.test.tsx', '.test.jsx', '.spec.tsx', '.spec.jsx'], # Component tests
'unit': ['.test.ts', '.test.js', '.spec.ts', '.spec.js'], # Logic tests
}
for test_file in codebase_path.rglob('*.{test,spec}.{ts,tsx,js,jsx}'):
test_path_str = str(test_file)
# E2E tests
if any(pattern in test_path_str for pattern in test_patterns['e2e']):
e2e_tests += 1
# Integration tests (component tests with TSX/JSX)
elif any(pattern in test_path_str for pattern in test_patterns['integration']):
integration_tests += 1
# Unit tests (pure logic, no JSX)
else:
unit_tests += 1
total_tests = unit_tests + integration_tests + e2e_tests
if total_tests > 0:
int_pct = (integration_tests / total_tests) * 100
unit_pct = (unit_tests / total_tests) * 100
e2e_pct = (e2e_tests / total_tests) * 100
# Testing Trophy: 70% integration, 20% unit, 10% E2E
if int_pct < 50: # Should be ~70%
findings.append({
'severity': 'medium',
'category': 'testing',
'title': 'Testing pyramid instead of testing trophy',
'current_state': f'Distribution: {int_pct:.0f}% integration, {unit_pct:.0f}% unit, {e2e_pct:.0f}% E2E',
'target_state': 'Testing Trophy: 70% integration, 20% unit, 10% E2E',
'migration_steps': [
'Write more integration tests (component + hooks + context)',
'Test user workflows, not implementation details',
'Reduce excessive unit tests of simple functions',
'Keep E2E tests for critical user journeys only',
'Use Testing Library for integration tests'
],
'effort': 'medium',
})
if unit_pct > 40: # Should be ~20%
findings.append({
'severity': 'low',
'category': 'testing',
'title': 'Too many unit tests',
'current_state': f'{unit_pct:.0f}% unit tests (target: ~20%)',
'target_state': 'Focus on integration tests that provide more confidence',
'migration_steps': [
'Review unit tests - many could be integration tests',
'Combine related unit tests into integration tests',
'Keep unit tests only for complex business logic',
'Test components with their hooks and context'
],
'effort': 'low',
})
return findings
def check_test_quality(codebase_path: Path) -> List[Dict]:
"""Check for test quality anti-patterns."""
findings = []
brittle_test_patterns = []
bad_query_usage = []
bad_naming = []
for test_file in codebase_path.rglob('*.{test,spec}.{ts,tsx,js,jsx}'):
try:
with open(test_file, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# Check for brittle tests (testing implementation)
if 'getByTestId' in content:
bad_query_usage.append(str(test_file))
# Check for testing exact counts (brittle)
if re.search(r'expect\([^)]+\)\.toHaveLength\(\d+\)', content):
brittle_test_patterns.append(str(test_file))
# Check test naming ("should X when Y")
test_names = re.findall(r'(?:it|test)\s*\(\s*[\'"]([^\'"]+)[\'"]', content)
for name in test_names:
if not (name.startswith('should ') or 'when' in name.lower()):
bad_naming.append((str(test_file), name))
except:
pass
if bad_query_usage:
findings.append({
'severity': 'medium',
'category': 'testing',
'title': f'Using getByTestId in {len(bad_query_usage)} test files',
'current_state': 'Tests use getByTestId instead of semantic queries',
'target_state': 'Use semantic queries: getByRole, getByLabelText, getByText',
'migration_steps': [
'Replace getByTestId with getByRole (most preferred)',
'Use getByLabelText for form inputs',
'Use getByText for user-visible content',
'Only use getByTestId as last resort',
'Add eslint-plugin-testing-library for enforcement'
],
'effort': 'medium',
'affected_files': bad_query_usage[:5],
})
if brittle_test_patterns:
findings.append({
'severity': 'low',
'category': 'testing',
'title': f'Brittle test patterns in {len(brittle_test_patterns)} files',
'current_state': 'Tests check exact counts and DOM structure',
'target_state': 'Test user behavior and outcomes, not exact DOM structure',
'migration_steps': [
'Avoid testing exact element counts',
'Focus on user-visible behavior',
'Test functionality, not implementation',
'Allow flexibility in DOM structure'
],
'effort': 'low',
})
if len(bad_naming) > 5: # More than 5 tests with poor naming
findings.append({
'severity': 'low',
'category': 'testing',
'title': f'{len(bad_naming)} tests with unclear naming',
'current_state': 'Test names don\'t follow "should X when Y" pattern',
'target_state': 'Use descriptive names: "should display error when API fails"',
'migration_steps': [
'Rename tests to describe expected behavior',
'Use pattern: "should [expected behavior] when [condition]"',
'Make tests self-documenting',
'Tests should read like requirements'
],
'effort': 'low',
})
return findings

View File

@@ -0,0 +1,503 @@
#!/usr/bin/env python3
"""
Bulletproof React Audit Engine
Orchestrates comprehensive React/TypeScript codebase analysis against Bulletproof
React architecture principles. Generates detailed audit reports and migration plans.
Usage:
python audit_engine.py /path/to/react-app --output report.md
python audit_engine.py /path/to/react-app --format json --output report.json
python audit_engine.py /path/to/react-app --migration-plan --output migration.md
"""
import argparse
import json
import sys
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional
import importlib.util
# Bulletproof React specific analyzers
ANALYZERS = {
'structure': 'analyzers.project_structure',
'components': 'analyzers.component_architecture',
'state': 'analyzers.state_management',
'api': 'analyzers.api_layer',
'testing': 'analyzers.testing_strategy',
'styling': 'analyzers.styling_patterns',
'errors': 'analyzers.error_handling',
'performance': 'analyzers.performance_patterns',
'security': 'analyzers.security_practices',
'standards': 'analyzers.standards_compliance',
}
class BulletproofAuditEngine:
"""
Core audit engine for Bulletproof React compliance analysis.
Uses progressive disclosure: loads only necessary analyzers based on scope.
"""
def __init__(self, codebase_path: Path, scope: Optional[List[str]] = None):
"""
Initialize Bulletproof React audit engine.
Args:
codebase_path: Path to the React codebase to audit
scope: Optional list of analysis categories to run
If None, runs all analyzers.
"""
self.codebase_path = Path(codebase_path).resolve()
self.scope = scope or list(ANALYZERS.keys())
self.findings: Dict[str, List[Dict]] = {}
self.metadata: Dict = {}
if not self.codebase_path.exists():
raise FileNotFoundError(f"Codebase path does not exist: {self.codebase_path}")
def discover_project(self) -> Dict:
"""
Phase 1: Initial React project discovery (lightweight scan).
Returns:
Dictionary containing React project metadata
"""
print("🔍 Phase 1: Discovering React project structure...")
metadata = {
'path': str(self.codebase_path),
'scan_time': datetime.now().isoformat(),
'is_react': self._detect_react(),
'tech_stack': self._detect_tech_stack(),
'structure_type': self._detect_structure_type(),
'total_files': self._count_files(),
'total_lines': self._count_lines(),
'git_info': self._get_git_info(),
}
if not metadata['is_react']:
print("⚠️ Warning: This does not appear to be a React project!")
print(" Bulletproof React audit is designed for React applications.")
self.metadata = metadata
return metadata
def _detect_react(self) -> bool:
"""Check if this is a React project."""
pkg_json = self.codebase_path / 'package.json'
if not pkg_json.exists():
return False
try:
with open(pkg_json, 'r') as f:
pkg = json.load(f)
deps = {**pkg.get('dependencies', {}), **pkg.get('devDependencies', {})}
return 'react' in deps or 'react-dom' in deps
except:
return False
def _detect_tech_stack(self) -> Dict[str, bool]:
"""Detect React ecosystem tools and libraries."""
pkg_json = self.codebase_path / 'package.json'
tech_stack = {}
if pkg_json.exists():
try:
with open(pkg_json, 'r') as f:
pkg = json.load(f)
deps = {**pkg.get('dependencies', {}), **pkg.get('devDependencies', {})}
# Core
tech_stack['react'] = 'react' in deps
tech_stack['typescript'] = 'typescript' in deps or (self.codebase_path / 'tsconfig.json').exists()
# Build tools
tech_stack['vite'] = 'vite' in deps
tech_stack['create-react-app'] = 'react-scripts' in deps
tech_stack['next'] = 'next' in deps
# State management
tech_stack['redux'] = 'redux' in deps or '@reduxjs/toolkit' in deps
tech_stack['zustand'] = 'zustand' in deps
tech_stack['jotai'] = 'jotai' in deps
tech_stack['mobx'] = 'mobx' in deps
# Data fetching
tech_stack['react-query'] = '@tanstack/react-query' in deps or 'react-query' in deps
tech_stack['swr'] = 'swr' in deps
tech_stack['apollo'] = '@apollo/client' in deps
tech_stack['rtk-query'] = '@reduxjs/toolkit' in deps
# Forms
tech_stack['react-hook-form'] = 'react-hook-form' in deps
tech_stack['formik'] = 'formik' in deps
# Styling
tech_stack['tailwind'] = 'tailwindcss' in deps or (self.codebase_path / 'tailwind.config.js').exists()
tech_stack['styled-components'] = 'styled-components' in deps
tech_stack['emotion'] = '@emotion/react' in deps
tech_stack['chakra-ui'] = '@chakra-ui/react' in deps
tech_stack['mui'] = '@mui/material' in deps
tech_stack['radix-ui'] = any('@radix-ui' in dep for dep in deps.keys())
# Testing
tech_stack['vitest'] = 'vitest' in deps
tech_stack['jest'] = 'jest' in deps
tech_stack['testing-library'] = '@testing-library/react' in deps
tech_stack['playwright'] = '@playwright/test' in deps
tech_stack['cypress'] = 'cypress' in deps
# Routing
tech_stack['react-router'] = 'react-router-dom' in deps
# Error tracking
tech_stack['sentry'] = '@sentry/react' in deps
# Code quality
tech_stack['eslint'] = 'eslint' in deps
tech_stack['prettier'] = 'prettier' in deps
tech_stack['husky'] = 'husky' in deps
except:
pass
return {k: v for k, v in tech_stack.items() if v}
def _detect_structure_type(self) -> str:
"""Determine project structure pattern (feature-based vs flat)."""
src_dir = self.codebase_path / 'src'
if not src_dir.exists():
return 'no_src_directory'
features_dir = src_dir / 'features'
components_dir = src_dir / 'components'
app_dir = src_dir / 'app'
# Count files in different locations
features_files = len(list(features_dir.rglob('*.{js,jsx,ts,tsx}'))) if features_dir.exists() else 0
components_files = len(list(components_dir.rglob('*.{js,jsx,ts,tsx}'))) if components_dir.exists() else 0
if features_dir.exists() and app_dir.exists():
if features_files > components_files * 2:
return 'feature_based'
else:
return 'mixed'
elif features_dir.exists():
return 'partial_feature_based'
else:
return 'flat'
def _count_files(self) -> int:
"""Count total files in React codebase."""
exclude_dirs = {'.git', 'node_modules', 'dist', 'build', '.next', 'out', 'coverage'}
count = 0
for path in self.codebase_path.rglob('*'):
if path.is_file() and not any(excluded in path.parts for excluded in exclude_dirs):
count += 1
return count
def _count_lines(self) -> int:
"""Count total lines of code in React files."""
exclude_dirs = {'.git', 'node_modules', 'dist', 'build', '.next', 'out', 'coverage'}
code_extensions = {'.js', '.jsx', '.ts', '.tsx'}
total_lines = 0
for path in self.codebase_path.rglob('*'):
if (path.is_file() and
path.suffix in code_extensions and
not any(excluded in path.parts for excluded in exclude_dirs)):
try:
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
total_lines += sum(1 for line in f if line.strip() and not line.strip().startswith(('//', '#', '/*', '*')))
except:
pass
return total_lines
def _get_git_info(self) -> Optional[Dict]:
"""Get git repository information."""
git_dir = self.codebase_path / '.git'
if not git_dir.exists():
return None
try:
import subprocess
result = subprocess.run(
['git', '-C', str(self.codebase_path), 'log', '--oneline', '-10'],
capture_output=True,
text=True,
timeout=5
)
commit_count = subprocess.run(
['git', '-C', str(self.codebase_path), 'rev-list', '--count', 'HEAD'],
capture_output=True,
text=True,
timeout=5
)
return {
'is_git_repo': True,
'recent_commits': result.stdout.strip().split('\n') if result.returncode == 0 else [],
'total_commits': int(commit_count.stdout.strip()) if commit_count.returncode == 0 else 0,
}
except:
return {'is_git_repo': True, 'error': 'Could not read git info'}
def run_analysis(self, phase: str = 'full') -> Dict:
"""
Phase 2: Deep Bulletproof React analysis using specialized analyzers.
Args:
phase: 'quick' for lightweight scan, 'full' for comprehensive analysis
Returns:
Dictionary containing all findings
"""
print(f"🔬 Phase 2: Running {phase} Bulletproof React analysis...")
for category in self.scope:
if category not in ANALYZERS:
print(f"⚠️ Unknown analyzer category: {category}, skipping...")
continue
print(f" Analyzing {category}...")
analyzer_findings = self._run_analyzer(category)
if analyzer_findings:
self.findings[category] = analyzer_findings
return self.findings
def _run_analyzer(self, category: str) -> List[Dict]:
"""
Run a specific Bulletproof React analyzer module.
Args:
category: Analyzer category name
Returns:
List of findings from the analyzer
"""
module_path = ANALYZERS.get(category)
if not module_path:
return []
try:
# Import analyzer module dynamically
analyzer_file = Path(__file__).parent / f"{module_path.replace('.', '/')}.py"
if not analyzer_file.exists():
print(f" ⚠️ Analyzer not yet implemented: {category}")
return []
spec = importlib.util.spec_from_file_location(module_path, analyzer_file)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# Each analyzer should have an analyze() function
if hasattr(module, 'analyze'):
return module.analyze(self.codebase_path, self.metadata)
else:
print(f" ⚠️ Analyzer missing analyze() function: {category}")
return []
except Exception as e:
print(f" ❌ Error running analyzer {category}: {e}")
return []
def calculate_scores(self) -> Dict[str, float]:
"""
Calculate Bulletproof React compliance scores for each category.
Returns:
Dictionary of scores (0-100 scale)
"""
scores = {}
# Calculate score for each category based on findings severity
for category, findings in self.findings.items():
if not findings:
scores[category] = 100.0
continue
# Weighted scoring based on severity
severity_weights = {'critical': 15, 'high': 8, 'medium': 3, 'low': 1}
total_weight = sum(severity_weights.get(f.get('severity', 'low'), 1) for f in findings)
# Score decreases based on weighted issues
penalty = min(total_weight * 2, 100) # Each point = 2% penalty
scores[category] = max(0, 100 - penalty)
# Overall score is weighted average
if scores:
scores['overall'] = sum(scores.values()) / len(scores)
else:
scores['overall'] = 100.0
return scores
def calculate_grade(self, score: float) -> str:
"""Convert score to letter grade."""
if score >= 90: return 'A'
if score >= 80: return 'B'
if score >= 70: return 'C'
if score >= 60: return 'D'
return 'F'
def generate_summary(self) -> Dict:
"""
Generate executive summary of Bulletproof React audit results.
Returns:
Summary dictionary
"""
critical_count = sum(
1 for findings in self.findings.values()
for f in findings
if f.get('severity') == 'critical'
)
high_count = sum(
1 for findings in self.findings.values()
for f in findings
if f.get('severity') == 'high'
)
scores = self.calculate_scores()
overall_score = scores.get('overall', 0)
# Estimate migration effort in person-days
effort_map = {'low': 0.5, 'medium': 2, 'high': 5}
total_effort = sum(
effort_map.get(f.get('effort', 'medium'), 2)
for findings in self.findings.values()
for f in findings
)
return {
'compliance_score': round(overall_score, 1),
'grade': self.calculate_grade(overall_score),
'category_scores': {k: round(v, 1) for k, v in scores.items() if k != 'overall'},
'critical_issues': critical_count,
'high_issues': high_count,
'total_issues': sum(len(findings) for findings in self.findings.values()),
'migration_effort_days': round(total_effort, 1),
'structure_type': self.metadata.get('structure_type', 'unknown'),
'metadata': self.metadata,
}
def main():
"""Main entry point for CLI usage."""
parser = argparse.ArgumentParser(
description='Bulletproof React audit tool for React/TypeScript applications',
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
'codebase',
type=str,
help='Path to the React codebase to audit'
)
parser.add_argument(
'--scope',
type=str,
help='Comma-separated list of analysis categories (structure,components,state,api,testing,styling,errors,performance,security,standards)',
default=None
)
parser.add_argument(
'--phase',
type=str,
choices=['quick', 'full'],
default='full',
help='Analysis depth: quick (Phase 1 only) or full (Phase 1 + 2)'
)
parser.add_argument(
'--format',
type=str,
choices=['markdown', 'json', 'html'],
default='markdown',
help='Output format for the report'
)
parser.add_argument(
'--output',
type=str,
help='Output file path (default: stdout)',
default=None
)
parser.add_argument(
'--migration-plan',
action='store_true',
help='Generate migration plan in addition to audit report'
)
args = parser.parse_args()
# Parse scope
scope = args.scope.split(',') if args.scope else None
# Initialize engine
try:
engine = BulletproofAuditEngine(args.codebase, scope=scope)
except FileNotFoundError as e:
print(f"❌ Error: {e}", file=sys.stderr)
sys.exit(1)
# Run audit
print("🚀 Starting Bulletproof React audit...")
print(f" Codebase: {args.codebase}")
print(f" Scope: {scope or 'all'}")
print(f" Phase: {args.phase}")
print()
# Phase 1: Discovery
metadata = engine.discover_project()
if metadata['is_react']:
print(f" React detected: ✅")
print(f" TypeScript: {'' if metadata['tech_stack'].get('typescript') else ''}")
print(f" Structure type: {metadata['structure_type']}")
print(f" Files: {metadata['total_files']}")
print(f" Lines of code: {metadata['total_lines']:,}")
else:
print(f" React detected: ❌")
print(" Continuing audit anyway...")
print()
# Phase 2: Analysis (if not quick mode)
if args.phase == 'full':
findings = engine.run_analysis()
# Generate summary
summary = engine.generate_summary()
# Output results
print()
print("📊 Bulletproof React Audit Complete!")
print(f" Compliance score: {summary['compliance_score']}/100 (Grade: {summary['grade']})")
print(f" Critical issues: {summary['critical_issues']}")
print(f" High issues: {summary['high_issues']}")
print(f" Total issues: {summary['total_issues']}")
print(f" Estimated migration effort: {summary['migration_effort_days']} person-days")
print()
# Generate report (to be implemented in report_generator.py)
if args.output:
print(f"📝 Report generation will be implemented in report_generator.py")
print(f" Format: {args.format}")
print(f" Output: {args.output}")
if args.migration_plan:
print(f" Migration plan: {args.output.replace('.md', '_migration.md')}")
if __name__ == '__main__':
main()