import { defineConfig } from 'tsup'; import { readFileSync, readdirSync, statSync } from 'node:fs'; 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/')); 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'. 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); } 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', 'pg', 'dotenv', 'dotenv/config', 'resend', 'node-cron', '@react-email/components', '@aws-sdk/client-s3', '@aws-sdk/s3-request-presigner', 'readline', 'crypto', 'url', 'fs', 'path', 'net', 'dns', 'tls', ...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'; }, }, { entry: unbundled, format: ['esm'], dts: false, splitting: false, sourcemap: false, clean: false, bundle: false, external: SHARED_EXTERNALS, esbuildOptions(o) { esbuildBase(o); o.outbase = 'src'; }, }, ]);