docs(modules): update module structure to reflect tsup build workflow

- fix entry point path from `index.js` to `src/index.js` compiled to `dist/index.js`
- update file tree to show `src/` as source root and `dist/` as published output
- update `package.json` example with correct `main`, `exports`, `files`, and build scripts
- add explanation of mandatory pre-build step before publish
- document why JSX must be compiled (turbopackIgnore + Node native resolution)
- add minimal `tsup.config.js` example with `bundle: false` and JSX loader
- add verification step to check compiled output before publishing
This commit is contained in:
2026-04-25 13:13:07 -04:00
parent 65e833d020
commit d6befcfa91
+56 -24
View File
@@ -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 : Le point d'entrée du package (`main` ou `exports["."]`) doit exporter :
```js ```js
// @zen/module-blog/index.js // @zen/module-blog/src/index.js (compilé vers dist/index.js par tsup)
export const manifest = { export const manifest = {
name: '@zen/module-blog', 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/ @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 ├── README.md # documente les env vars et la configuration
├── index.js # exporte manifest, register, createTables, dropTables ├── src/
└── src/ │ ├── index.js # exporte manifest, register, createTables, dropTables
├── db.server.js # createTables/dropTables ├── db.server.js # createTables/dropTables
├── register-server.js # imports déclencheurs (chargé par register()) ├── register-server.js # imports déclencheurs (chargé par register())
├── api.server.js # routes API (registerApiRoutes) ├── api.server.js # routes API (registerApiRoutes)
├── admin/ ├── admin/
│ ├── BlogAdminPage.client.js # registerPage + composant │ ├── BlogAdminPage.client.js # registerPage + composant
│ └── widgets/... # registerWidgetFetcher + registerWidget │ └── widgets/... # registerWidgetFetcher + registerWidget
└── public/ └── public/
└── BlogPublicPage.js # registerPublicModulePage └── 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/` : Le champ `files` dans `package.json` publie **uniquement** `dist/`, `README.md` et `LICENSE` :
```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` :
```json ```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 `<MyComponent />` 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 ## Cycle de vie complet
| Étape | Côté core | Côté module | | Étape | Côté core | Côté module |