Structure du projet

Ce que génère @lrvo/start, à quoi sert chaque fichier, et où écrire son propre code.

Cette page décrit l'arborescence générée par npx @lrvo/start, puis celle de @lrvo/core lui-même.

Le projet généré

mon-projet/
├── app/
│   ├── layout.js                     layout racine : providers, thème, manifestes
│   ├── icon.svg
│   ├── .lrvo/                        AUTO-GÉNÉRÉ, gitignoré
│   │   ├── modules.generated.js
│   │   ├── modules.client.js
│   │   ├── public-pages.server.js
│   │   ├── blocks.server.js
│   │   ├── blocks.client.js
│   │   └── templates.server.js
│   ├── (public)/
│   │   ├── layout.js
│   │   ├── page.js                   page d'accueil
│   │   ├── not-found.js
│   │   └── [...slug]/page.js         catch-all du contenu public
│   ├── (auth)/auth/
│   │   ├── page.js                   redirige vers /auth/login
│   │   └── [...auth]/page.js         pages d'authentification
│   ├── (admin)/admin/
│   │   ├── layout.js
│   │   ├── page.js
│   │   └── [...admin]/page.js        pages d'admin enregistrées
│   └── lrvo/
│       ├── api/[...path]/route.js    routeur API
│       ├── [...path]/page.js         pages publiques de module, + leur manifeste
│       └── media/[filename]/route.js route fichier des médias
├── components/
│   ├── Icons.js
│   └── lrvo/
│       ├── blocks/<id>/              blocs personnalisés
│       └── templates/                templates de rendu public
├── styles/globals.css
├── dev/icons/                        script make-favicon
├── instrumentation.js                boot serveur
├── theme.js                          apparence du projet
├── next.config.mjs
├── jsconfig.json                     alias @styles, @components
├── .npmrc                            token du registre privé, suivi par git
└── .env                              variables d'environnement, suivi par git

Les fichiers qui comptent

instrumentation.js

Point d'entrée du boot serveur. Il charge les manifestes puis initialise la plateforme.

export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    const { initializeLrvo } = await import('@lrvo/core');
    const { modules } = await import('./app/.lrvo/modules.generated.js');
    await import('./app/.lrvo/blocks.server.js');
    await import('./app/.lrvo/templates.server.js');
    const { default: theme } = await import('./theme.js');
    await initializeLrvo({ modules, theme });
  }
}

Le garde sur NEXT_RUNTIME évite d'exécuter ce code sur le runtime Edge, qui n'a ni pg ni système de fichiers.

Les templates sont chargés avant initializeLrvo pour être disponibles si un module les utilise pendant son boot.

theme.js

L'apparence de tout le projet : couleur de marque, teinte des gris, arrondis, coquille de l'admin, modes offerts. L'admin, les pages publiques, les modules, les courriels et les PDF la lisent. Le fichier généré porte les valeurs par défaut, commentées. Voir Personnaliser l'apparence.

app/layout.js

Le layout racine monte quatre choses, en plus de la feuille de style globale.

import '@styles/globals.css';
import LrvoModulesClient from './.lrvo/modules.client.js';
import LrvoBlocksClient from './.lrvo/blocks.client.js';
import './.lrvo/blocks.server.js';
import './.lrvo/templates.server.js';
import { LrvoProvider } from '@lrvo/core/features/provider';
import { ThemeStyle } from '@lrvo/core/themes/server';

export default function RootLayout({ children }) {
  return (
    <html lang="fr" suppressHydrationWarning>
      <head>
        <ThemeStyle />
      </head>
      <body className="antialiased">
        <LrvoModulesClient />
        <LrvoBlocksClient />
        <LrvoProvider>{children}</LrvoProvider>
      </body>
    </html>
  );
}

L'alias @styles vient du jsconfig.json généré, qui déclare aussi @components. styles/globals.css importe Tailwind puis @lrvo/core/styles/core.css. Voir Styles.

Deux détails ne sont pas évidents.

Les composants de manifeste sont rendus, pas seulement importés. Sous Turbopack, un import purement side-effect d'un fichier 'use client' orphelin inclut le fichier dans le bundle mais n'exécute pas son code top-level. Les enregistrements ne tourneraient jamais.

Les manifestes serveur sont importés ici en plus d'instrumentation.js. C'est nécessaire pour que les render.js marqués 'use client' et les templates traversent le compilateur RSC et soient référencés dans le manifeste client de React. Sans cet import, un bloc au rendu client échoue avec « Could not find the module in the React Client Manifest ».

Le manifeste des pages publiques

Le troisième cas de la même règle, mais son remède vit dans la route et non dans le layout :

// app/lrvo/[...path]/page.js
import '../../.lrvo/public-pages.server.js';

export { default, generateMetadata } from '@lrvo/core/public-pages/server';

Une page publique de module est retrouvée au rendu par une lecture de registre, pas par un import. Le compilateur RSC de la route ne peut donc pas deviner qu'elle existe, et les composants 'use client' qu'elle rend restent hors du React Client Manifest. L'import du manifeste crée l'arête statique qui manque.

Il est accroché à la route plutôt qu'au layout pour que les pages publiques des modules n'entrent pas dans le graphe de toutes les autres routes du site.

La règle générale, valable pour les trois cas : tout registre dont les entrées sont des composants doit être alimenté depuis le graphe RSC de la route qui les rend, jamais depuis instrumentation.js seul.

Les groupes de routes

GroupeRôle
(public)Site public. Le [...slug] délègue au registre de contenu public.
(auth)Pages d'authentification, servies par une seule route catch-all.
(admin)Administration. Le layout et les pages sont ré-exportés du core.

Chaque route du core tient en une ligne :

// app/(admin)/admin/layout.js
export { default } from '@lrvo/core/features/admin/layout';

C'est ce qui permet à une mise à jour du package de faire évoluer l'admin sans toucher au projet.

app/.lrvo/

Généré par npx lrvo-modules sync, gitignoré, jamais committé. Régénéré par les scripts postinstall, dev et build.

Ne jamais éditer ces fichiers : la prochaine synchronisation écrase les modifications. Voir Cycle de vie d'un module.

components/lrvo/

C'est ici que le projet ajoute ses extensions de contenu.

DossierContenu
blocks/<id>/Un bloc personnalisé, avec fields.js, template.js, render.js.
templates/Templates de rendu public, plus le content.js partagé.

Les deux sont détectés par lrvo-modules sync. Voir Blocs personnalisés et Contenu public et templates.

Où écrire son code

BesoinEmplacement
Page publique fixeapp/(public)/…
Extension d'admin propre au projetapp/lrvo.extensions.js, importé par app/layout.js
Bloc personnalisécomponents/lrvo/blocks/<id>/
Template de renducomponents/lrvo/templates/<id>.js
Fonctionnalité réutilisable entre projetsun package @lrvo/module-*

Si une fonctionnalité peut vivre en module, elle vit en module. Voir Créer un module.

L'arborescence de @lrvo/core

Utile pour lire la référence, pas pour développer.

src/
├── core/              infrastructure : api, database, users, email, storage,
│                      payments, pdf, cron, crypto, toast, themes, registres
├── features/          auth, admin, provider
├── shared/            components, icons, hooks, lib, styles, utils
├── blocks-registry/   blocs personnalisés et rendu serveur
└── media-registry/    façade vers @lrvo/module-medias

Une feature suit la règle plat plus un barrel : un index.js qui ré-exporte, des fichiers plats à côté. Les sous-dossiers ne sont autorisés que pour plusieurs fichiers du même rôle : components/, pages/, templates/, widgets/.

Suffixes de runtime

Le nom des fichiers porte l'information de frontière, et le build s'en sert comme source de vérité.

SuffixeSignification
.server.jsCode serveur strict. Peut importer pg, fs, node:*.
.client.jsDébute par 'use client'.
actions.jsDébute par 'use server'.
AucunModule neutre.

Voir Frontières serveur/client.

Étape suivante

Administration ajoute une section complète à l'admin.

Sur cette page