Files
metabuilder/docs/refactoring/reference/REFACTORING_QUICK_REFERENCE.md

485 lines
12 KiB
Markdown

# Refactoring Quick Reference Guide
## TL;DR Summary
**Problem:** 8 React components exceed 150 LOC limit (violates MetaBuilder architecture)
**Solution:** Decompose into smaller components, extract logic to hooks and Lua
**Timeline:** 3 weeks, 103 hours of work
**Impact:** Improved modularity, testability, and data persistence
| Violation | Current | Target | Reduction |
|-----------|---------|--------|-----------|
| GitHubActionsFetcher | 887 LOC | 95 LOC | 89% ↓ |
| NerdModeIDE | 733 LOC | 110 LOC | 85% ↓ |
| LuaEditor | 686 LOC | 120 LOC | 82% ↓ |
---
## Key Architectural Changes
### 1. Component Decomposition Pattern
**Before:**
```
MonolithicComponent.tsx (887 LOC)
- API fetching
- State management
- UI rendering
- Business logic
- Error handling
```
**After:**
```
MonolithicComponent.tsx (95 LOC)
├── SubComponent1.tsx (65 LOC) - Handle one thing
├── SubComponent2.tsx (80 LOC) - Handle one thing
├── SubComponent3.tsx (75 LOC) - Handle one thing
└── Hooks & Lua for business logic
```
### 2. State Management Shift
**Before:**
```typescript
const [data, setData] = useState(null)
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState(null)
// ... 8+ more useState
// Lost on page reload, no sharing
```
**After:**
```typescript
// Database (Prisma) - Persistent
model WorkflowRun {
id: String
name: String
status: String
// ... shared across sessions and users
}
// React State - Ephemeral UI state
const [selectedId, setSelectedId] = useState(null) // UI only
const { runs } = useWorkflowActions() // From database
```
### 3. Logic Extraction Pattern
**Before:**
```typescript
// In component, 200+ LOC of async operations
const fetchGitHubActions = async () => {
// Set loading, call API, handle errors, cache...
}
```
**After:**
```typescript
// Custom hook - 50 LOC, testable, reusable
export function useGitHubActions() {
// All API logic, caching, error handling
return { runs, isLoading, error, refetch }
}
// In component - 5 LOC
const { runs, isLoading } = useGitHubActions()
```
### 4. Configuration as Data
**Before:**
```typescript
const getStatusColor = (status: string) => {
if (status === 'completed') {
if (conclusion === 'success') return 'text-green-600'
// ... 50 lines of hardcoded logic
}
}
```
**After (JSON Config):**
```json
{
"statusColors": {
"completed-success": "text-green-600",
"completed-failure": "text-red-600"
}
}
```
**Usage:**
```typescript
const config = useUIConfig('GitHubActions')
const color = config.statusColors[status]
```
---
## Implementation Roadmap
### Week 1: Violation #1 (GitHubActionsFetcher)
```
Day 1-2: Database models + migrations
Day 3-4: useGitHubActions hook
Day 5: useWorkflowLogs + useWorkflowAnalysis hooks
Day 6: Sub-components creation
Day 7: Main component refactor + tests
```
### Week 2: Violation #2 (NerdModeIDE)
```
Day 1: Database models (projects, files, git config)
Day 2-4: useIDEFileTree (3 hours) + other hooks
Day 5: File tree components
Day 6: Editor + toolbar components
Day 7: Main component refactor + tests
```
### Week 3: Violation #3 (LuaEditor)
```
Day 1-2: Extract Monaco config + create hooks
Day 3-4: Parameter + execution components
Day 5: Security + snippet components
Day 6: Main component refactor
Day 7: Testing + documentation
```
---
## File Structure After Refactoring
```
src/
├── components/
│ ├── GitHubActionsFetcher.tsx (95 LOC) ← Refactored
│ │ ├── GitHubActionsToolbar.tsx (65 LOC)
│ │ ├── GitHubActionsRunsList.tsx (80 LOC)
│ │ ├── GitHubActionsRunRow.tsx (45 LOC)
│ │ ├── GitHubActionsJobsPanel.tsx (100 LOC)
│ │ ├── GitHubActionsJobStep.tsx (50 LOC)
│ │ └── GitHubActionsAnalysisPanel.tsx (70 LOC)
│ │
│ ├── NerdModeIDE.tsx (110 LOC) ← Refactored
│ │ ├── IDEFileTreePanel.tsx (90 LOC)
│ │ ├── IDEFileTreeNode.tsx (60 LOC)
│ │ ├── IDEEditorPanel.tsx (85 LOC)
│ │ ├── IDEToolbar.tsx (65 LOC)
│ │ ├── IDEConsolePanel.tsx (70 LOC)
│ │ ├── IDEGitDialog.tsx (75 LOC)
│ │ └── IDENewFileDialog.tsx (65 LOC)
│ │
│ ├── LuaEditor.tsx (120 LOC) ← Refactored
│ │ ├── LuaScriptSelector.tsx (70 LOC)
│ │ ├── LuaCodeEditor.tsx (110 LOC)
│ │ ├── LuaMonacoConfig.tsx (120 LOC)
│ │ ├── LuaParametersPanel.tsx (95 LOC)
│ │ ├── LuaParameterItem.tsx (60 LOC)
│ │ ├── LuaExecutionPanel.tsx (100 LOC)
│ │ ├── LuaExecutionResult.tsx (80 LOC)
│ │ ├── LuaSecurityWarningDialog.tsx (75 LOC)
│ │ └── LuaSnippetLibraryPanel.tsx (85 LOC)
│ │
│ └── (existing components...)
├── hooks/
│ ├── useGitHubActions.ts (60 LOC)
│ ├── useWorkflowLogs.ts (50 LOC)
│ ├── useWorkflowAnalysis.ts (40 LOC)
│ │
│ ├── useIDEProject.ts (40 LOC)
│ ├── useIDEFileTree.ts (100 LOC)
│ ├── useIDEEditor.ts (40 LOC)
│ ├── useIDEGit.ts (60 LOC)
│ ├── useIDETest.ts (50 LOC)
│ │
│ ├── useLuaScripts.ts (40 LOC)
│ ├── useLuaEditor.ts (40 LOC)
│ ├── useLuaExecution.ts (60 LOC)
│ ├── useLuaSecurity.ts (30 LOC)
│ │
│ └── (existing hooks...)
├── lib/
│ ├── github-ui-config.ts (30 LOC)
│ └── (existing libs...)
└── seed-data/
└── lua-scripts/
├── filter-workflow-runs.lua
├── aggregate-logs.lua
└── (more scripts...)
```
---
## Database Schema Summary
### New Models (15 total)
**GitHub Feature:**
- `GitHubWorkflowRun` - Workflow execution data
- `GitHubWorkflowJob` - Job details
- `GitHubConfig` - API config
**IDE Feature:**
- `IDEProject` - Project metadata
- `IDEFile` - File tree with content
- `GitConfig` - Git credentials (update)
- `IDETestRun` - Test results
**Lua Feature:**
- `LuaScriptLibrary` - Reusable snippets
- `LuaScriptParameter` - Parameter definitions
- `LuaTestRun` - Execution history
**Updated Models:**
- `LuaScript` - Add description field
- `User` - Track changes
---
## Hook API Patterns
### Pattern 1: Data Fetching Hook
```typescript
export function useGitHubActions() {
const [runs, setRuns] = useState<WorkflowRun[]>([])
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const refetch = useCallback(async () => {
setIsLoading(true)
try {
const data = await fetchFromAPI()
setRuns(data)
} catch (e) {
setError(e.message)
} finally {
setIsLoading(false)
}
}, [])
useEffect(() => {
refetch()
}, [])
return { runs, isLoading, error, refetch }
}
```
### Pattern 2: State Management Hook
```typescript
export function useIDEFileTree(projectId: string) {
const [files, setFiles] = useState<IDEFile[]>([])
const createFile = useCallback(async (parentId: string, name: string) => {
const newFile = await db.file.create({ projectId, parentId, name })
setFiles([...files, newFile])
}, [files])
return { files, createFile, deleteFile, updateFile }
}
```
### Pattern 3: Derived State Hook
```typescript
export function useLuaEditor(scripts: LuaScript[]) {
const [selectedId, setSelectedId] = useState<string | null>(
scripts.length > 0 ? scripts[0].id : null
)
const currentScript = useMemo(
() => scripts.find(s => s.id === selectedId) || null,
[scripts, selectedId]
)
return { selectedId, setSelectedId, currentScript }
}
```
---
## Testing Strategy
### Unit Test Template
```typescript
describe('useGitHubActions', () => {
it('should fetch and cache workflow runs', async () => {
const { result } = renderHook(() => useGitHubActions(), {
wrapper: MockProviders
})
expect(result.current.isLoading).toBe(true)
await waitFor(() => {
expect(result.current.runs.length).toBeGreaterThan(0)
expect(result.current.isLoading).toBe(false)
})
})
})
```
### Component Test Template
```typescript
describe('GitHubActionsRunRow', () => {
it('should render workflow run data', () => {
const mockRun = { id: 1, name: 'Test', status: 'completed' }
render(
<GitHubActionsRunRow
run={mockRun}
isSelected={false}
onSelect={jest.fn()}
/>
)
expect(screen.getByText('Test')).toBeInTheDocument()
})
})
```
---
## Migration Path (No Breaking Changes)
### Step 1: Create New Infrastructure
```bash
1. Add new Prisma models
2. Create hooks (side-by-side with old code)
3. Create new sub-components
4. Create wrapper for backward compatibility
```
### Step 2: Gradual Adoption
```bash
1. Tests use new hooks/components
2. Documentation shows new patterns
3. New features use new patterns
4. Old code still works
```
### Step 3: Cleanup (Optional)
```bash
1. Remove old component after 1 release
2. Move any users to new version
3. Clean up deprecated code
```
---
## Common Pitfalls to Avoid
### ❌ Don't: Props Drilling
```typescript
// BAD - passing through 5 levels
<Component1 data={data} onChange={onChange}>
<Component2 data={data} onChange={onChange}>
<Component3 data={data} onChange={onChange}>
<Component4 data={data} onChange={onChange}>
<Component5 data={data} onChange={onChange} />
```
### ✅ Do: Use Context or Hooks
```typescript
// GOOD - hook provides data
const { data, onChange } = useWorkflowContext()
<Component5 />
```
### ❌ Don't: Mix Data and UI
```typescript
// BAD - API calls in component
export function MyComponent() {
const [data, setData] = useState(null)
useEffect(() => {
fetch('/api/data').then(setData) // Should be in hook!
}, [])
}
```
### ✅ Do: Separate Concerns
```typescript
// GOOD - hook handles data
export function useMyData() {
const [data, setData] = useState(null)
useEffect(() => {
fetch('/api/data').then(setData)
}, [])
return data
}
export function MyComponent() {
const data = useMyData()
}
```
### ❌ Don't: Hardcode UI Configuration
```typescript
// BAD - colors hardcoded
const statusColors = {
success: '#16a34a',
failure: '#dc2626'
}
```
### ✅ Do: Load from Database
```typescript
// GOOD - config in database
const config = useUIConfig('GitHubActions')
const color = config.statusColors[status]
```
---
## Success Metrics
### Before Refactoring
- Average component size: 569 LOC
- Components > 150 LOC: 8
- Custom hooks: ~5
- Lua scripts: ~10
### After Refactoring
- Average component size: 73 LOC
- Components > 150 LOC: 0 ✅
- Custom hooks: ~20 ✅
- Lua scripts: ~15 ✅
---
## Next Steps
1. **Review** this strategy with team
2. **Adjust** timeline based on capacity
3. **Start** with Phase 1 (GitHubActionsFetcher)
4. **Track** progress with checklist
5. **Celebrate** each milestone
6. **Repeat** for Violations #2 and #3
---
## Questions to Review
- [ ] Database schema approved?
- [ ] Hook API contracts clear?
- [ ] Component responsibility boundaries defined?
- [ ] Testing approach agreed?
- [ ] Timeline realistic?
- [ ] Team capacity sufficient?
- [ ] Risk mitigation plans ready?
## Contact / Escalation
- **Questions about architecture?** → Review copilot-instructions.md
- **Database schema issues?** → Check prisma/schema.prisma
- **Hook design help?** → Look at existing hooks in src/hooks/
- **Component composition questions?** → Review /packages/ structure
---
**Document Version:** 1.0
**Last Updated:** 2025-12-25
**Status:** Ready for implementation
**Approval:** Pending ⏳