diff --git a/docs/MODULES.md b/docs/MODULES.md index f7807c3..d8870ac 100644 --- a/docs/MODULES.md +++ b/docs/MODULES.md @@ -30,7 +30,7 @@ Pour chaque module trouvé, le core vérifie qu'il exporte les bons symboles, pu Le point d'entrée du package (`main` ou `exports["."]`) doit exporter : ```js -// @zen/module-blog/index.js +// @zen/module-blog/src/index.js (compilé vers dist/index.js par tsup) export const manifest = { name: '@zen/module-blog', @@ -221,38 +221,70 @@ Toute variable requise par le module doit être déclarée dans `manifest.envVar ``` @zen/module-blog/ -├── package.json # name: "@zen/module-blog", main: "./index.js" +├── package.json # name: "@zen/module-blog", main: "./dist/index.js" +├── tsup.config.js # build avec bundle: false, loader JSX, outbase: 'src' ├── README.md # documente les env vars et la configuration -├── index.js # exporte manifest, register, createTables, dropTables -└── src/ - ├── db.server.js # createTables/dropTables - ├── register-server.js # imports déclencheurs (chargé par register()) - ├── api.server.js # routes API (registerApiRoutes) - ├── admin/ - │ ├── BlogAdminPage.client.js # registerPage + composant - │ └── widgets/... # registerWidgetFetcher + registerWidget - └── public/ - └── BlogPublicPage.js # registerPublicModulePage +├── src/ +│ ├── index.js # exporte manifest, register, createTables, dropTables +│ ├── db.server.js # createTables/dropTables +│ ├── register-server.js # imports déclencheurs (chargé par register()) +│ ├── api.server.js # routes API (registerApiRoutes) +│ ├── admin/ +│ │ ├── BlogAdminPage.client.js # registerPage + composant +│ │ └── widgets/... # registerWidgetFetcher + registerWidget +│ └── public/ +│ └── BlogPublicPage.js # registerPublicModulePage +└── dist/ # généré par `npm run build` — c'est ce qui est publié ``` -Tout le code du module vit dans `src/`. Seul `index.js` reste à la racine — c'est le point d'entrée public lu par le core via `main: "./index.js"`. Il importe depuis `./src/` : - -```js -// index.js -export async function register() { - await import('./src/register-server.js'); -} -export { createTables, dropTables } from './src/db.server.js'; -``` - -Le champ `files` dans `package.json` publie uniquement `index.js`, `src/`, `README.md` et `LICENSE` : +Le champ `files` dans `package.json` publie **uniquement** `dist/`, `README.md` et `LICENSE` : ```json -"files": ["index.js", "src", "README.md", "LICENSE"] +{ + "main": "./dist/index.js", + "exports": { ".": { "import": "./dist/index.js" } }, + "files": ["dist", "README.md", "LICENSE"], + "scripts": { + "build": "tsup", + "prepublishOnly": "npm run build" + } +} ``` --- +## Build obligatoire avant publish + +**Tout module `@zen/module-*` doit être pré-compilé avant publication.** Les fichiers du module ne doivent jamais contenir de JSX brut au runtime — le JSX doit être transformé en `React.createElement` (ou équivalent) par le build du module lui-même. + +**Pourquoi.** Le core découvre les modules via `import(/* turbopackIgnore */ name)` dans [`discover.server.js`](../src/core/modules/discover.server.js) ; le commentaire `turbopackIgnore` est nécessaire pour empêcher Turbopack/Webpack de tenter de bundler un nom de package dynamique. Conséquence : tout l'arbre d'imports transitifs du module est résolu et exécuté par Node natif, **sans** transformation JSX. Un fichier `.client.js` qui contient `` au runtime fait planter `register()` avec `Unexpected token '<'`. + +Le module doit donc utiliser le même setup que `@zen/core` : un build `tsup` avec `bundle: false`, qui transforme JSX → JS standard tout en préservant la structure de fichiers (un fichier d'entrée → un fichier de sortie). Le `'use client'` est conservé en haut des fichiers `.client.js` compilés, ce qui permet à Next.js de respecter les frontières RSC quand le projet consommateur bundle le module. + +### Exemple de `tsup.config.js` minimal + +```js +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/**/*.js', 'src/**/*.jsx'], + format: ['esm'], + outDir: 'dist', + outbase: 'src', + bundle: false, + splitting: false, + clean: true, + loader: { '.js': 'jsx' }, + jsx: 'automatic', +}); +``` + +### Vérifier qu'un module est correctement compilé + +Avant `npm publish`, ouvrir `dist/` et confirmer qu'aucun fichier ne contient de syntaxe JSX brute (chercher `<` suivi d'une majuscule). Tous les composants doivent apparaître sous forme d'appels `jsx(...)` ou `React.createElement(...)`. + +--- + ## Cycle de vie complet | Étape | Côté core | Côté module |