diff --git a/.claude/rules/coding-style.md b/.claude/rules/coding-style.md index c4528bf6..d8298fb4 100644 --- a/.claude/rules/coding-style.md +++ b/.claude/rules/coding-style.md @@ -5,29 +5,35 @@ Based on Google TypeScript Style Guide and industry best practices. ## TypeScript/JavaScript ### File Basics + - Files use UTF-8 encoding - Use `.ts` for TypeScript, `.tsx` for React TypeScript files - One component per file (React) - Use kebab-case or camelCase for file names (match project convention) ### Formatting + - 2 spaces for indentation (no tabs) - Column limit: 80 characters (soft limit: 100 for JSX) - Use `clang-format` or project formatter - Trailing commas in multi-line literals ### Braces (K&R Style) + ```ts // No line break before opening brace if (condition) { - doSomething(); + doSomething() } // Empty blocks may be concise -if (condition) {} else {} +if (condition) { +} else { +} ``` ### Whitespace + - One blank line between imports and implementation - Single blank line between consecutive methods in classes - No trailing whitespace @@ -35,12 +41,14 @@ if (condition) {} else {} - Space before opening brace ### Variables + - Use `const` by default, `let` only when reassignment needed - Never use `var` - One variable per declaration - Declare variables close to first use ### Naming + - Classes/Interfaces/Types/Enums: `UpperCamelCase` - Functions/Methods/Variables: `lowerCamelCase` - Constants: `UPPER_SNAKE_CASE` @@ -50,6 +58,7 @@ if (condition) {} else {} - File names: kebab-case (e.g., `user-profile.tsx`, `api-utils.ts`) ### Imports + - Use named exports, avoid default exports - Use `import type` for type-only imports - Group imports: external → internal → relative @@ -59,6 +68,7 @@ if (condition) {} else {} ## TypeScript ### Type Safety + - Use strict TypeScript (`strict: true` in tsconfig) - Never use `any` — use `unknown` instead - Prefer `type` over `interface` for simple type aliases @@ -66,6 +76,7 @@ if (condition) {} else {} - Use explicit return types for public functions ### Example + ```typescript // CORRECT: Explicit types function processUser(user: User): Result { @@ -73,7 +84,8 @@ function processUser(user: User): Result { } // AVOID: any -function processUser(user: any): any { // NEVER +function processUser(user: any): any { + // NEVER // ... } @@ -84,23 +96,25 @@ async function parseResponse(response: unknown): Promise { ``` ### Type Assertion + ```typescript // CORRECT: Safe assertion with validation -const data = result as KnownType; +const data = result as KnownType // CORRECT: Use unknown for API responses async function fetchUser(id: string): Promise { - const response = await fetch(`/api/users/${id}`); - return response.json(); + const response = await fetch(`/api/users/${id}`) + return response.json() } // NEVER: Blind casting -const user = (response as any).data.user; +const user = (response as any).data.user ``` ## React Components ### Component Structure + ```typescript // 1. Imports (external, then internal) // 2. Types/Interfaces @@ -129,18 +143,21 @@ export function UserProfile({ userId }: Props) { ``` ### Hooks Rules + - Call hooks at top level only (no conditionals) - Custom hooks must start with `use` - Extract complex logic to custom hooks - Keep hooks focused (split large hooks) ### State Management + - Prefer local state (`useState`) for component-specific state - Use React Context for shared state - Use Zustand/Redux for complex global state - Co-locate state with its usage ### Performance + - Memoize expensive computations: `useMemo` - Memoize callbacks passed to children: `useCallback` - Use `React.lazy` for code splitting @@ -159,69 +176,72 @@ const handleClick = useCallback(() => { ## Async/Await ### Rules + - Always handle errors in async functions - Use `try/catch` for error handling - Avoid async in useEffect (handle cleanup) - Use `Promise.all` for parallel operations ### Example + ```typescript // CORRECT: Proper async handling async function fetchData(): Promise { try { - const response = await api.getData(); - return response.data; + const response = await api.getData() + return response.data } catch (error) { - console.error('Failed to fetch:', error); - throw new DataFetchError('Failed to fetch data'); + console.error('Failed to fetch:', error) + throw new DataFetchError('Failed to fetch data') } } // CORRECT: Parallel requests -const [users, posts] = await Promise.all([ - fetchUsers(), - fetchPosts(), -]); +const [users, posts] = await Promise.all([fetchUsers(), fetchPosts()]) // CORRECT: Async useEffect with cleanup useEffect(() => { - let cancelled = false; - + let cancelled = false + async function load() { - const data = await fetchData(); - if (!cancelled) setData(data); + const data = await fetchData() + if (!cancelled) setData(data) + } + + load() + + return () => { + cancelled = true } - - load(); - - return () => { cancelled = true; }; -}, []); +}, []) ``` ## Error Handling ### Rules + - Use typed errors where possible - Never swallow errors silently - Create custom error classes for domain errors - Handle errors at component boundaries ### Custom Errors + ```typescript class ValidationError extends Error { constructor( message: string, - public field: string + public field: string, ) { - super(message); - this.name = 'ValidationError'; + super(message) + this.name = 'ValidationError' } } class NotFoundError extends Error { constructor(resource: string, id: string) { - super(`${resource} with id ${id} not found`); - this.name = 'NotFoundError'; + super(`${resource} with id ${id} not found`) + this.name = 'NotFoundError' } } ``` @@ -229,6 +249,7 @@ class NotFoundError extends Error { ## CSS/Styling ### Rules + - Use CSS Modules, Tailwind, or styled-components (pick one) - Follow existing project's styling approach - Use CSS custom properties for theme values @@ -236,16 +257,21 @@ class NotFoundError extends Error { - Keep styles co-located with components ### Naming (CSS Modules) + ```css /* ComponentName.module.css */ -.container { } -.headerTitle { } -.contentHidden { } +.container { +} +.headerTitle { +} +.contentHidden { +} ``` ## Accessibility (a11y) ### Requirements + - All interactive elements must be keyboard accessible - Use semantic HTML (`