@lastbrain/app 0.1.25 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/app-shell/(public)/page.d.ts.map +1 -1
  2. package/dist/index.d.ts +4 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +4 -0
  5. package/dist/layouts/AdminLayoutWithSidebar.d.ts +8 -0
  6. package/dist/layouts/AdminLayoutWithSidebar.d.ts.map +1 -0
  7. package/dist/layouts/AdminLayoutWithSidebar.js +9 -0
  8. package/dist/layouts/AuthLayoutWithSidebar.d.ts +8 -0
  9. package/dist/layouts/AuthLayoutWithSidebar.d.ts.map +1 -0
  10. package/dist/layouts/AuthLayoutWithSidebar.js +9 -0
  11. package/dist/scripts/db-init.js +1 -1
  12. package/dist/scripts/dev-sync.js +21 -10
  13. package/dist/scripts/init-app.d.ts.map +1 -1
  14. package/dist/scripts/init-app.js +114 -12
  15. package/dist/scripts/module-add.d.ts.map +1 -1
  16. package/dist/scripts/module-add.js +19 -6
  17. package/dist/scripts/module-build.d.ts.map +1 -1
  18. package/dist/scripts/module-build.js +285 -30
  19. package/dist/scripts/module-create.d.ts.map +1 -1
  20. package/dist/scripts/module-create.js +25 -15
  21. package/dist/scripts/module-remove.d.ts.map +1 -1
  22. package/dist/scripts/module-remove.js +24 -11
  23. package/dist/scripts/script-runner.js +1 -1
  24. package/dist/styles.css +1 -1
  25. package/dist/templates/DefaultDoc.js +1 -7
  26. package/dist/templates/components/AppAside.d.ts +6 -0
  27. package/dist/templates/components/AppAside.d.ts.map +1 -0
  28. package/dist/templates/components/AppAside.js +9 -0
  29. package/dist/templates/layouts/admin-layout.d.ts +4 -0
  30. package/dist/templates/layouts/admin-layout.d.ts.map +1 -0
  31. package/dist/templates/layouts/admin-layout.js +6 -0
  32. package/dist/templates/layouts/auth-layout.d.ts +4 -0
  33. package/dist/templates/layouts/auth-layout.d.ts.map +1 -0
  34. package/dist/templates/layouts/auth-layout.js +6 -0
  35. package/package.json +2 -1
  36. package/src/app-shell/(public)/page.tsx +6 -2
  37. package/src/auth/useAuthSession.ts +1 -1
  38. package/src/cli.ts +1 -1
  39. package/src/index.ts +6 -0
  40. package/src/layouts/AdminLayoutWithSidebar.tsx +35 -0
  41. package/src/layouts/AppProviders.tsx +1 -1
  42. package/src/layouts/AuthLayoutWithSidebar.tsx +35 -0
  43. package/src/scripts/db-init.ts +12 -7
  44. package/src/scripts/db-migrations-sync.ts +3 -3
  45. package/src/scripts/dev-sync.ts +49 -18
  46. package/src/scripts/init-app.ts +235 -65
  47. package/src/scripts/module-add.ts +50 -22
  48. package/src/scripts/module-build.ts +393 -88
  49. package/src/scripts/module-create.ts +85 -49
  50. package/src/scripts/module-remove.ts +116 -57
  51. package/src/scripts/readme-build.ts +2 -2
  52. package/src/scripts/script-runner.ts +3 -3
  53. package/src/templates/AuthGuidePage.tsx +1 -1
  54. package/src/templates/DefaultDoc.tsx +7 -7
@@ -42,7 +42,7 @@ async function loadModuleConfigs() {
42
42
  break;
43
43
  }
44
44
  }
45
- catch (err) {
45
+ catch {
46
46
  // Essayer le nom suivant
47
47
  }
48
48
  }
@@ -113,7 +113,28 @@ function toPascalCase(value) {
113
113
  .join("");
114
114
  }
115
115
  function buildPage(moduleConfig, page) {
116
- const segments = page.path.replace(/^\/+/, "").split("/").filter(Boolean);
116
+ // Extraire le préfixe du module (ex: @lastbrain/module-auth -> auth)
117
+ const modulePrefix = moduleConfig.moduleName
118
+ .replace(/^@lastbrain\/module-/, '')
119
+ .toLowerCase();
120
+ console.log(`🔄 Building page for module ${modulePrefix}: ${page.path}`);
121
+ // Ajouter le préfixe du module au path pour les sections admin et auth,
122
+ // MAIS seulement quand la section ne correspond PAS au module lui-même
123
+ let effectivePath = page.path;
124
+ if (page.section === 'admin' || (page.section === 'auth' && modulePrefix !== 'auth')) {
125
+ // Éviter les doublons si le préfixe est déjà présent
126
+ if (!page.path.startsWith(`/${modulePrefix}/`)) {
127
+ effectivePath = `/${modulePrefix}${page.path}`;
128
+ console.log(`📂 Added module prefix: ${page.path} -> ${effectivePath}`);
129
+ }
130
+ else {
131
+ console.log(`✅ Module prefix already present: ${page.path}`);
132
+ }
133
+ }
134
+ else if (page.section === 'auth' && modulePrefix === 'auth') {
135
+ console.log(`🏠 Auth module in auth section, no prefix needed: ${page.path}`);
136
+ }
137
+ const segments = effectivePath.replace(/^\/+/, "").split("/").filter(Boolean);
117
138
  const sectionPath = sectionDirectoryMap[page.section] ?? ["(public)"];
118
139
  // Générer le layout de section si nécessaire
119
140
  ensureSectionLayout(sectionPath);
@@ -125,14 +146,17 @@ function buildPage(moduleConfig, page) {
125
146
  : "Root";
126
147
  const wrapperName = `${page.componentExport}${wrapperSuffix}Route`;
127
148
  // Vérifier si la route a des paramètres dynamiques (segments avec [])
128
- const hasDynamicParams = segments.some(seg => seg.startsWith('[') && seg.endsWith(']'));
149
+ const hasDynamicParams = segments.some((seg) => seg.startsWith("[") && seg.endsWith("]"));
129
150
  // Pour les pages publiques (signin, signup, etc.), utiliser dynamic import sans SSR
130
151
  // pour éviter les erreurs d'hydratation avec les IDs HeroUI/React Aria
131
152
  const isPublicAuthPage = page.section === "public" &&
132
- (page.path.includes("signin") || page.path.includes("signup") || page.path.includes("reset-password"));
153
+ (page.path.includes("signin") ||
154
+ page.path.includes("signup") ||
155
+ page.path.includes("reset-password"));
133
156
  let content;
134
157
  if (isPublicAuthPage) {
135
- content = `"use client";
158
+ content = `// GENERATED BY LASTBRAIN MODULE BUILD
159
+ "use client";
136
160
 
137
161
  import dynamic from "next/dynamic";
138
162
 
@@ -141,16 +165,17 @@ const ${page.componentExport} = dynamic(
141
165
  { ssr: false }
142
166
  );
143
167
 
144
- export default function ${wrapperName}${hasDynamicParams ? '(props: any)' : '()'} {
145
- return <${page.componentExport} ${hasDynamicParams ? '{...props}' : ''} />;
168
+ export default function ${wrapperName}${hasDynamicParams ? "(props: any)" : "()"} {
169
+ return <${page.componentExport} ${hasDynamicParams ? "{...props}" : ""} />;
146
170
  }
147
171
  `;
148
172
  }
149
173
  else {
150
- content = `import { ${page.componentExport} } from "${moduleConfig.moduleName}";
174
+ content = `// GENERATED BY LASTBRAIN MODULE BUILD
175
+ import { ${page.componentExport} } from "${moduleConfig.moduleName}";
151
176
 
152
- export default function ${wrapperName}${hasDynamicParams ? '(props: any)' : '()'} {
153
- return <${page.componentExport} ${hasDynamicParams ? '{...props}' : ''} />;
177
+ export default function ${wrapperName}${hasDynamicParams ? "(props: any)" : "()"} {
178
+ return <${page.componentExport} ${hasDynamicParams ? "{...props}" : ""} />;
154
179
  }
155
180
  `;
156
181
  }
@@ -158,7 +183,7 @@ export default function ${wrapperName}${hasDynamicParams ? '(props: any)' : '()'
158
183
  console.log(`⭐ Generated page: ${filePath}`);
159
184
  const entry = {
160
185
  label: `Module:${moduleConfig.moduleName} ${page.componentExport}`,
161
- path: page.path,
186
+ path: effectivePath,
162
187
  module: moduleConfig.moduleName,
163
188
  section: page.section,
164
189
  };
@@ -169,18 +194,6 @@ export default function ${wrapperName}${hasDynamicParams ? '(props: any)' : '()'
169
194
  navigation[page.section]?.push(entry);
170
195
  }
171
196
  }
172
- function buildApi(moduleConfig, api) {
173
- const segments = api.path.replace(/^\/+/, "").split("/").filter(Boolean);
174
- const sanitizedSegments = segments[0] === "api" ? segments.slice(1) : segments;
175
- const routeDir = path.join(appDirectory, "api", ...sanitizedSegments);
176
- const filePath = path.join(routeDir, "route.ts");
177
- ensureDirectory(routeDir);
178
- const handler = `${moduleConfig.moduleName}/${api.entryPoint}`;
179
- const content = `export { ${api.handlerExport} } from "${handler}";
180
- `;
181
- fs.writeFileSync(filePath, content);
182
- console.log(`🔌 Generated API route: ${filePath}`);
183
- }
184
197
  function dumpNavigation() {
185
198
  const navPath = path.join(appDirectory, "navigation.generated.ts");
186
199
  const content = `export type MenuEntry = { label: string; path: string; module: string; section: string };
@@ -354,8 +367,10 @@ function generateDocsPage(moduleConfigs) {
354
367
  const moduleId = moduleName.replace("@lastbrain/module-", "");
355
368
  const docComponentName = `${toPascalCase(moduleId)}ModuleDoc`;
356
369
  // Trouver la config du module pour obtenir la description
357
- const moduleConfig = moduleConfigs.find(mc => mc.moduleName === moduleName);
358
- const description = moduleConfig ? getModuleDescription(moduleConfig) : "Module non configuré";
370
+ const moduleConfig = moduleConfigs.find((mc) => mc.moduleName === moduleName);
371
+ const description = moduleConfig
372
+ ? getModuleDescription(moduleConfig)
373
+ : "Module non configuré";
359
374
  // Importer le composant Doc seulement pour les modules actifs
360
375
  if (moduleEntry.active) {
361
376
  docImports.push(`import { ${docComponentName} } from "${moduleName}";`);
@@ -371,18 +386,18 @@ function generateDocsPage(moduleConfigs) {
371
386
  id: "${config.id}",
372
387
  name: "${config.name}",
373
388
  description: "${config.description}",
374
- ${config.active ? `content: <${config.component} />,` : 'content: null,'}
389
+ ${config.active ? `content: <${config.component} />,` : "content: null,"}
375
390
  available: ${config.active},
376
391
  }`);
377
392
  });
378
393
  const docsContent = `// Auto-generated docs page with module documentation
379
394
  import React from "react";
380
395
  import { DocPage } from "@lastbrain/app";
381
- ${docImports.join('\n')}
396
+ ${docImports.join("\n")}
382
397
 
383
398
  export default function DocsPage() {
384
399
  const modules = [
385
- ${moduleConfigurations.join(',\n')}
400
+ ${moduleConfigurations.join(",\n")}
386
401
  ];
387
402
 
388
403
  return <DocPage modules={modules} />;
@@ -391,6 +406,33 @@ ${moduleConfigurations.join(',\n')}
391
406
  fs.writeFileSync(docsPagePath, docsContent);
392
407
  console.log(`📚 Generated docs page: ${docsPagePath}`);
393
408
  }
409
+ function buildGroupedApi(apis, routePath) {
410
+ const segments = routePath.replace(/^\/+/, "").split("/").filter(Boolean);
411
+ const sanitizedSegments = segments[0] === "api" ? segments.slice(1) : segments;
412
+ const routeDir = path.join(appDirectory, "api", ...sanitizedSegments);
413
+ const filePath = path.join(routeDir, "route.ts");
414
+ ensureDirectory(routeDir);
415
+ // Grouper par module/entryPoint pour créer les exports
416
+ const exportsBySource = new Map();
417
+ apis.forEach(({ moduleConfig, api }) => {
418
+ const handler = `${moduleConfig.moduleName}/${api.entryPoint}`;
419
+ if (!exportsBySource.has(handler)) {
420
+ exportsBySource.set(handler, []);
421
+ }
422
+ exportsBySource.get(handler).push(api.handlerExport);
423
+ });
424
+ // Générer les exports - un export statement par source
425
+ const exportStatements = [];
426
+ exportsBySource.forEach((exports, source) => {
427
+ exportStatements.push(`export { ${exports.join(", ")} } from "${source}";`);
428
+ });
429
+ const content = exportStatements.join("\n") + "\n";
430
+ // Ajouter le marqueur de génération au début
431
+ const contentWithMarker = `// GENERATED BY LASTBRAIN MODULE BUILD
432
+ ${content}`;
433
+ fs.writeFileSync(filePath, contentWithMarker);
434
+ console.log(`🔌 Generated API route: ${filePath}`);
435
+ }
394
436
  function getModuleDescription(moduleConfig) {
395
437
  // Essayer de déduire la description depuis les pages ou retourner une description par défaut
396
438
  if (moduleConfig.pages.length > 0) {
@@ -398,16 +440,229 @@ function getModuleDescription(moduleConfig) {
398
440
  }
399
441
  return "Module documentation";
400
442
  }
443
+ function cleanGeneratedFiles() {
444
+ const generatedComment = "// GENERATED BY LASTBRAIN MODULE BUILD";
445
+ // Fichiers de base à préserver (paths relatifs exacts)
446
+ const protectedFiles = new Set([
447
+ // API de base
448
+ "api/storage",
449
+ // Layouts de base
450
+ "layout.tsx",
451
+ "not-found.tsx",
452
+ "page.tsx", // Page racine seulement
453
+ "admin/page.tsx", // Page admin racine
454
+ "admin/layout.tsx", // Layout admin racine
455
+ "docs/page.tsx", // Page docs générée
456
+ // Middleware et autres fichiers core
457
+ "middleware.ts",
458
+ // Dossiers de lib et config
459
+ "lib",
460
+ "config"
461
+ ]);
462
+ // Fonction pour vérifier si un chemin est protégé
463
+ const isProtected = (filePath) => {
464
+ const relativePath = path.relative(appDirectory, filePath);
465
+ // Protection exacte pour certains fichiers
466
+ if (protectedFiles.has(relativePath)) {
467
+ return true;
468
+ }
469
+ // Protection par préfixe pour les dossiers
470
+ return Array.from(protectedFiles).some(protectedPath => (protectedPath.endsWith('/') || ['lib', 'config', 'api/storage'].includes(protectedPath)) &&
471
+ relativePath.startsWith(protectedPath));
472
+ };
473
+ // Fonction pour nettoyer récursivement un dossier
474
+ const cleanDirectory = (dirPath) => {
475
+ if (!fs.existsSync(dirPath))
476
+ return;
477
+ const items = fs.readdirSync(dirPath);
478
+ for (const item of items) {
479
+ const itemPath = path.join(dirPath, item);
480
+ const stat = fs.statSync(itemPath);
481
+ if (stat.isDirectory()) {
482
+ // Nettoyer récursivement le sous-dossier
483
+ cleanDirectory(itemPath);
484
+ // Supprimer le dossier s'il est vide et non protégé
485
+ try {
486
+ if (!isProtected(itemPath) && fs.readdirSync(itemPath).length === 0) {
487
+ fs.rmdirSync(itemPath);
488
+ console.log(`🗑️ Removed empty directory: ${itemPath}`);
489
+ }
490
+ }
491
+ catch (e) {
492
+ // Ignorer les erreurs de suppression de dossiers
493
+ }
494
+ }
495
+ else if (item.endsWith('.tsx') || item.endsWith('.ts')) {
496
+ // Vérifier si c'est un fichier généré
497
+ if (!isProtected(itemPath)) {
498
+ try {
499
+ const content = fs.readFileSync(itemPath, 'utf-8');
500
+ // Supprimer les fichiers générés ou les wrapper simples de modules
501
+ if (content.includes(generatedComment) ||
502
+ content.includes('from "@lastbrain/module-') ||
503
+ content.includes('export {') && content.includes('} from "@lastbrain/module-')) {
504
+ fs.unlinkSync(itemPath);
505
+ console.log(`🗑️ Cleaned generated file: ${itemPath}`);
506
+ }
507
+ }
508
+ catch (e) {
509
+ // Ignorer les erreurs de lecture/suppression
510
+ }
511
+ }
512
+ else {
513
+ console.log(`🔒 Protected file skipped: ${itemPath}`);
514
+ }
515
+ }
516
+ }
517
+ };
518
+ // Nettoyer les dossiers de sections
519
+ const sectionsToClean = ['(public)', 'auth', 'admin', 'api'];
520
+ sectionsToClean.forEach(section => {
521
+ const sectionPath = path.join(appDirectory, section);
522
+ if (fs.existsSync(sectionPath)) {
523
+ cleanDirectory(sectionPath);
524
+ }
525
+ });
526
+ // Nettoyer les fichiers générés à la racine
527
+ const rootFiles = ['navigation.generated.ts'];
528
+ rootFiles.forEach(file => {
529
+ const filePath = path.join(appDirectory, file);
530
+ if (fs.existsSync(filePath)) {
531
+ fs.unlinkSync(filePath);
532
+ console.log(`🗑️ Cleaned root file: ${filePath}`);
533
+ }
534
+ });
535
+ console.log("🧹 Cleanup completed");
536
+ }
537
+ function generateAppAside() {
538
+ const targetPath = path.join(appDirectory, "components", "AppAside.tsx");
539
+ // Ne pas écraser si le fichier existe déjà
540
+ if (fs.existsSync(targetPath)) {
541
+ console.log(`⏭️ AppAside already exists, skipping: ${targetPath}`);
542
+ return;
543
+ }
544
+ const templateContent = `"use client";
545
+
546
+ import { AppAside as UIAppAside } from "@lastbrain/app";
547
+ import { useAuthSession } from "@lastbrain/app";
548
+ import { menuConfig } from "../../config/menu";
549
+
550
+ interface AppAsideProps {
551
+ className?: string;
552
+ isVisible?: boolean;
553
+ }
554
+
555
+ export function AppAside({ className = "", isVisible = true }: AppAsideProps) {
556
+ const { isSuperAdmin } = useAuthSession();
557
+
558
+ return (
559
+ <UIAppAside
560
+ className={className}
561
+ menuConfig={menuConfig}
562
+ isSuperAdmin={isSuperAdmin}
563
+ isVisible={isVisible}
564
+ />
565
+ );
566
+ }`;
567
+ try {
568
+ ensureDirectory(path.dirname(targetPath));
569
+ fs.writeFileSync(targetPath, templateContent, "utf-8");
570
+ console.log(`✅ Generated AppAside component: ${targetPath}`);
571
+ }
572
+ catch (error) {
573
+ console.error(`❌ Error generating AppAside component: ${error}`);
574
+ }
575
+ }
576
+ function generateLayouts() {
577
+ // Générer layout auth avec sidebar
578
+ const authLayoutPath = path.join(appDirectory, "auth", "layout.tsx");
579
+ if (!fs.existsSync(authLayoutPath)) {
580
+ const authLayoutContent = `import { AuthLayoutWithSidebar } from "@lastbrain/app";
581
+ import { menuConfig } from "../../config/menu";
582
+
583
+ export default function SectionLayout({
584
+ children,
585
+ }: {
586
+ children: React.ReactNode;
587
+ }) {
588
+ return (
589
+ <AuthLayoutWithSidebar menuConfig={menuConfig}>
590
+ {children}
591
+ </AuthLayoutWithSidebar>
592
+ );
593
+ }`;
594
+ try {
595
+ ensureDirectory(path.dirname(authLayoutPath));
596
+ fs.writeFileSync(authLayoutPath, authLayoutContent, "utf-8");
597
+ console.log(`✅ Generated auth layout with sidebar: ${authLayoutPath}`);
598
+ }
599
+ catch (error) {
600
+ console.error(`❌ Error generating auth layout: ${error}`);
601
+ }
602
+ }
603
+ else {
604
+ console.log(`⏭️ Auth layout already exists, skipping: ${authLayoutPath}`);
605
+ }
606
+ // Générer layout admin avec sidebar
607
+ const adminLayoutPath = path.join(appDirectory, "admin", "layout.tsx");
608
+ if (!fs.existsSync(adminLayoutPath)) {
609
+ const adminLayoutContent = `import { AdminLayoutWithSidebar } from "@lastbrain/app";
610
+ import { menuConfig } from "../../config/menu";
611
+
612
+ export default function AdminLayout({
613
+ children,
614
+ }: {
615
+ children: React.ReactNode;
616
+ }) {
617
+ return (
618
+ <AdminLayoutWithSidebar menuConfig={menuConfig}>
619
+ {children}
620
+ </AdminLayoutWithSidebar>
621
+ );
622
+ }`;
623
+ try {
624
+ ensureDirectory(path.dirname(adminLayoutPath));
625
+ fs.writeFileSync(adminLayoutPath, adminLayoutContent, "utf-8");
626
+ console.log(`✅ Generated admin layout with sidebar: ${adminLayoutPath}`);
627
+ }
628
+ catch (error) {
629
+ console.error(`❌ Error generating admin layout: ${error}`);
630
+ }
631
+ }
632
+ else {
633
+ console.log(`⏭️ Admin layout already exists, skipping: ${adminLayoutPath}`);
634
+ }
635
+ }
401
636
  export async function runModuleBuild() {
402
637
  ensureDirectory(appDirectory);
638
+ // Nettoyer les fichiers générés précédemment
639
+ console.log("🧹 Cleaning previously generated files...");
640
+ cleanGeneratedFiles();
403
641
  const moduleConfigs = await loadModuleConfigs();
642
+ console.log(`🔍 Loaded ${moduleConfigs.length} module configurations`);
643
+ // Générer les pages
404
644
  moduleConfigs.forEach((moduleConfig) => {
645
+ console.log(`📦 Processing module: ${moduleConfig.moduleName} with ${moduleConfig.pages.length} pages`);
405
646
  moduleConfig.pages.forEach((page) => buildPage(moduleConfig, page));
406
- moduleConfig.apis.forEach((api) => buildApi(moduleConfig, api));
647
+ });
648
+ // Grouper les APIs par chemin pour éviter les écrasements de fichier
649
+ const apisByPath = new Map();
650
+ moduleConfigs.forEach((moduleConfig) => {
651
+ moduleConfig.apis.forEach((api) => {
652
+ if (!apisByPath.has(api.path)) {
653
+ apisByPath.set(api.path, []);
654
+ }
655
+ apisByPath.get(api.path).push({ moduleConfig, api });
656
+ });
657
+ });
658
+ // Générer les fichiers de route groupés
659
+ apisByPath.forEach((apis, routePath) => {
660
+ buildGroupedApi(apis, routePath);
407
661
  });
408
662
  dumpNavigation();
409
663
  generateMenuConfig(moduleConfigs);
410
664
  generateDocsPage(moduleConfigs);
665
+ generateAppAside();
666
+ generateLayouts();
411
667
  copyModuleMigrations(moduleConfigs);
412
668
  }
413
- runModuleBuild();
@@ -1 +1 @@
1
- {"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"AAirBA;;GAEG;AACH,wBAAsB,YAAY,kBAsIjC"}
1
+ {"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"AAktBA;;GAEG;AACH,wBAAsB,YAAY,kBAyIjC"}
@@ -38,7 +38,7 @@ function getLastBrainPackageVersions(rootDir) {
38
38
  ui: `^${uiPackageJson.version}`,
39
39
  };
40
40
  }
41
- catch (error) {
41
+ catch {
42
42
  console.warn(chalk.yellow("⚠️ Impossible de lire les versions des packages, utilisation des versions par défaut"));
43
43
  return {
44
44
  core: "^0.1.0",
@@ -145,7 +145,7 @@ function generateBuildConfig(config) {
145
145
  // Générer les menus par section
146
146
  const menuSections = [];
147
147
  // Menu public
148
- const publicPages = pages.filter(p => p.section === "public");
148
+ const publicPages = pages.filter((p) => p.section === "public");
149
149
  if (publicPages.length > 0) {
150
150
  const publicMenuItems = publicPages.map((page, index) => {
151
151
  const title = page.name
@@ -163,7 +163,7 @@ function generateBuildConfig(config) {
163
163
  menuSections.push({ section: "public", items: publicMenuItems });
164
164
  }
165
165
  // Menu auth
166
- const authPages = pages.filter(p => p.section === "auth");
166
+ const authPages = pages.filter((p) => p.section === "auth");
167
167
  if (authPages.length > 0) {
168
168
  const authMenuItems = authPages.map((page, index) => {
169
169
  const title = page.name
@@ -181,7 +181,7 @@ function generateBuildConfig(config) {
181
181
  menuSections.push({ section: "auth", items: authMenuItems });
182
182
  }
183
183
  // Menu admin
184
- const adminPages = pages.filter(p => p.section === "admin");
184
+ const adminPages = pages.filter((p) => p.section === "admin");
185
185
  if (adminPages.length > 0) {
186
186
  const adminMenuItems = adminPages.map((page, index) => {
187
187
  const title = page.name
@@ -202,9 +202,11 @@ function generateBuildConfig(config) {
202
202
  const menuConfig = menuSections.length > 0
203
203
  ? `,
204
204
  menu: {
205
- ${menuSections.map(({ section, items }) => ` ${section}: [
205
+ ${menuSections
206
+ .map(({ section, items }) => ` ${section}: [
206
207
  ${items.join(",\n")}
207
- ]`).join(",\n")}
208
+ ]`)
209
+ .join(",\n")}
208
210
  }`
209
211
  : "";
210
212
  return `import type { ModuleBuildConfig } from "@lastbrain/core";
@@ -315,12 +317,14 @@ const jsonResponse = (payload: unknown, status = 200) => {
315
317
  */
316
318
  export async function GET(request: Request) {
317
319
  const supabase = await getSupabaseServerClient();
318
- ${authRequired ? `
320
+ ${authRequired
321
+ ? `
319
322
  const { data: { user }, error: authError } = await supabase.auth.getUser();
320
323
  if (authError || !user) {
321
324
  return jsonResponse({ error: "Non authentifié" }, 401);
322
325
  }
323
- ` : ""}
326
+ `
327
+ : ""}
324
328
 
325
329
  const { data, error } = await supabase
326
330
  .from("${tableName}")
@@ -338,12 +342,14 @@ export async function GET(request: Request) {
338
342
  */
339
343
  export async function POST(request: Request) {
340
344
  const supabase = await getSupabaseServerClient();
341
- ${authRequired ? `
345
+ ${authRequired
346
+ ? `
342
347
  const { data: { user }, error: authError } = await supabase.auth.getUser();
343
348
  if (authError || !user) {
344
349
  return jsonResponse({ error: "Non authentifié" }, 401);
345
350
  }
346
- ` : ""}
351
+ `
352
+ : ""}
347
353
 
348
354
  const body = await request.json();
349
355
 
@@ -365,12 +371,14 @@ export async function POST(request: Request) {
365
371
  */
366
372
  export async function PUT(request: Request) {
367
373
  const supabase = await getSupabaseServerClient();
368
- ${authRequired ? `
374
+ ${authRequired
375
+ ? `
369
376
  const { data: { user }, error: authError } = await supabase.auth.getUser();
370
377
  if (authError || !user) {
371
378
  return jsonResponse({ error: "Non authentifié" }, 401);
372
379
  }
373
- ` : ""}
380
+ `
381
+ : ""}
374
382
 
375
383
  const body = await request.json();
376
384
  const { id, ...updateData } = body;
@@ -398,12 +406,14 @@ export async function PUT(request: Request) {
398
406
  */
399
407
  export async function DELETE(request: Request) {
400
408
  const supabase = await getSupabaseServerClient();
401
- ${authRequired ? `
409
+ ${authRequired
410
+ ? `
402
411
  const { data: { user }, error: authError } = await supabase.auth.getUser();
403
412
  if (authError || !user) {
404
413
  return jsonResponse({ error: "Non authentifié" }, 401);
405
414
  }
406
- ` : ""}
415
+ `
416
+ : ""}
407
417
 
408
418
  const { searchParams } = new URL(request.url);
409
419
  const id = searchParams.get("id");
@@ -429,7 +439,7 @@ export async function DELETE(request: Request) {
429
439
  * Génère le contenu d'un fichier de migration SQL
430
440
  */
431
441
  function generateMigration(tables, slug) {
432
- const timestamp = new Date()
442
+ const _timestamp = new Date()
433
443
  .toISOString()
434
444
  .replace(/[-:]/g, "")
435
445
  .split(".")[0]
@@ -1 +1 @@
1
- {"version":3,"file":"module-remove.d.ts","sourceRoot":"","sources":["../../src/scripts/module-remove.ts"],"names":[],"mappings":"AAmEA,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,iBA2SvE"}
1
+ {"version":3,"file":"module-remove.d.ts","sourceRoot":"","sources":["../../src/scripts/module-remove.ts"],"names":[],"mappings":"AAmEA,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,iBAsWvE"}
@@ -36,7 +36,7 @@ async function removeGeneratedFiles(moduleName, modulePackage, targetDir) {
36
36
  }
37
37
  }
38
38
  }
39
- catch (error) {
39
+ catch {
40
40
  // Ignorer les erreurs de lecture
41
41
  }
42
42
  }
@@ -105,10 +105,12 @@ export async function removeModule(moduleName, targetDir) {
105
105
  let migrationFiles = [];
106
106
  if (fs.existsSync(modulesConfigPath)) {
107
107
  const modulesConfig = await fs.readJson(modulesConfigPath);
108
- const moduleEntry = modulesConfig.modules.find((m) => (typeof m === 'string' ? m : m.package) === module.package);
109
- if (moduleEntry && typeof moduleEntry === 'object' && moduleEntry.migrations) {
108
+ const moduleEntry = modulesConfig.modules.find((m) => (typeof m === "string" ? m : m.package) === module.package);
109
+ if (moduleEntry &&
110
+ typeof moduleEntry === "object" &&
111
+ moduleEntry.migrations) {
110
112
  migrationFiles = moduleEntry.migrations;
111
- console.log(chalk.gray(`DEBUG: Migrations from config: ${migrationFiles.join(', ')}`));
113
+ console.log(chalk.gray(`DEBUG: Migrations from config: ${migrationFiles.join(", ")}`));
112
114
  }
113
115
  }
114
116
  // Fallback: essayer de lire depuis node_modules si disponible
@@ -119,7 +121,7 @@ export async function removeModule(moduleName, targetDir) {
119
121
  migrationFiles = fs
120
122
  .readdirSync(moduleMigrationsDir)
121
123
  .filter((f) => f.endsWith(".sql"));
122
- console.log(chalk.gray(`DEBUG: Found ${migrationFiles.length} migration files from node_modules: ${migrationFiles.join(', ')}`));
124
+ console.log(chalk.gray(`DEBUG: Found ${migrationFiles.length} migration files from node_modules: ${migrationFiles.join(", ")}`));
123
125
  }
124
126
  else {
125
127
  console.log(chalk.gray(`DEBUG: No migrations found in config or node_modules`));
@@ -170,7 +172,7 @@ export async function removeModule(moduleName, targetDir) {
170
172
  for (const downFile of downFiles) {
171
173
  const downFilePath = path.join(moduleMigrationsDownDir, downFile);
172
174
  // Extraire le timestamp de la migration (14 premiers caractères)
173
- const migrationVersion = downFile.split('_')[0]; // Prend la partie avant le premier underscore
175
+ const migrationVersion = downFile.split("_")[0]; // Prend la partie avant le premier underscore
174
176
  try {
175
177
  console.log(chalk.gray(` Exécution de ${downFile}...`));
176
178
  execSync(`psql "postgresql://postgres:postgres@127.0.0.1:54322/postgres" -f "${downFilePath}"`, {
@@ -182,7 +184,7 @@ export async function removeModule(moduleName, targetDir) {
182
184
  const deleteQuery = `DELETE FROM supabase_migrations.schema_migrations WHERE version = '${migrationVersion}';`;
183
185
  const result = execSync(`psql "postgresql://postgres:postgres@127.0.0.1:54322/postgres" -c "${deleteQuery}"`, {
184
186
  cwd: targetDir,
185
- encoding: 'utf-8',
187
+ encoding: "utf-8",
186
188
  });
187
189
  console.log(chalk.gray(` ✓ Supprimé ${migrationVersion} de schema_migrations (${result.trim()})`));
188
190
  }
@@ -232,15 +234,17 @@ export async function removeModule(moduleName, targetDir) {
232
234
  if (migrationFiles.length > 0) {
233
235
  console.log(chalk.yellow("\n🗄️ Nettoyage de schema_migrations..."));
234
236
  // Extraire uniquement les timestamps (14 premiers caractères) de chaque fichier de migration
235
- const versions = migrationFiles.map(f => {
236
- const timestamp = f.split('_')[0]; // Prend la partie avant le premier underscore
237
+ const versions = migrationFiles
238
+ .map((f) => {
239
+ const timestamp = f.split("_")[0]; // Prend la partie avant le premier underscore
237
240
  return `'${timestamp}'`;
238
- }).join(', ');
241
+ })
242
+ .join(", ");
239
243
  const deleteQuery = `DELETE FROM supabase_migrations.schema_migrations WHERE version IN (${versions});`;
240
244
  try {
241
245
  const result = execSync(`psql "postgresql://postgres:postgres@127.0.0.1:54322/postgres" -c "${deleteQuery}"`, {
242
246
  cwd: targetDir,
243
- encoding: 'utf-8',
247
+ encoding: "utf-8",
244
248
  });
245
249
  console.log(chalk.gray(` ✓ Supprimé ${migrationFiles.length} entrées de schema_migrations`));
246
250
  console.log(chalk.gray(` ${result.trim()}`));
@@ -278,5 +282,14 @@ export async function removeModule(moduleName, targetDir) {
278
282
  process.exit(1);
279
283
  }
280
284
  console.log(chalk.green(`\n✅ Module ${module.displayName} supprimé avec succès!\n`));
285
+ // 7. Rebuilder les modules pour mettre à jour les fichiers générés
286
+ console.log(chalk.yellow("🔧 Mise à jour des modules..."));
287
+ try {
288
+ execSync("pnpm run build:modules", { cwd: targetDir, stdio: "inherit" });
289
+ console.log(chalk.green("✓ Modules mis à jour"));
290
+ }
291
+ catch (error) {
292
+ console.warn(chalk.yellow("⚠️ Erreur lors de la mise à jour des modules"));
293
+ }
281
294
  console.log(chalk.gray("Le serveur de développement redémarrera automatiquement.\n"));
282
295
  }
@@ -8,7 +8,7 @@ export async function runScript(scriptName, args = []) {
8
8
  const scriptPath = path.join(__dirname, `${scriptName}.js`);
9
9
  const child = spawn("node", [scriptPath, ...args], {
10
10
  stdio: "inherit",
11
- env: { ...process.env, PROJECT_ROOT: process.cwd() }
11
+ env: { ...process.env, PROJECT_ROOT: process.cwd() },
12
12
  });
13
13
  child.on("close", (code) => {
14
14
  if (code === 0) {