13 KiB
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 lintingeslint-plugin-react- React best practiceseslint-plugin-react-hooks- Hooks rules enforcementeslint-plugin-jsx-a11y- Accessibility ruleseslint-plugin-import- Import/export managementeslint-plugin-unused-imports- Remove dead codeeslint-plugin-simple-import-sort- Auto-sort importseslint-plugin-promise- Async/await best practiceseslint-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:
- Invoke @component-designing skill
- Answer design questions:
- Component composition strategy?
- State management approach?
- Custom hooks needed?
- Type definitions required?
- 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
waitForwith 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
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
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
npm run formatcheck
Checks: Code formatting consistency
Failures: Inconsistent formatting
Resolution: Always auto-fix with npm run format
Step 4: Stylelint Check
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:
- Commit as-is (accept debt)
- Fix design debt (🔴) - recommended
- Fix design + readability (🔴 + 🟡)
- Fix all (🔴 🟡 🟢)
- 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 typesno-console- Remove or replace with proper loggingreact/jsx-key- Add key props to list itemsreact-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 componentsreact/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:
// 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:
// 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
# 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
# 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)
# 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