Code Standards
Guide des conventions de code pour Wagoo.
TypeScript
Types obligatoires
Toujours typer vos variables :
// âś… Bon
const user: User = { id: '1', name: 'John' }
const count: number = 0
const isActive: boolean = true
// ❌ Mauvais
const user = { id: '1', name: 'John' }
const count = 0Return types
Toujours typer les retours de fonction :
// âś… Bon
function getUser(id: string): Promise<User> {
return db.user.findUnique({ where: { id } })
}
function formatName(name: string): string {
return name.toUpperCase()
}
// ❌ Mauvais
function getUser(id: string) {
return db.user.findUnique({ where: { id } })
}Interfaces vs Types
Préférer les interfaces pour les objets :
// âś… Bon
interface User {
id: string
name: string
email: string
}
// Types pour les unions
type Status = 'active' | 'inactive' | 'archived'
// ❌ Moins bon
type User = {
id: string
name: string
email: string
}React Components
Nommage
// âś… PascalCase pour les composants
const UserProfile = () => { ... }
const SettingsForm = () => { ... }
// ❌ camelCase
const userProfile = () => { ... }
const settingsForm = () => { ... }Props typing
// âś… Bon
interface ButtonProps {
label: string
onClick?: () => void
disabled?: boolean
variant?: 'primary' | 'secondary'
}
const Button: React.FC<ButtonProps> = ({
label,
onClick,
disabled = false,
variant = 'primary',
}) => {
return (
<button onClick={onClick} disabled={disabled} className={variant}>
{label}
</button>
)
}
// ❌ Mauvais - props pas typées
const Button = ({ label, onClick, ...props }) => {
return <button {...props}>{label}</button>
}Hooks rules
// âś… Bon - Hooks Ă la racine
const Profile = () => {
const [name, setName] = useState('')
const router = useRouter()
return <div>{name}</div>
}
// ❌ Mauvais - Hooks dans une condition
const Profile = ({ show }: { show: boolean }) => {
if (show) {
const [name, setName] = useState('') // ❌ Hook conditionnel!
}
return null
}Nommage de fichiers
Conventions
# Composants
components/
├── Button.tsx
├── UserCard.tsx
└── forms/
└── LoginForm.tsx
# Pages
app/
├── (dashboard)/
│ ├── page.tsx
│ └── settings/
│ └── page.tsx
# Utilities
lib/
├── api.ts
├── utils.ts
└── validators.ts
# Types
types/
├── user.ts
├── project.tsJamais camelCase pour les fichiers :
// âś… Bon
import Button from '@/components/Button'
// ❌ Mauvais
import Button from '@/components/button'Imports
Ordre des imports
// ✅ Bon - Groupes séparés par ligne vide
import React from 'react'
import { useState } from 'react'
import { Button } from '@/components/Button'
import { useUser } from '@/hooks/useUser'
import type { User } from '@/types/user'
// ❌ Mélangé
import React from 'react'
import { Button } from '@/components/Button'
import type { User } from '@/types/user'
import { useUser } from '@/hooks/useUser'Imports relatifs vs absolus
// ✅ Bon - Absolus pour clarté
import { Button } from '@/components/Button'
import { User } from '@/types/user'
// ❌ Relatifs à éviter
import { Button } from '../../../components/Button'Conventions de formatage
Prettier config
{
"semi": false,
"singleQuote": true,
"trailingComma": "es5",
"tabWidth": 2,
"printWidth": 100,
"arrowParens": "always"
}Quotes
// âś… Single quotes
const name = 'John'
// ❌ Double quotes
const name = "John"Semicolons
// âś… Pas de semicolon
const name = 'John'
// ❌ Avec semicolon
const name = 'John';ESLint rules
Règles importantes
// .eslintrc.js
module.exports = {
rules: {
'no-console': ['warn', { allow: ['warn', 'error'] }],
'no-unused-vars': 'error',
'prefer-const': 'error',
'eqeqeq': ['error', 'always'],
'@typescript-eslint/explicit-function-return-types': 'warn',
'@typescript-eslint/no-explicit-any': 'error',
},
}Linter
# Vérifier
pnpm lint
# Corriger
pnpm lint:fixComments
Commentaires utiles
// âś… Bon - explique le "pourquoi"
// Cache le résultat pendant 1h pour éviter les requêtes API
const cacheKey = `user-${userId}-${Date.now()}`
// ❌ Mauvais - évident
// Set the name
const name = 'John'JSDoc
// âś… Bon pour les exports publics
/**
* Formate une date selon la locale de l'utilisateur
* @param date - La date Ă formater
* @param locale - La locale (défaut: 'fr-FR')
* @returns La date formatée en string
*/
export function formatDate(date: Date, locale = 'fr-FR'): string {
return date.toLocaleDateString(locale)
}
// ❌ Pas besoin partout
// Calcule la somme
function sum(a: number, b: number) {
return a + b
}Gestion des erreurs
Toujours catcher
// âś… Bon
try {
const user = await db.user.findUnique(...)
} catch (error) {
console.error('Failed to fetch user:', error)
throw new Error('Unable to fetch user')
}
// ❌ Mauvais
const user = await db.user.findUnique(...) // Si erreur, crashValidation
import { z } from 'zod'
// âś… Bon avec Zod
const userSchema = z.object({
email: z.string().email(),
name: z.string().min(1),
})
const validateUser = (data: unknown) => {
return userSchema.safeParse(data)
}
// ❌ Mauvais - validation manuelle
function validateUser(data: any) {
if (!data.email || !data.email.includes('@')) {
throw new Error('Invalid email')
}
// ... plus de validation
}Constantes
// âś… Bon - constants en haut
const MAX_RETRY_ATTEMPTS = 3
const DEFAULT_PAGE_SIZE = 20
const getUserById = async (id: string) => {
// ...
}
// ❌ Hardcoded
const getUserById = async (id: string) => {
for (let i = 0; i < 3; i++) { // Pourquoi 3?
try {
return await db.user.findUnique(...)
} catch {}
}
}Async/Await
// âś… Bon
async function fetchUserData(id: string) {
try {
const user = await db.user.findUnique({ where: { id } })
return user
} catch (error) {
console.error('Failed:', error)
}
}
// ❌ Mélanger promises et async
function fetchUserData(id: string) {
return db.user.findUnique({ where: { id } }).then(user => user)
}Environment variables
// âś… Bon - valider les vars au startup
if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL is required')
}
// ❌ Mauvais - utiliser sans vérifier
const dbUrl = process.env.DATABASE_URL // Undefined?Prochaines étapes
→ Contributing
Last updated on