@lastbrain/app 0.1.33 → 0.1.36
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.
- package/README.md +23 -5
- package/dist/scripts/db-init.js +22 -3
- package/dist/scripts/module-create.d.ts.map +1 -1
- package/dist/scripts/module-create.js +452 -0
- package/dist/styles.css +1 -1
- package/dist/templates/DefaultDoc.d.ts.map +1 -1
- package/dist/templates/DefaultDoc.js +47 -30
- package/dist/templates/DocPage.d.ts.map +1 -1
- package/dist/templates/DocPage.js +1 -1
- package/dist/templates/SimpleHomePage.js +1 -1
- package/dist/templates/migrations/20201010100000_app_base.sql +23 -24
- package/package.json +1 -1
- package/src/scripts/db-init.ts +27 -4
- package/src/scripts/module-create.ts +499 -0
- package/src/templates/DefaultDoc.tsx +452 -739
- package/src/templates/DocPage.tsx +2 -1
- package/src/templates/SimpleHomePage.tsx +1 -1
- package/src/templates/migrations/20201010100000_app_base.sql +23 -24
package/README.md
CHANGED
|
@@ -11,18 +11,36 @@ Package principal pour créer et gérer des applications Next.js LastBrain.
|
|
|
11
11
|
- 🔧 **Scripts utilitaires** - Gestion modules, migrations DB, génération docs
|
|
12
12
|
- 🎨 **Intégration Tailwind v4** - Configuration optimale out-of-the-box
|
|
13
13
|
|
|
14
|
-
## 📦 Installation
|
|
14
|
+
## 📦 Installation & Démarrage rapide
|
|
15
|
+
|
|
16
|
+
> **Recommandé :** Utilisez directement le CLI pour créer une nouvelle application
|
|
15
17
|
|
|
16
18
|
```bash
|
|
17
|
-
|
|
19
|
+
# Créer une nouvelle application LastBrain
|
|
20
|
+
pnpx @lastbrain/app@latest init mon-app
|
|
21
|
+
|
|
22
|
+
# Accéder au dossier
|
|
23
|
+
cd mon-app
|
|
24
|
+
|
|
25
|
+
# Initialiser Supabase (optionnel)
|
|
26
|
+
pnpm db:init
|
|
27
|
+
|
|
28
|
+
# Démarrer le serveur
|
|
29
|
+
pnpm dev
|
|
18
30
|
```
|
|
19
31
|
|
|
20
|
-
|
|
32
|
+
### Installation manuelle
|
|
33
|
+
|
|
34
|
+
Si vous souhaitez intégrer LastBrain dans un projet existant :
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pnpm add @lastbrain/app @lastbrain/core @lastbrain/ui
|
|
38
|
+
```
|
|
21
39
|
|
|
22
|
-
|
|
40
|
+
## 🚀 Ce que fait la commande init
|
|
23
41
|
|
|
24
42
|
```bash
|
|
25
|
-
|
|
43
|
+
pnpx @lastbrain/app@latest init mon-app
|
|
26
44
|
```
|
|
27
45
|
|
|
28
46
|
Cette commande génère une application Next.js complète avec :
|
package/dist/scripts/db-init.js
CHANGED
|
@@ -80,10 +80,29 @@ function parseEnvFile(filePath) {
|
|
|
80
80
|
}));
|
|
81
81
|
}
|
|
82
82
|
function ensureEnvFile(values) {
|
|
83
|
-
const content = Object.entries(values)
|
|
84
|
-
.map(([key, value]) => `${key}=${value}`)
|
|
85
|
-
.join("\n");
|
|
86
83
|
envTargets.forEach((target) => {
|
|
84
|
+
let existingVars = {};
|
|
85
|
+
// Lire le fichier .env.local existant pour préserver les variables personnalisées
|
|
86
|
+
if (fs.existsSync(target)) {
|
|
87
|
+
try {
|
|
88
|
+
const existingContent = fs.readFileSync(target, "utf-8");
|
|
89
|
+
existingVars = Object.fromEntries(existingContent
|
|
90
|
+
.split("\n")
|
|
91
|
+
.filter((line) => line.includes("=") && !line.startsWith("#"))
|
|
92
|
+
.map((line) => {
|
|
93
|
+
const [key, ...value] = line.split("=");
|
|
94
|
+
return [key, value.join("=")];
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.warn(`⚠️ Erreur lors de la lecture de ${target}:`, error);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Fusionner les nouvelles valeurs avec les existantes (nouvelles valeurs prioritaires)
|
|
102
|
+
const mergedValues = { ...existingVars, ...values };
|
|
103
|
+
const content = Object.entries(mergedValues)
|
|
104
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
105
|
+
.join("\n");
|
|
87
106
|
fs.writeFileSync(target, content);
|
|
88
107
|
console.log(`📝 Wrote env to ${target}`);
|
|
89
108
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"AAqsCA;;GAEG;AACH,wBAAsB,YAAY,kBAyIjC"}
|
|
@@ -248,6 +248,9 @@ function generateIndexTs(pages) {
|
|
|
248
248
|
return `// Client Components
|
|
249
249
|
${exports.join("\n")}
|
|
250
250
|
|
|
251
|
+
// Documentation Component
|
|
252
|
+
export { Doc } from "./components/Doc.js";
|
|
253
|
+
|
|
251
254
|
// Configuration de build
|
|
252
255
|
export { default as buildConfig } from "./build.config.js";
|
|
253
256
|
`;
|
|
@@ -520,6 +523,448 @@ $$;
|
|
|
520
523
|
${tablesSQL}
|
|
521
524
|
`;
|
|
522
525
|
}
|
|
526
|
+
/**
|
|
527
|
+
* Generate Doc.tsx component for the module
|
|
528
|
+
*/
|
|
529
|
+
function generateDocComponent(config) {
|
|
530
|
+
const moduleNameClean = config.slug.replace("module-", "");
|
|
531
|
+
// Generate pages sections
|
|
532
|
+
const publicPages = config.pages.filter((p) => p.section === "public");
|
|
533
|
+
const authPages = config.pages.filter((p) => p.section === "auth");
|
|
534
|
+
const adminPages = config.pages.filter((p) => p.section === "admin");
|
|
535
|
+
let pagesSection = "";
|
|
536
|
+
if (config.pages.length > 0) {
|
|
537
|
+
pagesSection = `
|
|
538
|
+
<Card>
|
|
539
|
+
<CardHeader>
|
|
540
|
+
<h2 className="text-2xl font-semibold flex items-center gap-2">
|
|
541
|
+
<FileText size={24} />
|
|
542
|
+
Pages Disponibles
|
|
543
|
+
</h2>
|
|
544
|
+
</CardHeader>
|
|
545
|
+
<CardBody className="space-y-4">`;
|
|
546
|
+
if (publicPages.length > 0) {
|
|
547
|
+
pagesSection += `
|
|
548
|
+
<div>
|
|
549
|
+
<h3 className="text-lg font-semibold mb-2">Pages Publiques</h3>
|
|
550
|
+
<div className="space-y-2">`;
|
|
551
|
+
for (const page of publicPages) {
|
|
552
|
+
const componentName = page.name
|
|
553
|
+
.split("-")
|
|
554
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
555
|
+
.join("");
|
|
556
|
+
pagesSection += `
|
|
557
|
+
<div className="flex items-start gap-2">
|
|
558
|
+
<Chip size="sm" color="success" variant="flat">GET</Chip>
|
|
559
|
+
<code className="text-sm">${page.path}</code>
|
|
560
|
+
<span className="text-sm text-slate-600 dark:text-slate-400">- ${componentName}</span>
|
|
561
|
+
</div>`;
|
|
562
|
+
}
|
|
563
|
+
pagesSection += `
|
|
564
|
+
</div>
|
|
565
|
+
</div>`;
|
|
566
|
+
}
|
|
567
|
+
if (authPages.length > 0) {
|
|
568
|
+
pagesSection += `
|
|
569
|
+
<div>
|
|
570
|
+
<h3 className="text-lg font-semibold mb-2">Pages Protégées (Auth)</h3>
|
|
571
|
+
<div className="space-y-2">`;
|
|
572
|
+
for (const page of authPages) {
|
|
573
|
+
const componentName = page.name
|
|
574
|
+
.split("-")
|
|
575
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
576
|
+
.join("");
|
|
577
|
+
pagesSection += `
|
|
578
|
+
<div className="flex items-start gap-2">
|
|
579
|
+
<Chip size="sm" color="primary" variant="flat">GET</Chip>
|
|
580
|
+
<code className="text-sm">${page.path}</code>
|
|
581
|
+
<span className="text-sm text-slate-600 dark:text-slate-400">- ${componentName}</span>
|
|
582
|
+
</div>`;
|
|
583
|
+
}
|
|
584
|
+
pagesSection += `
|
|
585
|
+
</div>
|
|
586
|
+
</div>`;
|
|
587
|
+
}
|
|
588
|
+
if (adminPages.length > 0) {
|
|
589
|
+
pagesSection += `
|
|
590
|
+
<div>
|
|
591
|
+
<h3 className="text-lg font-semibold mb-2">Pages Admin</h3>
|
|
592
|
+
<div className="space-y-2">`;
|
|
593
|
+
for (const page of adminPages) {
|
|
594
|
+
const componentName = page.name
|
|
595
|
+
.split("-")
|
|
596
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
597
|
+
.join("");
|
|
598
|
+
pagesSection += `
|
|
599
|
+
<div className="flex items-start gap-2">
|
|
600
|
+
<Chip size="sm" color="secondary" variant="flat">GET</Chip>
|
|
601
|
+
<code className="text-sm">${page.path}</code>
|
|
602
|
+
<span className="text-sm text-slate-600 dark:text-slate-400">- ${componentName}</span>
|
|
603
|
+
</div>`;
|
|
604
|
+
}
|
|
605
|
+
pagesSection += `
|
|
606
|
+
</div>
|
|
607
|
+
</div>`;
|
|
608
|
+
}
|
|
609
|
+
pagesSection += `
|
|
610
|
+
</CardBody>
|
|
611
|
+
</Card>
|
|
612
|
+
`;
|
|
613
|
+
}
|
|
614
|
+
// Generate APIs section
|
|
615
|
+
let apisSection = "";
|
|
616
|
+
if (config.tables.length > 0) {
|
|
617
|
+
apisSection = `
|
|
618
|
+
<Card>
|
|
619
|
+
<CardHeader>
|
|
620
|
+
<h2 className="text-2xl font-semibold flex items-center gap-2">
|
|
621
|
+
<Zap size={24} />
|
|
622
|
+
API Routes
|
|
623
|
+
</h2>
|
|
624
|
+
</CardHeader>
|
|
625
|
+
<CardBody className="space-y-4">`;
|
|
626
|
+
for (const table of config.tables) {
|
|
627
|
+
for (const section of table.sections) {
|
|
628
|
+
apisSection += `
|
|
629
|
+
<div>
|
|
630
|
+
<h3 className="text-lg font-semibold mb-2">
|
|
631
|
+
<code>/api/${section}/${table.name}</code>
|
|
632
|
+
</h3>
|
|
633
|
+
<div className="flex gap-2">
|
|
634
|
+
<Chip size="sm" color="success" variant="flat">GET</Chip>
|
|
635
|
+
<Chip size="sm" color="primary" variant="flat">POST</Chip>
|
|
636
|
+
<Chip size="sm" color="warning" variant="flat">PUT</Chip>
|
|
637
|
+
<Chip size="sm" color="danger" variant="flat">DELETE</Chip>
|
|
638
|
+
</div>
|
|
639
|
+
</div>`;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
apisSection += `
|
|
643
|
+
</CardBody>
|
|
644
|
+
</Card>
|
|
645
|
+
`;
|
|
646
|
+
}
|
|
647
|
+
// Generate tables section
|
|
648
|
+
let tablesSection = "";
|
|
649
|
+
if (config.tables.length > 0) {
|
|
650
|
+
tablesSection = `
|
|
651
|
+
<Card>
|
|
652
|
+
<CardHeader>
|
|
653
|
+
<h2 className="text-2xl font-semibold flex items-center gap-2">
|
|
654
|
+
<Database size={24} />
|
|
655
|
+
Base de Données
|
|
656
|
+
</h2>
|
|
657
|
+
</CardHeader>
|
|
658
|
+
<CardBody className="space-y-6">`;
|
|
659
|
+
for (const table of config.tables) {
|
|
660
|
+
tablesSection += `
|
|
661
|
+
<TableStructure
|
|
662
|
+
tableName="${table.name}"
|
|
663
|
+
title="${table.name}"
|
|
664
|
+
description="Table ${table.name} du module ${moduleNameClean}"
|
|
665
|
+
/>`;
|
|
666
|
+
}
|
|
667
|
+
tablesSection += `
|
|
668
|
+
</CardBody>
|
|
669
|
+
</Card>
|
|
670
|
+
`;
|
|
671
|
+
}
|
|
672
|
+
// Installation commands
|
|
673
|
+
const installSection = `
|
|
674
|
+
<Card>
|
|
675
|
+
<CardHeader>
|
|
676
|
+
<h2 className="text-2xl font-semibold flex items-center gap-2">
|
|
677
|
+
<Package size={24} />
|
|
678
|
+
Installation
|
|
679
|
+
</h2>
|
|
680
|
+
</CardHeader>
|
|
681
|
+
<CardBody className="space-y-4">
|
|
682
|
+
<div>
|
|
683
|
+
<h3 className="text-lg font-semibold mb-2">Ajouter le module</h3>
|
|
684
|
+
<Snippet symbol="" hideSymbol className="text-sm mb-2">
|
|
685
|
+
pnpm lastbrain add-module ${moduleNameClean}
|
|
686
|
+
</Snippet>
|
|
687
|
+
<Snippet symbol="" hideSymbol className="text-sm mb-2">
|
|
688
|
+
pnpm build:modules
|
|
689
|
+
</Snippet>
|
|
690
|
+
</div>
|
|
691
|
+
|
|
692
|
+
<div>
|
|
693
|
+
<h3 className="text-lg font-semibold mb-2">Appliquer les migrations</h3>
|
|
694
|
+
<Snippet symbol="" hideSymbol className="text-sm mb-2">
|
|
695
|
+
cd apps/votre-app
|
|
696
|
+
</Snippet>
|
|
697
|
+
<Snippet symbol="" hideSymbol className="text-sm mb-2">
|
|
698
|
+
supabase migration up
|
|
699
|
+
</Snippet>
|
|
700
|
+
</div>
|
|
701
|
+
</CardBody>
|
|
702
|
+
</Card>
|
|
703
|
+
`;
|
|
704
|
+
// Usage section with placeholder
|
|
705
|
+
const usageSection = `
|
|
706
|
+
<Card>
|
|
707
|
+
<CardHeader>
|
|
708
|
+
<h2 className="text-2xl font-semibold flex items-center gap-2">
|
|
709
|
+
<BookOpen size={24} />
|
|
710
|
+
Utilisation
|
|
711
|
+
</h2>
|
|
712
|
+
</CardHeader>
|
|
713
|
+
<CardBody className="space-y-4">
|
|
714
|
+
<Alert color="default" className="mb-4">
|
|
715
|
+
<p className="text-sm">
|
|
716
|
+
📝 <strong>Section à compléter par l'auteur du module</strong>
|
|
717
|
+
</p>
|
|
718
|
+
<p className="text-sm text-slate-600 dark:text-slate-400 mt-2">
|
|
719
|
+
Ajoutez ici des exemples d'utilisation, des configurations spécifiques,
|
|
720
|
+
et toute information utile pour les développeurs utilisant ce module.
|
|
721
|
+
</p>
|
|
722
|
+
</Alert>
|
|
723
|
+
|
|
724
|
+
<div>
|
|
725
|
+
<h3 className="text-lg font-semibold mb-2">Exemple d'utilisation</h3>
|
|
726
|
+
<Alert color="primary" className="p-4 mb-4">
|
|
727
|
+
<pre className="whitespace-pre-wrap">{\`// Importez les composants depuis le module
|
|
728
|
+
import { ${config.pages.length > 0
|
|
729
|
+
? config.pages[0].name
|
|
730
|
+
.split("-")
|
|
731
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
732
|
+
.join("") + "Page"
|
|
733
|
+
: "Component"} } from "${config.moduleName}";
|
|
734
|
+
|
|
735
|
+
// Utilisez-les dans votre application
|
|
736
|
+
<${config.pages.length > 0
|
|
737
|
+
? config.pages[0].name
|
|
738
|
+
.split("-")
|
|
739
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
740
|
+
.join("") + "Page"
|
|
741
|
+
: "Component"} />\`}</pre>
|
|
742
|
+
</Alert>
|
|
743
|
+
</div>
|
|
744
|
+
</CardBody>
|
|
745
|
+
</Card>
|
|
746
|
+
`;
|
|
747
|
+
// Danger zone
|
|
748
|
+
const dangerSection = `
|
|
749
|
+
<Card>
|
|
750
|
+
<CardHeader>
|
|
751
|
+
<h2 className="text-2xl font-semibold flex items-center gap-2 text-danger">
|
|
752
|
+
<AlertTriangle size={24} />
|
|
753
|
+
Danger Zone
|
|
754
|
+
</h2>
|
|
755
|
+
</CardHeader>
|
|
756
|
+
<CardBody className="space-y-4">
|
|
757
|
+
<Alert color="danger" className="mb-4">
|
|
758
|
+
<p className="text-sm font-semibold">
|
|
759
|
+
⚠️ Cette action est irréversible
|
|
760
|
+
</p>
|
|
761
|
+
<p className="text-sm mt-2">
|
|
762
|
+
La suppression du module supprimera toutes les pages, routes API et migrations associées.
|
|
763
|
+
</p>
|
|
764
|
+
</Alert>
|
|
765
|
+
|
|
766
|
+
<div>
|
|
767
|
+
<h3 className="text-lg font-semibold mb-2">Supprimer le module</h3>
|
|
768
|
+
<Snippet symbol="" hideSymbol color="danger" className="text-sm mb-2">
|
|
769
|
+
pnpm lastbrain remove-module ${moduleNameClean}
|
|
770
|
+
</Snippet>
|
|
771
|
+
<Snippet symbol="" hideSymbol color="danger" className="text-sm mb-2">
|
|
772
|
+
pnpm build:modules
|
|
773
|
+
</Snippet>
|
|
774
|
+
</div>
|
|
775
|
+
</CardBody>
|
|
776
|
+
</Card>
|
|
777
|
+
`;
|
|
778
|
+
return `"use client";
|
|
779
|
+
|
|
780
|
+
import { Card, CardBody, CardHeader } from "@lastbrain/ui";
|
|
781
|
+
import { Chip } from "@lastbrain/ui";
|
|
782
|
+
import { Snippet } from "@lastbrain/ui";
|
|
783
|
+
import { Alert } from "@lastbrain/ui";
|
|
784
|
+
import { TableStructure } from "@lastbrain/ui";
|
|
785
|
+
import {
|
|
786
|
+
FileText,
|
|
787
|
+
Zap,
|
|
788
|
+
Database,
|
|
789
|
+
Package,
|
|
790
|
+
BookOpen,
|
|
791
|
+
AlertTriangle
|
|
792
|
+
} from "lucide-react";
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Documentation component for ${config.moduleName}
|
|
796
|
+
* Auto-generated from ${config.slug}.build.config.ts
|
|
797
|
+
*
|
|
798
|
+
* To regenerate this file, run:
|
|
799
|
+
* pnpm generate:module-docs
|
|
800
|
+
*/
|
|
801
|
+
export function Doc() {
|
|
802
|
+
return (
|
|
803
|
+
<div className="container mx-auto p-6 space-y-6">
|
|
804
|
+
<Card>
|
|
805
|
+
<CardHeader>
|
|
806
|
+
<div>
|
|
807
|
+
<h1 className="text-3xl font-bold mb-2">📦 Module ${moduleNameClean}</h1>
|
|
808
|
+
<p className="text-slate-600 dark:text-slate-400">${config.moduleName}</p>
|
|
809
|
+
</div>
|
|
810
|
+
</CardHeader>
|
|
811
|
+
<CardBody>
|
|
812
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
813
|
+
<div>
|
|
814
|
+
<p className="text-sm text-slate-600 dark:text-slate-400">Package</p>
|
|
815
|
+
<code className="text-sm font-semibold">${config.moduleName}</code>
|
|
816
|
+
</div>
|
|
817
|
+
<div>
|
|
818
|
+
<p className="text-sm text-slate-600 dark:text-slate-400">Slug</p>
|
|
819
|
+
<code className="text-sm font-semibold">${config.slug}</code>
|
|
820
|
+
</div>
|
|
821
|
+
<div>
|
|
822
|
+
<p className="text-sm text-slate-600 dark:text-slate-400">Type</p>
|
|
823
|
+
<code className="text-sm font-semibold">Module LastBrain</code>
|
|
824
|
+
</div>
|
|
825
|
+
</div>
|
|
826
|
+
</CardBody>
|
|
827
|
+
</Card>
|
|
828
|
+
${pagesSection}${apisSection}${tablesSection}${installSection}${usageSection}${dangerSection}
|
|
829
|
+
</div>
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
`;
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Generate README.md for the module
|
|
836
|
+
*/
|
|
837
|
+
async function generateModuleReadme(config, moduleDir) {
|
|
838
|
+
const moduleNameClean = config.slug.replace("module-", "");
|
|
839
|
+
let md = `# 📦 Module ${moduleNameClean}\n\n`;
|
|
840
|
+
md += `> ${config.moduleName}\n\n`;
|
|
841
|
+
// Information section
|
|
842
|
+
md += `## 📋 Informations\n\n`;
|
|
843
|
+
md += `- **Nom du package**: \`${config.moduleName}\`\n`;
|
|
844
|
+
md += `- **Slug**: \`${config.slug}\`\n`;
|
|
845
|
+
md += `- **Type**: Module LastBrain\n\n`;
|
|
846
|
+
// Pages section
|
|
847
|
+
if (config.pages.length > 0) {
|
|
848
|
+
md += `## 📄 Pages Disponibles\n\n`;
|
|
849
|
+
const publicPages = config.pages.filter((p) => p.section === "public");
|
|
850
|
+
const authPages = config.pages.filter((p) => p.section === "auth");
|
|
851
|
+
const adminPages = config.pages.filter((p) => p.section === "admin");
|
|
852
|
+
if (publicPages.length > 0) {
|
|
853
|
+
md += `### Pages Publiques\n\n`;
|
|
854
|
+
for (const page of publicPages) {
|
|
855
|
+
const componentName = page.name
|
|
856
|
+
.split("-")
|
|
857
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
858
|
+
.join("");
|
|
859
|
+
md += `- **GET** \`${page.path}\` - ${componentName}\n`;
|
|
860
|
+
}
|
|
861
|
+
md += `\n`;
|
|
862
|
+
}
|
|
863
|
+
if (authPages.length > 0) {
|
|
864
|
+
md += `### Pages Protégées (Auth)\n\n`;
|
|
865
|
+
for (const page of authPages) {
|
|
866
|
+
const componentName = page.name
|
|
867
|
+
.split("-")
|
|
868
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
869
|
+
.join("");
|
|
870
|
+
md += `- **GET** \`${page.path}\` - ${componentName}\n`;
|
|
871
|
+
}
|
|
872
|
+
md += `\n`;
|
|
873
|
+
}
|
|
874
|
+
if (adminPages.length > 0) {
|
|
875
|
+
md += `### Pages Admin\n\n`;
|
|
876
|
+
for (const page of adminPages) {
|
|
877
|
+
const componentName = page.name
|
|
878
|
+
.split("-")
|
|
879
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
880
|
+
.join("");
|
|
881
|
+
md += `- **GET** \`${page.path}\` - ${componentName}\n`;
|
|
882
|
+
}
|
|
883
|
+
md += `\n`;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
// APIs section
|
|
887
|
+
if (config.tables.length > 0) {
|
|
888
|
+
md += `## 🔌 API Routes\n\n`;
|
|
889
|
+
for (const table of config.tables) {
|
|
890
|
+
for (const section of table.sections) {
|
|
891
|
+
md += `### \`/api/${section}/${table.name}\`\n\n`;
|
|
892
|
+
md += `**Méthodes supportées**: GET, POST, PUT, DELETE\n\n`;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
// Database section
|
|
897
|
+
if (config.tables.length > 0) {
|
|
898
|
+
md += `## 🗄️ Base de Données\n\n`;
|
|
899
|
+
md += `### Tables\n\n`;
|
|
900
|
+
for (const table of config.tables) {
|
|
901
|
+
md += `#### \`${table.name}\`\n\n`;
|
|
902
|
+
md += `\`\`\`tsx\n`;
|
|
903
|
+
md += `<TableStructure\n`;
|
|
904
|
+
md += ` tableName="${table.name}"\n`;
|
|
905
|
+
md += ` title="${table.name}"\n`;
|
|
906
|
+
md += ` description="Table ${table.name} du module ${moduleNameClean}"\n`;
|
|
907
|
+
md += `/>\n`;
|
|
908
|
+
md += `\`\`\`\n\n`;
|
|
909
|
+
}
|
|
910
|
+
// Get migration files
|
|
911
|
+
const migrationsPath = path.join(moduleDir, "supabase", "migrations");
|
|
912
|
+
if (fs.existsSync(migrationsPath)) {
|
|
913
|
+
const migrationFiles = fs
|
|
914
|
+
.readdirSync(migrationsPath)
|
|
915
|
+
.filter((f) => f.endsWith(".sql"))
|
|
916
|
+
.sort();
|
|
917
|
+
if (migrationFiles.length > 0) {
|
|
918
|
+
md += `### Migrations\n\n`;
|
|
919
|
+
for (const migration of migrationFiles) {
|
|
920
|
+
md += `- \`${migration}\`\n`;
|
|
921
|
+
}
|
|
922
|
+
md += `\n`;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
// Installation section
|
|
927
|
+
md += `## 📦 Installation\n\n`;
|
|
928
|
+
md += `\`\`\`bash\n`;
|
|
929
|
+
md += `pnpm lastbrain add-module ${moduleNameClean}\n`;
|
|
930
|
+
md += `pnpm build:modules\n`;
|
|
931
|
+
md += `\`\`\`\n\n`;
|
|
932
|
+
md += `### Appliquer les migrations\n\n`;
|
|
933
|
+
md += `\`\`\`bash\n`;
|
|
934
|
+
md += `cd apps/votre-app\n`;
|
|
935
|
+
md += `supabase migration up\n`;
|
|
936
|
+
md += `\`\`\`\n\n`;
|
|
937
|
+
// Usage section
|
|
938
|
+
md += `## 💡 Utilisation\n\n`;
|
|
939
|
+
md += `<!-- 📝 Section à compléter par l'auteur du module -->\n\n`;
|
|
940
|
+
md += `### Exemple d'utilisation\n\n`;
|
|
941
|
+
md += `\`\`\`tsx\n`;
|
|
942
|
+
md += `// Importez les composants depuis le module\n`;
|
|
943
|
+
if (config.pages.length > 0) {
|
|
944
|
+
const firstPage = config.pages[0];
|
|
945
|
+
const componentName = firstPage.name
|
|
946
|
+
.split("-")
|
|
947
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
948
|
+
.join("");
|
|
949
|
+
md += `import { ${componentName}Page } from "${config.moduleName}";\n\n`;
|
|
950
|
+
md += `// Utilisez-les dans votre application\n`;
|
|
951
|
+
md += `<${componentName}Page />\n`;
|
|
952
|
+
}
|
|
953
|
+
md += `\`\`\`\n\n`;
|
|
954
|
+
md += `### Configuration\n\n`;
|
|
955
|
+
md += `<!-- Ajoutez ici les détails de configuration spécifiques -->\n\n`;
|
|
956
|
+
// Danger zone
|
|
957
|
+
md += `## ⚠️ Danger Zone\n\n`;
|
|
958
|
+
md += `La suppression du module supprimera toutes les pages, routes API et migrations associées. **Cette action est irréversible.**\n\n`;
|
|
959
|
+
md += `\`\`\`bash\n`;
|
|
960
|
+
md += `pnpm lastbrain remove-module ${moduleNameClean}\n`;
|
|
961
|
+
md += `pnpm build:modules\n`;
|
|
962
|
+
md += `\`\`\`\n\n`;
|
|
963
|
+
// Write README.md
|
|
964
|
+
const readmePath = path.join(moduleDir, "README.md");
|
|
965
|
+
await fs.writeFile(readmePath, md);
|
|
966
|
+
console.log(chalk.yellow(" 📄 README.md"));
|
|
967
|
+
}
|
|
523
968
|
/**
|
|
524
969
|
* Crée la structure du module
|
|
525
970
|
*/
|
|
@@ -531,6 +976,7 @@ async function createModuleStructure(config, rootDir) {
|
|
|
531
976
|
await fs.ensureDir(path.join(moduleDir, "src"));
|
|
532
977
|
await fs.ensureDir(path.join(moduleDir, "src", "web"));
|
|
533
978
|
await fs.ensureDir(path.join(moduleDir, "src", "api"));
|
|
979
|
+
await fs.ensureDir(path.join(moduleDir, "src", "components"));
|
|
534
980
|
await fs.ensureDir(path.join(moduleDir, "supabase", "migrations"));
|
|
535
981
|
// Créer package.json
|
|
536
982
|
console.log(chalk.yellow(" 📄 package.json"));
|
|
@@ -548,6 +994,9 @@ async function createModuleStructure(config, rootDir) {
|
|
|
548
994
|
// Créer server.ts
|
|
549
995
|
console.log(chalk.yellow(" 📄 src/server.ts"));
|
|
550
996
|
await fs.writeFile(path.join(moduleDir, "src", "server.ts"), generateServerTs(config.tables));
|
|
997
|
+
// Créer Doc.tsx
|
|
998
|
+
console.log(chalk.yellow(" 📄 src/components/Doc.tsx"));
|
|
999
|
+
await fs.writeFile(path.join(moduleDir, "src", "components", "Doc.tsx"), generateDocComponent(config));
|
|
551
1000
|
// Créer les pages
|
|
552
1001
|
console.log(chalk.blue("\n📄 Création des pages..."));
|
|
553
1002
|
for (const page of config.pages) {
|
|
@@ -586,6 +1035,9 @@ async function createModuleStructure(config, rootDir) {
|
|
|
586
1035
|
console.log(chalk.yellow(` 📄 supabase/migrations/${migrationFileName}`));
|
|
587
1036
|
await fs.writeFile(path.join(moduleDir, "supabase", "migrations", migrationFileName), generateMigration(config.tables, config.slug));
|
|
588
1037
|
}
|
|
1038
|
+
// Générer la documentation du module
|
|
1039
|
+
console.log(chalk.blue("\n📝 Génération de la documentation..."));
|
|
1040
|
+
await generateModuleReadme(config, moduleDir);
|
|
589
1041
|
console.log(chalk.green(`\n✅ Module ${config.slug} créé avec succès!\n`));
|
|
590
1042
|
console.log(chalk.gray(`📂 Emplacement: ${moduleDir}\n`));
|
|
591
1043
|
console.log(chalk.blue("Prochaines étapes:"));
|