# Guide de développement Ce document couvre les conventions de code, les règles de sécurité et la procédure de publication du package `@zen/core`. Pour les conventions de rédaction : [LANGUE.md](./dev/LANGUE.md) et [REDACTION.md](./dev/REDACTION.md). Pour les décisions de design et l'identité visuelle : [DESIGN.md](./DESIGN.md). Pour l'architecture partagée (composants, icônes) : [ARCHITECTURE.md](./dev/ARCHITECTURE.md). Pour la procédure de publication du package : [PUBLICATION.md](./dev/PUBLICATION.md). Pour les conventions de commit : [COMMITS.md](./dev/COMMITS.md). > **Contexte projet** : les utilisateurs finaux créent leur projet via `npx @zen/start`, qui génère automatiquement une structure Next.js avec la plateforme déjà intégré. Lors d'une assistance ou d'une revue, partez du principe que le projet cible est déjà structuré de cette façon. --- ## Standards de code **Les promesses ne s'ignorent pas.** Chaque `Promise` est `await`ée ou `.catch()`ée. Une promesse silencieuse qui échoue est un bug invisible. **Les variables d'environnement et la documentation se mettent à jour avec le code.** Toute variable ajoutée, renommée ou supprimée doit être reflétée dans `.env.example`. Toute décision architecturale ou convention nouvelle doit être documentée dans le fichier `docs/` concerné. **Une fonction, une responsabilité.** Si elle fait deux choses, c'est deux fonctions. Si elle ne tient pas sur un écran, la découper. **Le flux de contrôle se lit de haut en bas.** Pas de récursion non bornée, pas de callbacks imbriqués à plus de deux niveaux. Quelqu'un qui lit le code pour la première fois doit pouvoir suivre l'exécution sans se perdre. **Les données entrantes sont suspectes.** On valide en entrée de fonction. On ne suppose pas que l'appelant a fait le travail. **La portée des variables est minimale.** On déclare au plus près de l'usage. Pas de variables réutilisées pour deux rôles différents. **ESLint passe sans avertissement.** Un warning ignoré aujourd'hui est un bug non détecté demain. **Les commentaires reflètent toujours le comportement réel du code.** Un commentaire obsolète est pire qu'un commentaire absent — il induit en erreur. Quand on modifie une fonction, on met à jour son commentaire. Un commentaire qui contredit le code est un bug de documentation. --- ## Conventions d'arborescence Une feature (`src/features//` ou `src/core//`) suit la règle **flat + un barrel** : un `index.js` qui ré-exporte, des fichiers plats côte à côte pour l'implémentation. Pas de sous-dossier `lib/`, `middleware/`, `actions/` quand il ne contient qu'un ou deux fichiers — remonter directement au niveau du dossier feature. Les sous-dossiers sont autorisés uniquement quand ils contiennent plusieurs fichiers du même rôle : `components/`, `pages/`, `templates/`, `widgets/`. ### Suffixes de runtime Tout fichier épinglé à une frontière Next.js porte le suffixe dans son nom : - `.server.js` → code serveur strict (peut importer `pg`, `fs`, etc.) - `.client.js` → débute par `'use client'` - pas de suffixe → module neutre, utilisable des deux côtés - `actions.js` → débute par `'use server'` (server actions Next.js) Ces suffixes ne sont pas cosmétiques : **le build les utilise comme source de vérité**. Le build compile l'intégralité de `src/` avec `bundle: false` — chaque fichier reste un module séparé, ce qui permet à Next.js de respecter les frontières RSC et `'use client'` sans que le bundler ne fusionne les modules. --- ## Build et configuration tsup `tsup.config.js` compile **tous les fichiers `.js` et `.jsx` de `src/`** avec `bundle: false`. Chaque fichier devient un module standalone dans `dist/` ; les imports relatifs sont préservés tels quels. La structure `dist/` reflète exactement `src/` grâce à `outbase: 'src'`. - **Ajouter un module public = éditer seulement `package.json#exports`.** L'entry list se régénère au prochain build via un walk de `src/`. - **Pas de bundling des fichiers internes** : les modules de registre (`registry.js`, etc.) sont des singletons — les bundler créerait une copie inline distincte et casserait le partage d'état entre les pages et les widgets. - Les self-imports `@zen/core/*` sont générés automatiquement à partir des clés de `exports` et restent toujours dans la liste `external`. - Les fichiers `.js` et `.jsx` sont tous traités comme du JSX via esbuild (`loader: { '.js': 'jsx' }`, `jsx: 'automatic'`) — pas besoin d'extension `.jsx` pour écrire du JSX. --- ## Étendre l'admin L'admin utilise un **registre runtime** pour permettre aux projets consommateurs d'ajouter des widgets, des entrées de navigation, et des pages sans modifier le core. ```js // app/zen.extensions.js — projet consommateur import { registerWidget, registerWidgetFetcher, registerNavItem, registerNavSection, registerPage, } from '@zen/core/features/admin'; import OrdersWidget from './admin/OrdersWidget'; import OrdersPage from './admin/OrdersPage'; import { countOrders } from './admin/orders.server'; registerWidgetFetcher('orders', async () => ({ total: await countOrders() })); registerWidget({ id: 'orders', Component: OrdersWidget, order: 20 }); registerNavSection({ id: 'commerce', title: 'Commerce', icon: 'ShoppingBag03Icon', order: 30 }); registerNavItem({ id: 'orders', label: 'Commandes', icon: 'ShoppingBag03Icon', href: '/admin/orders', sectionId: 'commerce' }); registerPage({ slug: 'orders', Component: OrdersPage, title: 'Commandes' }); ``` ```js // app/layout.js — un seul import suffit ; les side effects enregistrent tout. import './zen.extensions'; ``` --- ## Sécurité **Données entrantes** : toute donnée externe est considérée malveillante par défaut. On valide côté serveur uniquement. **Base de données** : uniquement des requêtes paramétrées — jamais de SQL construit par concaténation de chaînes. **Secrets** : aucun token, clé API ou mot de passe dans le code. Tout passe par des variables d'environnement, jamais commitées. **Erreurs exposées** : pas de stack trace, nom de table ou requête SQL retournés au client. On log côté serveur, on renvoie un message générique côté client.