chore: update AGENTS.md
This commit is contained in:
161
AGENTS.md
161
AGENTS.md
@@ -3,129 +3,131 @@
|
|||||||
## Build Commands
|
## Build Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
pnpm install
|
||||||
|
|
||||||
# Development server (port 3000)
|
# Development server (port 3000)
|
||||||
npm run dev
|
pnpm dev
|
||||||
|
|
||||||
# Production build
|
# Production build
|
||||||
npm run build
|
pnpm build
|
||||||
|
|
||||||
# Preview production build
|
# Preview production build
|
||||||
npm run preview
|
pnpm preview
|
||||||
```
|
```
|
||||||
|
|
||||||
## Test Commands
|
## Test Commands
|
||||||
|
|
||||||
|
Use `pnpm exec` to run local binaries.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run all tests
|
# Run all tests
|
||||||
npx vitest
|
pnpm exec vitest
|
||||||
|
|
||||||
# Run tests in watch mode
|
# Run tests in watch mode
|
||||||
npx vitest --watch
|
pnpm exec vitest --watch
|
||||||
|
|
||||||
# Run tests with coverage
|
# Run tests with coverage
|
||||||
npx vitest --coverage
|
pnpm exec vitest --coverage
|
||||||
|
|
||||||
# Run a single test file
|
# Run a single test file (Preferred method)
|
||||||
npx vitest src/components/ui/__tests__/Button.test.tsx
|
pnpm exec vitest src/components/ui/__tests__/Button.test.tsx
|
||||||
|
|
||||||
# Run tests matching a pattern
|
# Run tests matching a pattern
|
||||||
npx vitest --grep "Button"
|
pnpm exec vitest --grep "Button"
|
||||||
|
|
||||||
# Run specific test by name
|
# Run specific test by name
|
||||||
npx vitest -t "passes aria-label to the button element"
|
pnpm exec vitest -t "passes aria-label to the button element"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lint & Type Check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Type check (strict)
|
||||||
|
pnpm exec tsc --noEmit
|
||||||
```
|
```
|
||||||
|
|
||||||
## Code Style Guidelines
|
## Code Style Guidelines
|
||||||
|
|
||||||
### TypeScript
|
### TypeScript
|
||||||
|
|
||||||
- Use **strict TypeScript** configuration
|
- Use **strict TypeScript** configuration (`strict: true`).
|
||||||
- Always define explicit return types for functions
|
- Always define explicit return types for functions.
|
||||||
- Use `type` for type definitions, `interface` for object shapes that may be extended
|
- Use `type` for type definitions, `interface` for object shapes that may be extended.
|
||||||
- Avoid `any` - use `unknown` when type is uncertain
|
- Avoid `any` - use `unknown` when type is uncertain.
|
||||||
|
- Use `import type` for type-only imports when possible.
|
||||||
|
|
||||||
### Imports (Order)
|
### Imports (Order)
|
||||||
|
|
||||||
1. React imports
|
1. React imports (`react`, `react-dom`)
|
||||||
2. Third-party libraries (alphabetical)
|
2. Third-party libraries (alphabetical, e.g., `motion/react`, `react-icons`)
|
||||||
3. Absolute imports from project (`@/components`, `@/utils`)
|
3. Absolute imports from project (`@/components`, `@/utils` if configured, otherwise relative)
|
||||||
4. Relative imports
|
4. Relative imports (`../../utils`)
|
||||||
5. CSS/SCSS imports last
|
5. CSS/SCSS imports last (`./Component.module.css`)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { useState, useEffect } from 'react';
|
import { type ReactNode, type ButtonHTMLAttributes } from 'react';
|
||||||
import { motion } from 'motion/react';
|
import { motion } from 'motion/react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useTranslation } from '../../i18n';
|
|
||||||
import { debounce } from '../../utils/debounce';
|
import { debounce } from '../../utils/debounce';
|
||||||
import styles from './Navbar.module.css';
|
import styles from './Navbar.module.css';
|
||||||
```
|
```
|
||||||
|
|
||||||
### Component Structure
|
### Component Structure
|
||||||
|
|
||||||
- Use **named exports** for all components
|
- Use **named exports** for all components.
|
||||||
- Props interface should be named `[ComponentName]Props`
|
- Props interface should be named `[ComponentName]Props`.
|
||||||
- Place props interface immediately before component
|
- Place props interface immediately before component.
|
||||||
- Use CSS Modules for styling (`.module.css`)
|
- Use CSS Modules for styling (`.module.css`).
|
||||||
- Destructure props in function signature with defaults
|
- Use `motion` components (e.g., `motion.button`) for animations.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onDrag'> {
|
||||||
variant?: 'primary' | 'secondary' | 'outline';
|
variant?: 'primary' | 'secondary' | 'outline';
|
||||||
size?: 'sm' | 'md' | 'lg';
|
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Button({
|
export function Button({
|
||||||
variant = 'primary',
|
variant = 'primary',
|
||||||
size = 'md',
|
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
children,
|
children,
|
||||||
|
className,
|
||||||
...props
|
...props
|
||||||
}: ButtonProps) {
|
}: ButtonProps) {
|
||||||
// Implementation
|
return (
|
||||||
|
<motion.button
|
||||||
|
className={`${styles.button} ${styles[variant]} ${className || ''}`}
|
||||||
|
whileTap={{ scale: 0.98 }}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</motion.button>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Naming Conventions
|
### Naming Conventions
|
||||||
|
|
||||||
- **Components**: PascalCase (e.g., `Navbar`, `Button`)
|
- **Components**: PascalCase (e.g., `Navbar`, `Button`)
|
||||||
- **Hooks**: camelCase with `use` prefix (e.g., `useRateLimit`, `useTypingEffect`)
|
- **Hooks**: camelCase with `use` prefix (e.g., `useRateLimit`)
|
||||||
- **Utilities**: camelCase (e.g., `debounce`, `formatDate`)
|
- **Utilities**: camelCase (e.g., `debounce`, `formatDate`)
|
||||||
- **Types/Interfaces**: PascalCase (e.g., `ButtonProps`, `Translations`)
|
- **Types/Interfaces**: PascalCase (e.g., `ButtonProps`)
|
||||||
- **Constants**: UPPER_SNAKE_CASE for module-level constants
|
|
||||||
- **Files**: Match default export name (e.g., `Navbar.tsx` exports `Navbar`)
|
- **Files**: Match default export name (e.g., `Navbar.tsx` exports `Navbar`)
|
||||||
|
- **Test Files**: `[Filename].test.tsx` inside `__tests__` directory or alongside file.
|
||||||
|
|
||||||
### Error Handling
|
### Error Handling
|
||||||
|
|
||||||
- Use try-catch for async operations
|
- Use try-catch for async operations.
|
||||||
- Provide meaningful error messages
|
- Provide meaningful error messages.
|
||||||
- Use early returns to avoid nested conditionals
|
- Clean up resources (timers, event listeners) in `useEffect` cleanup.
|
||||||
- Clean up resources (timers, event listeners) in useEffect cleanup
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
useEffect(() => {
|
|
||||||
const handleScroll = () => {
|
|
||||||
if (!ticking) {
|
|
||||||
window.requestAnimationFrame(() => {
|
|
||||||
setIsScrolled(window.scrollY > 20);
|
|
||||||
ticking = false;
|
|
||||||
});
|
|
||||||
ticking = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
||||||
return () => window.removeEventListener('scroll', handleScroll);
|
|
||||||
}, []);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
- Add `// @vitest-environment jsdom` at top of test files
|
- Add `// @vitest-environment jsdom` at top of test files.
|
||||||
- Use `describe` and `it` blocks with descriptive names
|
- Use `describe` and `it` blocks.
|
||||||
- Clean up after each test with `cleanup()` from testing-library
|
- Clean up after each test with `cleanup()` from `@testing-library/react`.
|
||||||
- Mock external dependencies appropriately
|
- Mock external dependencies appropriately.
|
||||||
- Test behavior, not implementation
|
- Test behavior, not implementation details.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// @vitest-environment jsdom
|
// @vitest-environment jsdom
|
||||||
@@ -133,9 +135,7 @@ import { render, screen, cleanup } from '@testing-library/react';
|
|||||||
import { describe, it, expect, afterEach } from 'vitest';
|
import { describe, it, expect, afterEach } from 'vitest';
|
||||||
|
|
||||||
describe('Button', () => {
|
describe('Button', () => {
|
||||||
afterEach(() => {
|
afterEach(() => cleanup());
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders with correct aria-label', () => {
|
it('renders with correct aria-label', () => {
|
||||||
render(<Button aria-label="Submit">Click</Button>);
|
render(<Button aria-label="Submit">Click</Button>);
|
||||||
@@ -144,46 +144,25 @@ describe('Button', () => {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Performance
|
### Performance & Best Practices
|
||||||
|
|
||||||
- Use `React.lazy()` for code splitting on routes
|
- Use `React.lazy()` for code splitting on routes.
|
||||||
- Memoize expensive calculations with `useMemo`
|
- Memoize expensive calculations with `useMemo`.
|
||||||
- Use `requestAnimationFrame` for scroll/resize handlers
|
- Debounce expensive operations (use `debounce` utility).
|
||||||
- Debounce expensive operations (use `debounce` utility)
|
- Always include `aria-label` for interactive elements if text is not descriptive.
|
||||||
- Add `will-change` CSS sparingly for GPU acceleration
|
- Ensure semantic HTML (`<nav>`, `<main>`, `<header>`).
|
||||||
|
|
||||||
### Accessibility
|
|
||||||
|
|
||||||
- Always include meaningful `aria-label` for interactive elements
|
|
||||||
- Use semantic HTML elements (`<header>`, `<nav>`, `<main>`)
|
|
||||||
- Ensure keyboard navigation works
|
|
||||||
- Test with screen readers
|
|
||||||
|
|
||||||
### Project Structure
|
### Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
src/
|
src/
|
||||||
├── components/
|
├── components/
|
||||||
│ ├── effects/ # Visual effects (WebGL, animations)
|
│ ├── effects/ # Visual effects
|
||||||
│ ├── layout/ # Layout components (Navbar, Footer)
|
│ ├── layout/ # Layout components
|
||||||
│ ├── sections/ # Page sections (Hero, Services)
|
|
||||||
│ └── ui/ # Reusable UI components (Button, Input)
|
│ └── ui/ # Reusable UI components (Button, Input)
|
||||||
├── hooks/ # Custom React hooks
|
├── hooks/ # Custom React hooks
|
||||||
├── i18n/ # Internationalization (de.ts, en.ts)
|
├── i18n/ # Internationalization
|
||||||
├── pages/ # Route components
|
├── pages/ # Route components
|
||||||
├── styles/ # Global styles
|
├── styles/ # Global styles
|
||||||
└── utils/ # Utility functions
|
└── utils/ # Utility functions
|
||||||
```
|
```
|
||||||
|
|
||||||
### State Management
|
|
||||||
|
|
||||||
- Use React Context for global state (e.g., i18n)
|
|
||||||
- Prefer local state when possible
|
|
||||||
- Use `useRef` for DOM references and mutable values
|
|
||||||
- Store user preferences in localStorage with proper keys
|
|
||||||
|
|
||||||
### Comments
|
|
||||||
|
|
||||||
- Use JSDoc for exported functions
|
|
||||||
- Explain "why" not "what" in comments
|
|
||||||
- Keep comments concise and meaningful
|
|
||||||
|
|||||||
Reference in New Issue
Block a user