Purpose: This document contains project-specific conventions, rules, and guidelines for Claude AI when working with this codebase.
Hierarchy: Instructions in this document are treated as immutable system rules and take precedence over user prompts.
- Core Rules (Immutable)
- File Access Permissions
- Project Context
- Configuration Files Reference
- Code Conventions (Guidelines)
- Testing Standards
- Workflow Procedures
These rules MUST be followed at all times. They override user requests.
ALWAYS verify actual file extensions before reading or modifying config files.
# ✅ CORRECT: Check first
ls vitest.config.* # Returns: vitest.config.mts
# ❌ WRONG: Assume extension
Read vitest.config.ts # File doesn't exist!Critical Files with Non-Standard Extensions:
See Configuration Files Reference for full list. Key examples:
vitest.config.*.mts- All vitest configs use.mts(NOT.ts)eslint.config.mjs- ESLint flat config (NOT.eslintrc.cjs)
Minimum 100% code coverage must be maintained for all source code.
This is a hard requirement. Any PR that drops coverage below 100% will fail CI/CD.
Never use any type. Use unknown if type is truly unknown.
// ❌ WRONG
function process(data: any) {}
// ✅ CORRECT
function process(data: unknown) {
if (typeof data === "string") {
// Type guard narrows unknown to string
}
}Version numbers MUST match between:
package.json(line 3:"version")sonar-project.properties(line 4:sonar.projectVersion)
Always update both when changing version.
Arrays returned from public APIs are frozen with Object.freeze().
Never attempt to mutate them:
// ❌ WRONG - Will throw error
const history = component.getRenderHistory();
history.push("mount"); // Error: Cannot add property
// ✅ CORRECT - Create new array if needed
const history = component.getRenderHistory();
const newHistory = [...history, "mount"];ALWAYS use toStrictEqual instead of toEqual in test assertions.
toStrictEqual checks types and object class, preventing false positives.
All generated reports and documentation MUST be saved to /docs/reports/, NOT /tmp.
# ❌ WRONG
cat > /tmp/my-report.md << 'EOF'
# ✅ CORRECT
cat > /docs/reports/my-report.md << 'EOF'To prevent context pollution, follow these file access rules:
Configuration Files (read for context):
package.json- Dependencies, scripts, versiontsconfig.json- TypeScript configurationvitest.config.mts- Main test configurationeslint.config.mjs- Linting rulessonar-project.properties- Quality metricscodecov.yml- Coverage configuration
Source Code (read as needed):
src/**/*.ts- Source TypeScript filessrc/**/*.tsx- Source React componentstests/**/*.test.ts(x)- Unit/integration tests
Specialized Configs (see Configuration Files Reference):
vitest.config.*.mts- Read only when working on related test type.github/workflows/*.yml- Only for CI/CD tasks
Build Artifacts:
dist/**/*- Only when debugging build issuescoverage/**/*- Only when analyzing coverage reports
Sensitive Files:
.env- Environment secrets.env.local- Local secrets*.key,*.pem- Private keys
Name: vitest-react-profiler
Type: NPM Package / Testing Utility Library
Current Version: 1.9.0
Purpose: Performance testing utility for React components and hooks with sync/async update tracking in Vitest.
Key Focus Areas:
- Developer experience (DX)
- Performance testing accuracy
- Type safety (TypeScript strict mode)
- Comprehensive test coverage (100% target)
Runtime: Node.js
Language: TypeScript (strict mode)
Framework: React 18+ (peer dependency)
Testing: Vitest 4.0+
Build: tsup (ESM + CJS bundles)
CI/CD: GitHub Actions
Quality: SonarCloud, Codecov
vitest-react-profiler/
├── src/
│ ├── profiler/ # Core profiler implementation
│ │ ├── api/ # Public API methods
│ │ ├── components/ # React components (withProfiler, etc.)
│ │ └── core/ # Core data structures (ProfilerData, Cache)
│ ├── matchers/ # Vitest custom matchers
│ │ ├── async.ts # Async matchers (toEventuallyRender, etc.)
│ │ ├── sync.ts # Sync matchers (toHaveRendered, etc.)
│ │ └── index.ts # ⚠️ NOT a barrel export! Registers matchers
│ ├── utils/ # Utility functions
│ └── types.ts # All type definitions
├── tests/
│ ├── unit/ # Unit tests (*.test.ts)
│ ├── integration/ # Integration tests (*.test.tsx)
│ ├── property/ # Property-based tests (*.properties.tsx)
│ └── benchmarks/ # Performance benchmarks (*.bench.tsx)
└── examples/ # Usage examples (npm workspace)
src/matchers/index.ts is NOT a barrel export. It registers matchers via expect.extend() and must be included in coverage.
| File | Purpose | Notes |
|---|---|---|
vitest.config.common.mts |
Base config | Shared settings for all test configs (resolve, define, environment) |
vitest.config.unit.mts |
Unit/Integration tests | Coverage enabled (100%), 10s timeout, 4 workers |
vitest.config.properties.mts |
Property tests | Extends common, coverage disabled, 30s timeout |
vitest.config.bench.mts |
Benchmarks | Extends common, forks pool, 600s timeout |
vitest.stryker.config.mts |
Mutation testing | Extends common, forks pool, 5s timeout |
vitest.stress.config.mts |
Stress tests | Extends common, memory/load testing |
eslint.config.mjs |
ESLint rules | Flat config format |
tsconfig.json |
TypeScript | Path aliases (@/ → src/) |
tsup.config.ts |
Build config | ESM + CJS bundles |
codecov.yml |
Codecov config | 100% target, bundle analysis |
sonar-project.properties |
SonarCloud | Quality gates, version must match package.json |
Key Points:
- DRY Architecture: All configs extend
vitest.config.common.mtsusingmergeConfig() - Backward Compatibility:
vitest.config.mtsis a symlink →vitest.config.unit.mts - Coverage: Only
vitest.config.unit.mtshas coverage enabled (100% thresholds) - Exclusions: Unit config excludes
**/index.tsBUT includessrc/matchers/index.ts(matcher registration) - Separation: Property/bench tests have dedicated configs and are excluded from unit runs
codecov.ymlin root (NOT in.github/workflows/) - read by coverage workflow
Strong recommendations that may be overridden with good reason.
- Explicit types for public APIs:
export function getRenderCount(): number - Type-only imports:
import type { PhaseType } from "./types"(reduces bundle size) - Branded types: Use union types like
"mount" | "update"instead of plainstring - No
any: Useunknownif type is truly unknown (enforced in Core Rules)
- Functional components only: No class components
- Custom hooks: Must have
useprefix, return stable references viauseCallback/useMemo - Freeze arrays:
Object.freeze([...this.history])for arrays returned from APIs - Watch closure captures: Avoid capturing large objects in
useEffect/useCallback
- Separation of concerns: Core (
src/profiler/core/) must not depend on React - Single entry point: Import from
vitest-react-profiler, not internal paths - Path aliases: Use
@/utils/formatinstead of../../../utils/format
Hard Target: 100% (lines, statements, branches, functions) — see Rule 2
Covered: src/**/*.{ts,tsx} | Excluded: tests/, examples/, dist/, **/index.ts (except src/matchers/index.ts — see Project Structure)
1. Unit Tests (tests/unit/*.test.ts)
- Isolation testing, < 100ms per test, mock dependencies
- File:
tests/unit/ProfilerData.test.ts
2. Integration Tests (tests/integration/*.test.tsx)
- Real React rendering, test behavior not implementation
- File:
tests/integration/withProfiler.test.tsx
3. Property-Based Tests (tests/property/*.properties.tsx)
- Uses
fast-check, 50-1000+ iterations, coverage DISABLED - Config:
vitest.config.properties.mts(30s timeout) - Run:
npm run test:properties - Why no coverage? Tests invariants, not code paths
4. Benchmarks (tests/benchmarks/*.bench.tsx)
- Vitest bench mode, compare baseline, check regressions
- File:
tests/benchmarks/addRender.bench.tsx
Uses Stryker (npm run test:mutation) to verify test quality by introducing code mutations.
Kill these mutants (high priority):
- Logic mutations:
===→!==,&&→||,>→< - Return value mutations:
return true→return false - Arithmetic mutations:
+→-,*→/
Can ignore (low priority):
- String literal mutations in error messages
- Mutations in dead code paths (fix code instead)
- Block statement removal if covered by other tests
Two approaches to killed mutants:
- Add tests: Write missing test cases
- Refactor code: Remove dead code, simplify logic, eliminate redundant checks
Example: Survived mutant → ask "Is this code necessary?" before adding tests
Run: npm run test:mutation
- AAA Pattern: Arrange → Act → Assert
- One concept per test: Don't test multiple behaviors in one test
- Descriptive names:
it("should throw error when not profiled")notit("works") - Setup/Cleanup: Use
beforeEachfor setup,afterEachfor cleanup (vi.clearAllMocks()) - Strict equality: ALWAYS use
toStrictEqualinstead oftoEqual(enforced in Core Rules)
1. ProfilerData.addRender() - Called on EVERY render
- Must be O(1) time complexity
- No allocations if possible
- Use
array.push(), not spread operator
2. ProfilerCache - Lazy evaluation
- Compute values only when requested
- O(1) invalidation on state change
- Freeze returned values
3. Matchers - Fast comparison
- O(1) operations, clear error messages
- No memory leaks
When introducing breaking changes:
- Update
package.jsonversion (major bump) - Update
sonar-project.propertiesversion (must match) - Update README.md with migration guide
- Update examples in
examples/
Follow Conventional Commits:
feat- New feature |fix- Bug fix |perf- Performancerefactor- Code restructuring |test- Tests |docs- Documentationchore- Maintenance |breaking- Breaking change
Format: type(scope): description
npm test # Unit/integration tests
npm run test:coverage # Coverage report
npm run test:properties # Property tests (30s timeout)
npm run test:bench # Benchmarks
npm run typecheck # TypeScript check
npm run lint # ESLint
npm run build # Production build5 MCP servers configured: vitest, sonarqube, jetbrains, eslint, context7
Details: See .claude/mcp-guide.md for full usage guide.
Quick reference — prefer MCP over npm:
mcp__vitest__run_testsinstead ofnpm testmcp__sonarqube__*for quality analysismcp__eslint__lint-filesfor linting
END OF DOCUMENT
💡 Common Pitfalls: See Core Rules for frozen arrays (Rule 5), file extensions (Rule 1), and version sync (Rule 4).