diff --git a/docs/DEV.md b/docs/DEV.md index 4c07cfc..4813178 100644 --- a/docs/DEV.md +++ b/docs/DEV.md @@ -60,14 +60,11 @@ Ces suffixes ne sont pas cosmétiques : **le build les utilise comme source de v ### Source de vérité -`tsup.config.js` dérive ses entrées de deux sources : +`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. -1. `package.json#exports` — pour tous les points d'entrée publics. -2. Un glob récursif `src/**/*.{server,client}.js` — pour le wiring interne (pages, widgets) qui doit rester en module séparé sans être public. - -**Ajouter un module public = éditer seulement `package.json#exports`.** La liste `external` et la liste `entry` sont régénérées au prochain build. Il n'y a plus trois listes à synchroniser. - -Les self-imports `@zen/core/*` sont générés automatiquement à partir des clés de `exports`. +- **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 avec `bundle: true` dans un barrel 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`. --- diff --git a/src/core/email/templates/BaseLayout.jsx b/src/core/email/templates/BaseLayout.js similarity index 100% rename from src/core/email/templates/BaseLayout.jsx rename to src/core/email/templates/BaseLayout.js diff --git a/src/core/email/templates/index.js b/src/core/email/templates/index.js index 29375db..463a003 100644 --- a/src/core/email/templates/index.js +++ b/src/core/email/templates/index.js @@ -1 +1 @@ -export { BaseLayout } from './BaseLayout.jsx'; +export { BaseLayout } from './BaseLayout.js'; diff --git a/src/features/auth/email.js b/src/features/auth/email.js index 32e1ed9..a2a94d9 100644 --- a/src/features/auth/email.js +++ b/src/features/auth/email.js @@ -1,9 +1,9 @@ import { render } from '@react-email/components'; import { fail, info } from '@zen/core/shared/logger'; import { sendEmail } from '@zen/core/email'; -import { VerificationEmail } from './templates/VerificationEmail.jsx'; -import { PasswordResetEmail } from './templates/PasswordResetEmail.jsx'; -import { PasswordChangedEmail } from './templates/PasswordChangedEmail.jsx'; +import { VerificationEmail } from './templates/VerificationEmail.js'; +import { PasswordResetEmail } from './templates/PasswordResetEmail.js'; +import { PasswordChangedEmail } from './templates/PasswordChangedEmail.js'; export { createEmailVerification, verifyEmailToken, createPasswordReset, verifyResetToken, deleteResetToken } from '../../core/users/verifications.js'; diff --git a/src/features/auth/templates/PasswordChangedEmail.jsx b/src/features/auth/templates/PasswordChangedEmail.js similarity index 100% rename from src/features/auth/templates/PasswordChangedEmail.jsx rename to src/features/auth/templates/PasswordChangedEmail.js diff --git a/src/features/auth/templates/PasswordResetEmail.jsx b/src/features/auth/templates/PasswordResetEmail.js similarity index 100% rename from src/features/auth/templates/PasswordResetEmail.jsx rename to src/features/auth/templates/PasswordResetEmail.js diff --git a/src/features/auth/templates/VerificationEmail.jsx b/src/features/auth/templates/VerificationEmail.js similarity index 100% rename from src/features/auth/templates/VerificationEmail.jsx rename to src/features/auth/templates/VerificationEmail.js diff --git a/tsup.config.js b/tsup.config.js index c521495..c1954ce 100644 --- a/tsup.config.js +++ b/tsup.config.js @@ -4,34 +4,37 @@ import { join } from 'node:path'; const pkg = JSON.parse(readFileSync('./package.json', 'utf8')); -// Source de vérité #1 : package.json#exports. Donne la liste des points -// d'entrée publics et la liste des self-imports à marquer external. -const exportEntries = Object.values(pkg.exports) - .map(e => e.import).filter(Boolean) - .map(p => p.replace('./dist/', 'src/')); - +// Les self-imports @zen/core/* restent toujours externes — ils pointent vers +// d'autres points d'entrée du même package, jamais vers des fichiers internes. const selfImports = Object.keys(pkg.exports) .filter(k => k !== '.' && !k.endsWith('.css')) .map(k => '@zen/core' + k.slice(1)); -// Source de vérité #2 : les fichiers *.server.js et *.client.js sous src/. -// Convention : un tel fichier est *toujours* un point d'entrée non-bundlé — -// soit il fait partie de l'API publique (listé dans exports), soit c'est un -// wiring interne (pages, widgets) qui doit rester un module séparé pour -// préserver les frontières RSC / 'use client'. +// Tous les fichiers source .js et .jsx sont compilés en module standalone +// (bundle: false). Chaque fichier est transpilé individuellement ; les imports +// relatifs sont préservés tels quels dans le dist. +// +// Pourquoi bundle: false pour tout ? +// - Les fichiers *.server.js et *.client.js doivent rester des modules séparés +// pour que Next.js respecte les frontières RSC / 'use client'. +// - Les modules de registre (registry.js, etc.) sont des singletons : si un +// barrel comme index.js bundlait registry.js avec bundle: true, il en ferait +// une copie inline distincte de la version importée en relatif par les pages +// et widgets — deux instances, zéro partage d'état. +// - Tous les fichiers doivent exister dans dist pour que les imports relatifs +// des fichiers *.server.js/*.client.js (compilés sans bundling) puissent être +// résolus à l'exécution. +// +// La liste des points d'entrée publics reste définie dans package.json#exports. +// Modifier un export public = éditer seulement package.json#exports. function walk(dir, out = []) { for (const name of readdirSync(dir)) { const full = join(dir, name); if (statSync(full).isDirectory()) walk(full, out); - else if (/\.(server|client)\.js$/.test(name)) out.push(full); + else if (/\.(js|jsx)$/.test(name)) out.push(full); } return out; } -const boundaryFiles = walk('src'); - -// Dédup : un chemin déclaré dans exports ET détecté par la glob ne devient -// pas deux entrées. -const allEntries = [...new Set([...exportEntries, ...boundaryFiles])]; const SHARED_EXTERNALS = [ 'react', 'react-dom', 'next', @@ -41,42 +44,18 @@ const SHARED_EXTERNALS = [ ...selfImports, ]; -const unbundled = allEntries.filter(e => /\.(server|client)\.js$/.test(e)); -const bundled = allEntries.filter(e => !/\.(server|client)\.js$/.test(e)); - -const esbuildBase = (o) => { - o.loader = { '.js': 'jsx', '.jsx': 'jsx' }; - o.jsx = 'automatic'; -}; - -export default defineConfig([ - { - entry: bundled, - format: ['esm'], - dts: false, - splitting: false, - sourcemap: false, - clean: true, - bundle: true, - external: SHARED_EXTERNALS, - esbuildOptions(o) { - esbuildBase(o); - o.platform = 'neutral'; - o.legalComments = 'inline'; - }, +export default defineConfig({ + entry: walk('src'), + format: ['esm'], + dts: false, + splitting: false, + sourcemap: false, + clean: true, + bundle: false, + external: SHARED_EXTERNALS, + esbuildOptions(o) { + o.loader = { '.js': 'jsx', '.jsx': 'jsx' }; + o.jsx = 'automatic'; + o.outbase = 'src'; }, - { - entry: unbundled, - format: ['esm'], - dts: false, - splitting: false, - sourcemap: false, - clean: false, - bundle: false, - external: SHARED_EXTERNALS, - esbuildOptions(o) { - esbuildBase(o); - o.outbase = 'src'; - }, - }, -]); +});