--- name: oliwer-code-style description: > Applique le style de code personnel d'Oliwer pour tout projet web TypeScript/JavaScript (React, Next.js, Vite, Hono, Prisma). TOUJOURS utiliser ce skill quand : - Tu generes du code TS/JS/TSX/JSX (composants React, hooks, services, controleurs, routes, etc.) - Tu reviewes ou corriges du code existant TypeScript/JavaScript - Tu proposes une architecture de projet, une structure de dossiers, ou un decoupage de modules - Tu travailles sur un projet avec Hono, Prisma, Next.js, Vite, ou React - L'utilisateur demande comment structurer une feature, un module, un service, ou un composant --- # Style de Code Personnel JS/TS Dev expert. Respecte conventions strictes pour tout projet web (React, NextJS, Vite, Hono). --- ## 0. Regles de Style du Texte Genere Ces regles s'appliquent a tout texte produit par Claude : commentaires de code, messages d'erreur, JSDoc, logs, prose. - **Apostrophes :** Ne jamais ecrire `'` (apostrophe typographique ou droite) dans du texte genere. Utiliser `'` a la place. Exemples : `J'ai`, `l'utilisateur`, `n'existe pas`, `c'est`. - **Tiret cadratin :** Ne jamais utiliser `--` ou `---` comme separateur dans du texte. Utiliser `:` ou reformuler la phrase. - Ces regles ne s'appliquent pas au contenu des blocs de code (les strings JS/TS, les valeurs, les templates litteraux gardent leur syntaxe normale). ```ts // ❌ A eviter dans commentaires et JSDoc // Valide l'email — retourne null si invalide // J'ai choisi cette approche car... // ✅ Correct // Valide l'email. Retourne null si invalide. // J'ai choisi cette approche car... // Les strings dans le code restent normales const msg = "L'email est invalide" // OK, c'est du code ``` --- ## 1. Architecture et Paradigme - **General :** Fonctions flechees (`const myFunc = () => {}`) pour composants React, utilitaires, logique fonctionnelle. - **Frontend (Hors NextJS) :** Architecture "Features" : code groupe par fonctionnalite, pas par type de fichier. - **API/Backend (Hono/Prisma) :** OO (Classes) pour controleurs/services. Structure modulaire par ressource. - **Server/Client (Next.js) :** Tout composant = Server Component par defaut. `'use client'` uniquement si hooks, evenements DOM ou state local. Jamais sur layout sauf necessite absolue. ``` // ✅ Structure backend modulaire (Hono) src/ ├── modules/ │ └── user/ │ ├── user.controller.ts # Classe : orchestration requete/reponse │ ├── user.service.ts # Classe : logique metier pure │ ├── user.repository.ts # Classe : acces Prisma │ ├── user.routes.ts # Fonctions flechees : declaration routes Hono │ └── index.ts # Barrel export ├── middlewares/ └── index.ts // ✅ Structure feature-based (React/Vite) src/ ├── features/ │ └── auth/ │ ├── components/ │ ├── hooks/ │ ├── utils/ │ └── index.ts ├── components/ui/ └── lib/ ``` --- ## 2. Imports et Exports - **Barrel files** (`index.ts`) toujours. Importer depuis barrel, jamais depuis fichier direct. ```ts // ✅ Bien import { UserService, AuthService } from '@/modules/user' import { Button, Input } from '@/components/ui' // ❌ Eviter import { UserService } from '@/modules/user/user.service' import { Button } from '@/components/ui/Button' ``` --- ## 3. Typage TypeScript et Validation - **`any` INTERDIT.** Generiques ou `unknown` si necessaire. - `interface` pour objets extensibles. `type` pour unions/intersections/alias primitifs. - `enum` pour valeurs fixes connues a la compilation. - **Zod** pour toute validation runtime (formulaires, body, API externes, env vars). ```ts // ✅ interface pour les objets interface User { id: string email: string role: UserRole } // ✅ type pour les unions type Status = 'active' | 'inactive' | 'pending' // ✅ enum pour les valeurs fixes enum UserRole { Admin = 'ADMIN', Member = 'MEMBER', Guest = 'GUEST', } // ✅ Zod pour validation + inference de type const CreateUserSchema = z.object({ email: z.string().email(), password: z.string().min(8), role: z.nativeEnum(UserRole).default(UserRole.Member), }) type CreateUserDto = z.infer // ✅ Variables d'environnement typees et validees // src/env.ts const EnvSchema = z.object({ DATABASE_URL: z.string().url(), JWT_SECRET: z.string().min(32), NODE_ENV: z.enum(['development', 'production', 'test']).default('development'), }) export const env = EnvSchema.parse(process.env) // Usage : import { env } from '@/env' ``` --- ## 4. Gestion des Erreurs - **Backend (Hono) :** Middleware d'erreur global + classes typees heritant `Error`. - **Frontend :** Retourner objet discrimine depuis Server Actions/fetch. Pas de `throw` cote client. - **Async/await** toujours. Jamais `.then()/.catch()`. `try/catch` explicite. ```ts // ✅ Classes d'erreurs typees (backend) class AppError extends Error { constructor( public readonly message: string, public readonly statusCode: number, public readonly code: string, ) { super(message) } } class NotFoundError extends AppError { constructor(resource: string) { super(`${resource} not found`, 404, 'NOT_FOUND') } } // ✅ Resultat discrimine (frontend / Server Actions) type Result = { success: true; data: T } | { success: false; error: string } const createUser = async (dto: CreateUserDto): Promise> => { try { const user = await userService.create(dto) return { success: true, data: user } } catch (error) { return { success: false, error: "Impossible de creer l'utilisateur." } } } // Cote composant const result = await createUser(formData) if (!result.success) { setError(result.error) return } redirect(`/users/${result.data.id}`) ``` --- ## 5. Conventions de Nommage - `PascalCase` : composants, classes, interfaces, enums + fichiers (`UserCard.tsx`, `UserService.ts`). - `camelCase` : hooks, utilitaires, variables, fonctions + fichiers (`useAuth.ts`, `formatDate.ts`). - `kebab-case` : fichiers/dossiers routeur Next.js (`mon-profil/page.tsx`, `user-settings/layout.tsx`). - `SCREAMING_SNAKE_CASE` : constantes globales (`MAX_RETRY_COUNT`, `API_BASE_URL`). ``` // ✅ Nommage correct src/ ├── app/ # Next.js App Router │ └── user-settings/ │ └── page.tsx # kebab-case ├── features/auth/ │ ├── components/ │ │ └── LoginForm.tsx # PascalCase │ ├── hooks/ │ │ └── useSession.ts # camelCase │ └── utils/ │ └── formatAuthError.ts # camelCase └── lib/ └── constants.ts # MAX_UPLOAD_SIZE = 10_000_000 ``` --- ## 6. Performance React - **React Compiler (`experimental.reactCompiler: true`) :** Pas de `useMemo`/`useCallback`/`React.memo` manuel : le compiler insere automatiquement. - **Sans compiler (React <=18) :** `useMemo`/`useCallback` uniquement si calcul couteux (profile) ou lib tierce compare par reference (DnD, chart, map). - Preferer composition a memorisation manuelle. ```tsx // ✅ Avec React Compiler : ecriture naturelle, le compiler optimise const ProductList = ({ products, onSelect }: ProductListProps) => { const sorted = products.slice().sort((a, b) => a.name.localeCompare(b.name)) const handleSelect = (id: string) => onSelect(id) return
    {sorted.map(p => )}
} // ✅ Sans compiler : memorisation manuelle ciblee apres profiling const ProductList = ({ products, onSelect }: ProductListProps) => { const sorted = useMemo( () => products.slice().sort((a, b) => a.name.localeCompare(b.name)), [products], ) const handleSelect = useCallback((id: string) => onSelect(id), [onSelect]) return
    {sorted.map(p => )}
} ``` --- ## 7. Hooks Personnalises - Prefixe `use` + `PascalCase` : `useUserSession`, `useCartItems`. - 1 hook = 1 responsabilite. Extraire des que ~3 hooks natifs lies au meme domaine. - Dans `src/hooks/` (global) ou `src/features//hooks/` (local) + barrel export. ```ts // ✅ useCartItems.ts const useCartItems = () => { const [items, setItems] = useState([]) const [isLoading, setIsLoading] = useState(false) const addItem = async (productId: string) => { setIsLoading(true) const result = await cartService.add(productId) if (result.success) setItems(prev => [...prev, result.data]) setIsLoading(false) } const removeItem = (id: string) => { setItems(prev => prev.filter(item => item.id !== id)) } return { items, isLoading, addItem, removeItem } } ``` --- ## 8. UI et Composants (React / NextJS) - **Tailwind CSS** exclusivement. Pas de styles inline sauf valeurs dynamiques impossibles en classes. - Variantes conditionnelles complexes : `clsx` ou `cva`. - Verifier si composant existe avant d'en creer un. Tout reutilisable dans `components/ui/`. ```tsx // ✅ Variantes avec cva import { cva, type VariantProps } from 'class-variance-authority' import { clsx } from 'clsx' const buttonVariants = cva( 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors', { variants: { variant: { primary: 'bg-blue-600 text-white hover:bg-blue-700', ghost: 'bg-transparent hover:bg-neutral-100 text-neutral-700', destructive: 'bg-red-600 text-white hover:bg-red-700', }, size: { sm: 'h-8 px-3', md: 'h-10 px-4', lg: 'h-12 px-6', }, }, defaultVariants: { variant: 'primary', size: 'md' }, }, ) interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps {} const Button = ({ variant, size, className, ...props }: ButtonProps) => (