Reciprok Docs

Nommage

kebab-case pour fichiers et dossiers, conventions par contexte

Règle d'or : tous les fichiers et dossiers en kebab-case. Tout. Sans exception (ou alors documentée).

Pourquoi kebab-case

Critèresnake_casecamelCasePascalCasekebab-case
URLs (Next.js)salesalesalenatif
Cohérence avec les routes Next.js (/about-us)nonnonnonoui
Cohérence avec les CSS classesnonnonnonoui
Compatible filesystem case-insensitive (macOS)ouisalesaleoui
Lisibilitéouimoyenmoyenoui
Standard Node/Bun ecosystemnonrarenonmajoritaire

Le kebab-case gagne sur tous les fronts pour les noms de fichiers et de dossiers. À l'intérieur du code, on utilise les conventions JavaScript classiques (camelCase pour les variables, PascalCase pour les types/classes/composants).

Règles par type d'objet

Fichiers et dossiers

Toujours kebab-case.

✅ apps/server/src/modules/members/members.routes.ts
✅ apps/web/src/components/member-card.tsx
✅ packages/db/src/schema/room-capacity.ts
✅ apps/server/src/lib/error-format.ts

❌ apps/server/src/modules/Members/Members.routes.ts
❌ apps/web/src/components/MemberCard.tsx
❌ packages/db/src/schema/RoomCapacity.ts
❌ apps/server/src/lib/errorFormat.ts

Y compris pour les composants React. Le composant React lui-même reste en PascalCase (export function MemberCard()), mais le fichier qui le contient est en kebab-case (member-card.tsx).

Variables, fonctions, paramètres

camelCase.

const memberCount = 42;
function calculateBudget(request: Request) { /* ... */ }
const onMemberClick = () => { /* ... */ };

Types, interfaces, classes, composants React

PascalCase.

type MemberFilters = { /* ... */ };
interface RequestPayload { /* ... */ }
class CommissionCalculator { /* ... */ }
export function MemberCard() { /* ... */ }

Constantes globales

UPPER_SNAKE_CASE uniquement pour les vraies constantes (valeurs immuables, partagées, semantiquement constantes). Pas pour des const locaux.

const MAX_RESULTS_PER_PAGE = 50;
const ANTHROPIC_BASE_URL = 'https://api.anthropic.com';

// Non, c'est une const locale, pas une "constante" sémantique
const userInput = req.body; // ✅ camelCase, OK
const USER_INPUT = req.body; //

Enums

PascalCase pour le nom de l'enum, PascalCase ou UPPER_SNAKE_CASE pour les valeurs (à choisir, restons cohérents, on choisit UPPER_SNAKE_CASE).

enum RequestStatus {
  NEW = 'NEW',
  QUALIFYING = 'QUALIFYING',
  WON = 'WON',
}

Préférer cependant les union types (type Status = 'new' | 'qualifying' | 'won') aux enums TS, ils sont plus simples, plus compatibles avec les schémas Zod/TypeBox, et n'introduisent pas de bytecode runtime.

Tables et colonnes Drizzle

snake_case pour les noms côté base, camelCase côté TS via le mapping Drizzle.

export const requestAvailabilityCheck = pgTable('request_availability_check', {
  id: uuid('id').defaultRandom().primaryKey(),
  resultId: uuid('result_id').notNull(),
  tokenHash: text('token_hash').notNull(),
  // …
});

C'est la convention SQL standard, et Drizzle la mappe automatiquement en camelCase TS.

Variables d'environnement

UPPER_SNAKE_CASE.

DATABASE_URL=
ANTHROPIC_API_KEY=
META_WHATSAPP_TOKEN=

Conventions de fichiers par contexte

Modules métier (Elysia)

modules/[feature]/
├── [feature].routes.ts        # Elysia routes (HTTP)
├── [feature].service.ts       # Logique métier
├── [feature].repository.ts    # Accès Drizzle
├── [feature].schema.ts        # Schémas TypeBox locaux
└── index.ts                   # Re-export public uniquement

Composants React

components/
├── member-card/
│   ├── member-card.tsx        # Composant principal
│   ├── member-card.test.tsx   # Tests colocalisés
│   ├── member-card.types.ts   # Types locaux si > 5 lignes
│   └── index.ts               # Re-export

Quand un composant n'a qu'un fichier, on évite le sous-dossier :

components/
├── badge.tsx                  # OK, fichier unique
└── member-card/               # Sous-dossier seulement si plusieurs fichiers
    ├── member-card.tsx
    └── ...

Schémas Drizzle

packages/db/src/schema/
├── members.ts                 # Table member + relations
├── rooms.ts                   # Tables room et room_capacity
├── requests.ts
├── auth.ts
└── index.ts                   # Re-export tout

Un fichier par "domaine de table", pas un fichier par table (sauf si la table est très grosse).

Tests

Tests colocalisés avec le code, suffixe .test.ts ou .test.tsx.

members.service.ts
members.service.test.ts

Pas de dossier __tests__/ séparé. Plus simple à naviguer.

Hooks React

use- en préfixe (convention React), kebab-case.

hooks/
├── use-member.ts
├── use-debounce.ts
└── use-magic-token.ts

À l'intérieur, le hook lui-même est en camelCase (useMember).

Index files

Chaque dossier de module exporte son contrat public via index.ts. Les fichiers internes ne sont jamais importés directement depuis l'extérieur.

modules/members/index.ts
// Public, peut être importé depuis n'importe où
export { membersRoutes } from './members.routes';
export type { Member, MemberFilters } from './members.types';
modules/requests/requests.service.ts
// ✅ Bon, passe par l'index
import { type Member } from '@/modules/members';

// ❌ Mauvais, importe un fichier interne
import { type Member } from '@/modules/members/members.types';

Cette règle est lintable via eslint-plugin-boundaries ou les paths de tsconfig, à activer dès que possible.

Acronymes

Les acronymes restent lowercase dans les noms en camelCase, et PascalCase dans les noms PascalCase.

// camelCase
const apiKey = '...';        //
const apiURL = '...';        // ❌ (URL devrait être Url ici)
const userId = 42;           //
const userID = 42;           //

// PascalCase
type ApiKey = string;        //
type ApiResponse = { /* */ }; //
type APIResponse = { /* */ }; //

Cas particuliers

Fichiers de config

Les fichiers de config gardent leur nom natif imposé par l'outil :

biome.json                   # Biome impose ce nom
next.config.ts               # Next.js impose
drizzle.config.ts            # Drizzle impose
tsconfig.json
package.json

Composants UI shadcn

Les composants shadcn générés sont déjà en kebab-case (button.tsx, dropdown-menu.tsx). On garde la convention shadcn telle quelle.

Exceptions documentées

Liste des exceptions tolérées dans le repo (à maintenir à jour) :

Fichier / dossierPourquoi
README.md, CHANGELOG.md, CONTRIBUTING.mdConvention universelle GitHub
Dockerfile, MakefileImposé par l'outil
LICENSEConvention universelle
app/ (Next.js App Router)Imposé par Next.js
_app.tsx, _document.tsxImposé par Next.js (rare en App Router)

Aucune autre exception ne doit exister sans commentaire CONVENTION-EXCEPTION (cf. index conventions).

On this page