Démarrer

npm create fougere shop --frond blog --app nuxt
cd shop && pnpm install && pnpm dev

Alpha. Les paquets @fougere/* sont sur npm sous le tag alpha — le numéro de version est toute la promesse, la surface peut encore bouger. Ce site (site/) et demos/nuxt-blog servent de références complètes. La structure ci-dessous correspond à l'état actuel. Vous avez déjà une app Nuxt ? Ajoutez-y Fougere.

Structure d'une app

Une app Fougere est une app Nuxt plus un répertoire fronds/ et un fichier de config :

mon-app/
  fougere.config.ts       ← persistance, auth, topologie
  nuxt.config.ts          ← modules: ['@fougere/nuxt']
  fronds/
    blog/
      entities/Post.ts
      handlers/PostHandler.ts
      collectors/CurrentUserCollector.ts
      seeds/Post.seed.ts
    user/                 ← un second domaine
      entities/User.ts
  app/
    pages/                ← des pages Nuxt ordinaires, qui parlent via les primitives

Le scanner découvre les entités, handlers, collectors et seeds placés sous fronds/. L'emplacement du fichier et le nom de la classe servent à leur enregistrement ; aucun fichier de câblage supplémentaire n'est nécessaire.

Chaque dossier placé directement sous fronds/ définit une Frond. Une page importe une entité avec le nom de ce dossier :

import Post from '@frond/blog/entities/Post';

@fougere/nuxt lit le scan et enregistre un alias @frond/<nom> par Frond, donc cet import marche pour blog comme pour user — rien à ajouter, ni package.json, ni entrée dans pnpm-workspace.yaml. Renommer une Frond, ou en importer une depuis autre chose qu'une app Nuxt, est là où le package.json d'une Frond sert : voir Frond — nommer et importer.

fougere new écrit les mêmes dossiers un cran plus haut — l'app sous apps/<nom>/, les Fronds partagées à la racine du workspace, fougere: { root: '../..' } dans nuxt.config.ts — pour que plusieurs apps consomment les mêmes domaines. Une app, un dossier : la forme ci-dessus.

Un seul domaine, pas de fronds/ du tout

La racine du projet peut utiliser la même convention qu'un dossier sous fronds/. Une app avec un seul domaine peut donc placer directement ses entités et handlers à la racine :

mon-shop/
  fougere.config.ts
  nuxt.config.ts
  entities/Product.ts       ← la racine EST la Frond, nommée d'après le dossier
  handlers/ProductHandler.ts
  app/pages/

fougere new mon-shop --flat --frond blog écrit exactement ça. import Product from '@frond/mon-shop/entities/Product' — même règle d'alias, pas de segment fronds/, et le mot n'arrive que le jour où il y a deux domaines à distinguer. La racine doit au moins porter un entities/ pour compter ; un services/ seul est un nom de dossier ordinaire.

Quand le deuxième domaine arrive, il va dans fronds/billing/ et la Frond racine reste où elle est — rien ne bouge, aucun import n'est réécrit. fronds/ ne définit pas une Frond, c'est là où vivent les autres.

fougere.config.ts — référence

import { defineFougere } from '@fougere/core';
import { betterAuth } from '@fougere/auth-better';
import User from './fronds/user/entities/User';

export default defineFougere({
  // Persistance. Trois formes :
  //   'sqlite'                          → mémoire côté Nuxt (resemée à chaque reload)
  //   { dialect: 'sqlite', path: '…' }  → fichier (survit aux reloads et aux deploys)
  //   false                             → pas de db
  db: { dialect: 'sqlite', path: '.data/app.db' },

  // Topologie. Une Frond listée ici reste SCANNÉE (ses entités continuent de
  // nourrir formulaires et validation en metadata) mais n'est pas hébergée
  // localement : les appels voyagent en JSON-RPC vers le process qui l'héberge.
  // Commenter la ligne → in-process.
  // remotes: { blog: 'http://127.0.0.1:4100' },

  // Auth (optionnel) — better-auth derrière une fine couche de traduction.
  auth: betterAuth({
    user: User,                             // votre entité qui étend AuthUser
    secret: process.env.AUTH_SECRET!,       // 32+ caractères
    baseUrl: process.env.SITE_URL ?? 'http://localhost:3000',
    basePath: '/auth',                      // monte /auth/** (sign-in, sign-up, sign-out…)
    trustedOrigins: ['http://localhost:3000'],
    sessionTtl: 30 * 24 * 60 * 60 * 1000,
    providers: {
      credential: { minPasswordLength: 8, autoSignIn: true },
      // google: { clientId: …, clientSecret: … },
    },
  }),
});

Les tables dérivent de vos entités. Au boot, l'auto-DDL SQLite crée les tables manquantes et ajoute les colonnes absentes. Renommages, suppressions et changements de type demandent une migration explicite. Les seeds tournent après cette synchronisation et appellent les opérations avec leur validation habituelle.

Lancer

pnpm install
pnpm dev          # scan → synchronise le schéma additif → seed → sert sur :3000

Vérifiez dans le log de boot le nombre de Fronds détectées :

INF [boot:app] scanned 2 frond(s) in 467ms
INF [boot] ready in 483ms — 2 frond(s) + auth (/auth)

Suite : Entités — le vocabulaire des champs et les quatre axes.

Construit avec Fougere — ce site tourne sur le framework qu'il documente.