Files
2025-11-29 18:02:45 +08:00

444 lines
13 KiB
Markdown

# Linter-Driven Development Reference (TypeScript + React)
## Overview
The linter-driven development workflow ensures code quality through automated tooling and design validation. This orchestrator manages the complete lifecycle from design to commit-ready code.
## Quality Tool Stack
### 1. TypeScript Compiler (`tsc`)
**Command**: `npm run typecheck`
**Purpose**: Type safety validation
**Can auto-fix**: No
**Failure resolution**: Manual type fixes or refactoring
### 2. ESLint
**Command**:
- Check: `npm run lintcheck`
- Fix: `npm run lint`
**Purpose**: Code quality, style, and complexity analysis
**Plugins used**:
- `eslint-plugin-sonarjs` - Complexity metrics (THE KEY PLUGIN)
- `typescript-eslint` - TypeScript-aware linting
- `eslint-plugin-react` - React best practices
- `eslint-plugin-react-hooks` - Hooks rules enforcement
- `eslint-plugin-jsx-a11y` - Accessibility rules
- `eslint-plugin-import` - Import/export management
- `eslint-plugin-unused-imports` - Remove dead code
- `eslint-plugin-simple-import-sort` - Auto-sort imports
- `eslint-plugin-promise` - Async/await best practices
- `eslint-plugin-security` - Security vulnerabilities
**Can auto-fix**: Many rules (formatting, imports, simple violations)
**Requires refactoring**: Complexity rules, design issues
### 3. Prettier
**Command**:
- Check: `npm run formatcheck`
- Fix: `npm run format`
**Purpose**: Code formatting consistency
**Can auto-fix**: Always (100% auto-fixable)
### 4. Stylelint
**Command**:
- Check: `npm run stylecheck`
- Fix: `npm run stylefix`
**Purpose**: SCSS/CSS linting
**Can auto-fix**: Most rules
## Workflow Phases in Detail
### Phase 1: Design
**Trigger**: New components, custom hooks, major architectural changes
**Actions**:
1. Invoke @component-designing skill
2. Answer design questions:
- Component composition strategy?
- State management approach?
- Custom hooks needed?
- Type definitions required?
3. Receive design plan with:
- Component structure
- Props interfaces
- Custom hooks
- Type definitions
- File organization
**Output**: Design document ready for implementation
### Phase 2: Implementation + Testing
**Testing principles** (from @testing skill):
- Write tests for public API only
- Use React Testing Library patterns
- Test user behavior, not implementation
- Use MSW for API mocking (real HTTP)
- Avoid `waitFor` with arbitrary delays
- Achieve 100% coverage on leaf components/hooks (no dependencies on other types)
- Integration tests for orchestrating components (test interactions and composition)
**Implementation approach**:
- Parallel development (test + code together)
- Focus on behavior validation
- Use real implementations over mocks
- Follow component composition patterns
- Push business logic into leaf types for better testability
### Phase 3: Linter Loop
This is the core quality gate with multiple sub-checks:
#### Step 1: Type Checking
```bash
npm run typecheck
```
**Checks**: TypeScript compilation, type safety
**Failures**: Type errors, missing types, type mismatches
**Resolution**:
- Fix types manually
- Add type assertions where needed
- Use type guards for narrowing
- Cannot proceed if failing
#### Step 2: ESLint Check
```bash
npm run lintcheck
```
**Checks**: Code quality, complexity, React rules, hooks rules, a11y
**Failures**: See "ESLint Failure Categories" below
**Resolution**:
- Auto-fix: `npm run lint`
- If auto-fix insufficient → manual fixes or invoke @refactoring
#### Step 3: Prettier Check
```bash
npm run formatcheck
```
**Checks**: Code formatting consistency
**Failures**: Inconsistent formatting
**Resolution**: Always auto-fix with `npm run format`
#### Step 4: Stylelint Check
```bash
npm run stylecheck
```
**Checks**: SCSS/CSS quality and consistency
**Failures**: Style violations, naming issues
**Resolution**: Auto-fix with `npm run stylefix`, some manual fixes
#### Loop Behavior
```
Run all checks → Any fail? → Run auto-fixes → Re-run checks
Still failing?
Complexity/design issues?
Invoke @refactoring
Re-run checks
Loop until pass
```
### Phase 4: Pre-Commit Review (Advisory)
**Always invoked**, even if linter passes.
**Purpose**: Validate design principles that linters cannot enforce
**Scope**:
- **Primary**: All changed code in current commit
- **Secondary**: Broader file context (flags patterns for future refactoring)
**Categories**:
- 🔴 **Design Debt**: Will cause pain when extending code
- Primitive obsession (string IDs, unvalidated inputs)
- Prop drilling (state passed through 3+ levels)
- Tight coupling
- Missing error boundaries
- Non-self-validating types
- 🟡 **Readability Debt**: Hard to understand and work with
- Mixed abstraction levels
- Complex nested logic
- Inline styles or logic
- Poor naming
- Missing component extraction
- 🟢 **Polish Opportunities**: Minor improvements
- Missing JSDoc
- Accessibility enhancements
- Type refinements
- Better naming
**Output**: Advisory report with specific line references and fix suggestions
**User Decision Points**:
1. Commit as-is (accept debt)
2. Fix design debt (🔴) - recommended
3. Fix design + readability (🔴 + 🟡)
4. Fix all (🔴 🟡 🟢)
5. Expand scope (refactor related code)
### Phase 5: Commit Ready
**Checklist**:
- ✅ Type checking passes
- ✅ ESLint passes
- ✅ Prettier passes
- ✅ Stylelint passes
- ✅ Tests pass (100% coverage on leaf types, integration tests on orchestrating components)
- ✅ Design review complete (advisory)
**Output**:
- Commit readiness summary
- Suggested commit message
- List of modified/added files
- Coverage report
- Design review findings
- User decision prompt
## Coverage Targets
Follow @testing skill principles for coverage strategy:
- **Leaf types** (pure logic, no external dependencies): 100% unit test coverage
- **Orchestrating types** (coordinate pieces, call external systems): Integration tests
See **testing/reference.md** for:
- Detailed coverage targets explanation
- Examples of leaf vs orchestrating types
- Testing approach for each type
- Architectural benefits
## ESLint Failure Categories
### Category 1: Auto-Fixable (npm run lint)
These are fixed automatically:
- Unused imports (`unused-imports/no-unused-imports`)
- Import sorting (`simple-import-sort/imports`)
- Missing semicolons, quotes, spacing (handled by Prettier)
- Simple style violations
- Arrow function simplification (`arrow-body-style`)
**Action**: Run `npm run lint` → Re-run checks → Continue
### Category 2: Requires Manual Fix
These need developer intervention but are straightforward:
- `@typescript-eslint/no-explicit-any` - Replace any with proper types
- `no-console` - Remove or replace with proper logging
- `react/jsx-key` - Add key props to list items
- `react-hooks/exhaustive-deps` - Fix hook dependencies
- Type-related issues
**Action**: Fix issues manually → Re-run checks → Continue
### Category 3: Requires Refactoring (invoke @refactoring)
These indicate design or complexity problems:
- `sonarjs/cognitive-complexity` (max: 15)
- `sonarjs/cyclomatic-complexity` (max: 10)
- `sonarjs/expression-complexity` (max: 5)
- `sonarjs/max-lines-per-function` (max: 200)
- `sonarjs/max-lines` (max: 600)
- `sonarjs/nested-control-flow` (max: 4 levels)
- `react/no-unstable-nested-components` - Extract components
- `react/no-multi-comp` - Split into multiple files
**Action**: Invoke @refactoring skill → Apply patterns (storifying, extract hooks/functions, early returns, simplify conditionals) → Re-run checks → Continue
## Complexity Thresholds Explained
### Cognitive Complexity (max: 15)
**What it measures**: How difficult is it to understand the code?
**Increments for**: Nested structures, breaks in linear flow, recursion
**Why it matters**: High cognitive load → more bugs, harder maintenance
**How to fix**: Invoke @refactoring skill which applies **storifying** - making code read like a story at single abstraction level. See refactoring/reference.md for detailed techniques and examples.
**Example violation**:
```tsx
// Cognitive complexity: 18 (too high!)
function validateUser(user: User): ValidationResult {
if (user) { // +1
if (user.email) { // +2 (nested)
if (isValidEmail(user.email)) { // +3 (nested)
if (user.age >= 18) { // +4 (nested)
if (user.country === 'US') { // +5 (nested)
return { valid: true }
} else { // +1
return { valid: false, reason: 'Not in US' }
}
}
} else { // +1
return { valid: false, reason: 'Invalid email' }
}
}
}
return { valid: false, reason: 'Missing user' }
}
```
**How to fix**: Invoke @refactoring skill to storify with early returns (see refactoring/reference.md)
### Cyclomatic Complexity (max: 10)
**What it measures**: Number of independent paths through code
**Increments for**: if, else, case, &&, ||, while, for, catch
**Why it matters**: More paths → more test cases needed, higher bug risk
**Fix strategies**: Extract functions, use polymorphism, simplify conditionals
### Expression Complexity (max: 5)
**What it measures**: Number of operators in a single expression
**Increments for**: &&, ||, ternary operators
**Why it matters**: Hard to read, error-prone
**Example violation**:
```tsx
// Expression complexity: 6 (too high!)
const isValid = user && user.email && isValidEmail(user.email) && user.age >= 18 && user.country === 'US' && !user.banned
```
**Fix**: Extract to variables or validation function
### Storifying Pattern
When cognitive complexity is high, invoke @refactoring skill which applies storifying patterns (making code read like a story at single abstraction level).
See **refactoring/reference.md** for:
- Detailed storifying explanation with examples
- TypeScript and React examples
- When and how to apply storifying
- Step-by-step storifying process
## Integration with Other Skills
### @component-designing
**When invoked**: Phase 1, if new components/major changes needed
**Input**: Feature requirements
**Output**: Component structure, props, hooks, types
**Next step**: Proceed to Phase 2 implementation
### @testing
**When applied**: Phase 2, during implementation
**Input**: Component/hook to test
**Output**: Test files with React Testing Library
**Principles**: User-centric testing, real implementations
### @refactoring
**When invoked**: Phase 3, when linter fails on complexity
**Input**: Failing component/function + linter error
**Output**: Refactored code that passes linter
**Patterns**: Storifying, extract hooks/functions, simplify logic, early returns, single abstraction levels
### @pre-commit-review
**When invoked**: Phase 4, always (advisory)
**Input**: All changed code + file context
**Output**: Design review findings (🔴 🟡 🟢)
**Decision**: User chooses whether to apply fixes
### @documentation
**When invoked**: After commit (feature complete)
**Input**: Implemented feature
**Output**: Storybook stories, JSDoc, feature docs
**Purpose**: Documentation for humans and AI
## Command Alternatives
Different projects may use different naming conventions:
### Option 1: Separate check/fix commands
```bash
# Checks (validation only)
npm run typecheck
npm run lintcheck
npm run formatcheck
npm run stylecheck
# Fixes (auto-fix where possible)
npm run lint
npm run format
npm run stylefix
```
### Option 2: Combined commands
```bash
# Check all quality gates
npm run checkall # or npm run check
# Fix all auto-fixable issues
npm run fix
```
### Option 3: Task runner (if available)
```bash
# Using task runner (e.g., make, task)
task lint
task fix
```
**Plugin should detect** which commands are available from `package.json` scripts.
## Best Practices
### 1. Fail Fast, Fix Fast
- Run linter frequently during development
- Don't accumulate linter errors
- Fix auto-fixable issues immediately
### 2. Trust Complexity Metrics
- SonarJS complexity rules are calibrated well
- If it flags complexity → there's real complexity
- Refactor rather than disable
### 3. Respect Advisory Review
- Design debt compounds over time
- Fix 🔴 before committing when possible
- Track accepted debt in tickets
### 4. Test After Refactoring
- Complexity fixes can introduce bugs
- Re-run tests after @refactoring
- Verify behavior unchanged
### 5. Commit Granularly
- Small, focused commits
- Each commit passes all gates
- Easy to review and revert
## Troubleshooting
### "Type errors won't go away"
- TypeScript errors require manual fixes
- Consider if types are correct (not the code)
- Use type guards for narrowing
- Add type assertions as last resort
### "ESLint keeps failing after auto-fix"
- Auto-fix only handles simple rules
- Complexity rules need refactoring
- Invoke @refactoring skill
- May need architectural changes
### "Linter passes but review finds issues"
- Expected! Linters can't enforce design principles
- Review catches: primitive obsession, coupling, architecture
- User decides whether to fix
### "Too many findings in review"
- Common for legacy code
- Fix incrementally (design debt first)
- Consider broader refactor ticket
- Don't let perfect be enemy of good
## Related Files
- For design patterns: See @component-designing/reference.md
- For testing strategies: See @testing/reference.md
- For refactoring patterns: See @refactoring/reference.md
- For review principles: See @pre-commit-review/reference.md