@lastbrain/app 0.1.42 → 0.1.44
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/dist/__tests__/module-registry.test.js +1 -1
- package/dist/layouts/AdminLayout.d.ts.map +1 -1
- package/dist/layouts/AdminLayout.js +0 -1
- package/dist/layouts/AdminLayoutWithSidebar.d.ts +2 -1
- package/dist/layouts/AdminLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/AdminLayoutWithSidebar.js +10 -4
- package/dist/layouts/AuthLayout.d.ts.map +1 -1
- package/dist/layouts/AuthLayout.js +0 -1
- package/dist/layouts/AuthLayoutWithSidebar.d.ts +2 -1
- package/dist/layouts/AuthLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/AuthLayoutWithSidebar.js +10 -4
- package/dist/layouts/PublicLayout.d.ts +6 -2
- package/dist/layouts/PublicLayout.d.ts.map +1 -1
- package/dist/layouts/PublicLayout.js +4 -3
- package/dist/layouts/PublicLayoutWithSidebar.d.ts +2 -1
- package/dist/layouts/PublicLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/PublicLayoutWithSidebar.js +10 -4
- package/dist/scripts/db-migrations-sync.js +67 -38
- package/dist/scripts/dev-sync.js +1 -0
- package/dist/scripts/init-app.d.ts.map +1 -1
- package/dist/scripts/init-app.js +172 -59
- package/dist/scripts/module-build.d.ts.map +1 -1
- package/dist/scripts/module-build.js +67 -6
- package/dist/scripts/module-create.d.ts.map +1 -1
- package/dist/scripts/module-create.js +19 -0
- package/dist/scripts/module-delete.d.ts.map +1 -1
- package/dist/scripts/module-delete.js +6 -2
- package/dist/scripts/module-list.d.ts.map +1 -1
- package/dist/scripts/module-list.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/templates/DefaultDoc.d.ts.map +1 -1
- package/dist/templates/DefaultDoc.js +6 -13
- package/dist/templates/DocPage.d.ts.map +1 -1
- package/dist/templates/DocPage.js +3 -5
- package/dist/templates/SimpleDocPage.js +2 -2
- package/dist/templates/SimpleHomePage.js +1 -1
- package/package.json +7 -2
- package/src/__tests__/module-registry.test.ts +1 -1
- package/src/layouts/AdminLayout.tsx +0 -2
- package/src/layouts/AdminLayoutWithSidebar.tsx +13 -6
- package/src/layouts/AuthLayout.tsx +0 -2
- package/src/layouts/AuthLayoutWithSidebar.tsx +13 -6
- package/src/layouts/PublicLayout.tsx +14 -2
- package/src/layouts/PublicLayoutWithSidebar.tsx +13 -6
- package/src/scripts/db-migrations-sync.ts +86 -57
- package/src/scripts/dev-sync.ts +1 -0
- package/src/scripts/init-app.ts +191 -58
- package/src/scripts/module-build.ts +77 -6
- package/src/scripts/module-create.ts +25 -2
- package/src/scripts/module-delete.ts +8 -6
- package/src/scripts/module-list.ts +1 -4
- package/src/templates/DefaultDoc.tsx +346 -455
- package/src/templates/DocPage.tsx +40 -22
- package/src/templates/SimpleDocPage.tsx +2 -2
- package/src/templates/SimpleHomePage.tsx +3 -3
package/src/scripts/init-app.ts
CHANGED
|
@@ -85,7 +85,7 @@ export async function initApp(options: InitAppOptions) {
|
|
|
85
85
|
await createNextStructure(targetDir, force, useHeroUI, withAuth);
|
|
86
86
|
|
|
87
87
|
// 4. Créer les fichiers de configuration
|
|
88
|
-
await createConfigFiles(targetDir, force, useHeroUI);
|
|
88
|
+
await createConfigFiles(targetDir, force, useHeroUI, projectName);
|
|
89
89
|
|
|
90
90
|
// 5. Créer le système de proxy storage
|
|
91
91
|
await createStorageProxy(targetDir, force);
|
|
@@ -138,6 +138,21 @@ export async function initApp(options: InitAppOptions) {
|
|
|
138
138
|
execSync("pnpm build:modules", { cwd: targetDir, stdio: "inherit" });
|
|
139
139
|
console.log(chalk.green("\n✓ Routes des modules générées\n"));
|
|
140
140
|
|
|
141
|
+
console.log(
|
|
142
|
+
chalk.yellow("📜 Synchronisation des migrations des modules...\n"),
|
|
143
|
+
);
|
|
144
|
+
try {
|
|
145
|
+
execSync("pnpm db:migrations:sync", {
|
|
146
|
+
cwd: targetDir,
|
|
147
|
+
stdio: "inherit",
|
|
148
|
+
});
|
|
149
|
+
console.log(chalk.green("\n✓ Migrations synchronisées\n"));
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.log(
|
|
152
|
+
chalk.yellow("\n⚠️ Erreur de synchronisation des migrations\n"),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
141
156
|
console.log(chalk.yellow("🗄️ Initialisation de la base de données...\n"));
|
|
142
157
|
try {
|
|
143
158
|
execSync("pnpm db:init", { cwd: targetDir, stdio: "inherit" });
|
|
@@ -195,6 +210,7 @@ export async function initApp(options: InitAppOptions) {
|
|
|
195
210
|
console.log(chalk.white(` cd ${relativePath}`));
|
|
196
211
|
console.log(chalk.white(" pnpm install"));
|
|
197
212
|
console.log(chalk.white(" pnpm build:modules"));
|
|
213
|
+
console.log(chalk.white(" pnpm db:migrations:sync"));
|
|
198
214
|
console.log(chalk.white(" pnpm db:init"));
|
|
199
215
|
console.log(chalk.white(" pnpm dev\n"));
|
|
200
216
|
}
|
|
@@ -206,11 +222,12 @@ export async function initApp(options: InitAppOptions) {
|
|
|
206
222
|
chalk.white(" 3. pnpm build:modules (générer les routes des modules)"),
|
|
207
223
|
);
|
|
208
224
|
console.log(
|
|
209
|
-
chalk.white(" 4. pnpm db:
|
|
225
|
+
chalk.white(" 4. pnpm db:migrations:sync (synchroniser les migrations)"),
|
|
226
|
+
);
|
|
227
|
+
console.log(
|
|
228
|
+
chalk.white(" 5. pnpm db:init (initialiser la base de données)"),
|
|
210
229
|
);
|
|
211
|
-
console.log(chalk.white("
|
|
212
|
-
console.log(chalk.white(" 4. pnpm db:init (initialiser Supabase)"));
|
|
213
|
-
console.log(chalk.white(" 5. pnpm dev (démarrer le serveur)\n"));
|
|
230
|
+
console.log(chalk.white(" 6. pnpm dev (lancer le serveur)\n"));
|
|
214
231
|
|
|
215
232
|
console.log(chalk.gray("Prérequis pour Supabase :"));
|
|
216
233
|
console.log(chalk.white(" - Docker Desktop installé et lancé"));
|
|
@@ -490,75 +507,38 @@ async function createNextStructure(
|
|
|
490
507
|
let layoutContent = "";
|
|
491
508
|
|
|
492
509
|
if (useHeroUI) {
|
|
493
|
-
// Layout avec HeroUI
|
|
510
|
+
// Layout avec HeroUI - Server Component
|
|
494
511
|
layoutContent = `// GENERATED BY LASTBRAIN APP-SHELL
|
|
495
|
-
|
|
496
|
-
"use client";
|
|
512
|
+
// Server Component pour permettre le SSR des pages enfants
|
|
497
513
|
|
|
498
514
|
import "../styles/globals.css";
|
|
499
|
-
import {
|
|
500
|
-
|
|
501
|
-
import { ThemeProvider } from "next-themes";
|
|
502
|
-
import { useRouter } from "next/navigation";
|
|
503
|
-
import { AppProviders } from "../components/AppProviders";
|
|
515
|
+
import { ClientLayout } from "../components/ClientLayout";
|
|
504
516
|
import type { PropsWithChildren } from "react";
|
|
505
|
-
import { AppHeader } from "../components/AppHeader";
|
|
506
517
|
|
|
507
518
|
export default function RootLayout({ children }: PropsWithChildren<{}>) {
|
|
508
|
-
const router = useRouter();
|
|
509
|
-
|
|
510
519
|
return (
|
|
511
520
|
<html lang="fr" suppressHydrationWarning>
|
|
512
521
|
<body className="min-h-screen">
|
|
513
|
-
<
|
|
514
|
-
<ThemeProvider
|
|
515
|
-
attribute="class"
|
|
516
|
-
defaultTheme="dark"
|
|
517
|
-
enableSystem={false}
|
|
518
|
-
storageKey="lastbrain-theme"
|
|
519
|
-
>
|
|
520
|
-
<AppProviders>
|
|
521
|
-
<AppHeader />
|
|
522
|
-
<div className="min-h-screen text-foreground bg-background">
|
|
523
|
-
{children}
|
|
524
|
-
</div>
|
|
525
|
-
</AppProviders>
|
|
526
|
-
</ThemeProvider>
|
|
527
|
-
</HeroUIProvider>
|
|
522
|
+
<ClientLayout>{children}</ClientLayout>
|
|
528
523
|
</body>
|
|
529
524
|
</html>
|
|
530
525
|
);
|
|
531
526
|
}
|
|
532
527
|
`;
|
|
533
528
|
} else {
|
|
534
|
-
// Layout Tailwind CSS uniquement
|
|
529
|
+
// Layout Tailwind CSS uniquement - Server Component
|
|
535
530
|
layoutContent = `// GENERATED BY LASTBRAIN APP-SHELL
|
|
536
|
-
|
|
537
|
-
"use client";
|
|
531
|
+
// Server Component pour permettre le SSR des pages enfants
|
|
538
532
|
|
|
539
533
|
import "../styles/globals.css";
|
|
540
|
-
import {
|
|
541
|
-
import { AppProviders } from "../components/AppProviders";
|
|
534
|
+
import { ClientLayout } from "../components/ClientLayout";
|
|
542
535
|
import type { PropsWithChildren } from "react";
|
|
543
|
-
import { AppHeader } from "../components/AppHeader";
|
|
544
536
|
|
|
545
537
|
export default function RootLayout({ children }: PropsWithChildren<{}>) {
|
|
546
538
|
return (
|
|
547
539
|
<html lang="fr" suppressHydrationWarning>
|
|
548
540
|
<body className="min-h-screen">
|
|
549
|
-
<
|
|
550
|
-
attribute="class"
|
|
551
|
-
defaultTheme="light"
|
|
552
|
-
enableSystem={false}
|
|
553
|
-
storageKey="lastbrain-theme"
|
|
554
|
-
>
|
|
555
|
-
<AppProviders>
|
|
556
|
-
<AppHeader />
|
|
557
|
-
<div className="min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-white">
|
|
558
|
-
{children}
|
|
559
|
-
</div>
|
|
560
|
-
</AppProviders>
|
|
561
|
-
</ThemeProvider>
|
|
541
|
+
<ClientLayout>{children}</ClientLayout>
|
|
562
542
|
</body>
|
|
563
543
|
</html>
|
|
564
544
|
);
|
|
@@ -593,9 +573,16 @@ export default function RootLayout({ children }: PropsWithChildren<{}>) {
|
|
|
593
573
|
const homePageContent = `// GENERATED BY LASTBRAIN APP-SHELL
|
|
594
574
|
|
|
595
575
|
import { SimpleHomePage } from "@lastbrain/app";
|
|
576
|
+
import { Footer } from "@lastbrain/ui";
|
|
577
|
+
import { footerConfig } from "../config/footer";
|
|
596
578
|
|
|
597
579
|
export default function RootPage() {
|
|
598
|
-
return
|
|
580
|
+
return (
|
|
581
|
+
<>
|
|
582
|
+
<SimpleHomePage showAuth={${withAuth}} />
|
|
583
|
+
<Footer config={footerConfig} />
|
|
584
|
+
</>
|
|
585
|
+
);
|
|
599
586
|
}
|
|
600
587
|
`;
|
|
601
588
|
await fs.writeFile(homePagePath, homePageContent);
|
|
@@ -643,6 +630,9 @@ export default function NotFound() {
|
|
|
643
630
|
await createRoute(appDir, "auth", "auth", force);
|
|
644
631
|
await createRoute(appDir, "docs", "public", force);
|
|
645
632
|
|
|
633
|
+
// Créer le composant ClientLayout (wrapper client avec providers)
|
|
634
|
+
await createClientLayout(targetDir, force, useHeroUI);
|
|
635
|
+
|
|
646
636
|
// Créer le composant AppHeader
|
|
647
637
|
await createAppHeader(targetDir, force);
|
|
648
638
|
|
|
@@ -653,6 +643,92 @@ export default function NotFound() {
|
|
|
653
643
|
await createAppProvidersWrapper(targetDir, force);
|
|
654
644
|
}
|
|
655
645
|
|
|
646
|
+
async function createClientLayout(
|
|
647
|
+
targetDir: string,
|
|
648
|
+
force: boolean,
|
|
649
|
+
useHeroUI: boolean,
|
|
650
|
+
) {
|
|
651
|
+
const componentsDir = path.join(targetDir, "components");
|
|
652
|
+
await fs.ensureDir(componentsDir);
|
|
653
|
+
|
|
654
|
+
const clientLayoutPath = path.join(componentsDir, "ClientLayout.tsx");
|
|
655
|
+
|
|
656
|
+
if (!fs.existsSync(clientLayoutPath) || force) {
|
|
657
|
+
let clientLayoutContent = "";
|
|
658
|
+
|
|
659
|
+
if (useHeroUI) {
|
|
660
|
+
// ClientLayout avec HeroUI
|
|
661
|
+
clientLayoutContent = `"use client";
|
|
662
|
+
|
|
663
|
+
import { HeroUIProvider } from "@heroui/system";
|
|
664
|
+
import { ThemeProvider } from "next-themes";
|
|
665
|
+
import { useRouter } from "next/navigation";
|
|
666
|
+
import { AppProviders } from "./AppProviders";
|
|
667
|
+
import { AppHeader } from "./AppHeader";
|
|
668
|
+
import type { ReactNode } from "react";
|
|
669
|
+
|
|
670
|
+
export function ClientLayout({ children }: { children: ReactNode }) {
|
|
671
|
+
const router = useRouter();
|
|
672
|
+
|
|
673
|
+
return (
|
|
674
|
+
<HeroUIProvider navigate={router.push}>
|
|
675
|
+
<ThemeProvider
|
|
676
|
+
attribute="class"
|
|
677
|
+
defaultTheme="dark"
|
|
678
|
+
enableSystem={false}
|
|
679
|
+
storageKey="lastbrain-theme"
|
|
680
|
+
>
|
|
681
|
+
<AppProviders>
|
|
682
|
+
<AppHeader />
|
|
683
|
+
<div className="min-h-screen text-foreground bg-background">
|
|
684
|
+
{children}
|
|
685
|
+
</div>
|
|
686
|
+
</AppProviders>
|
|
687
|
+
</ThemeProvider>
|
|
688
|
+
</HeroUIProvider>
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
`;
|
|
692
|
+
} else {
|
|
693
|
+
// ClientLayout Tailwind CSS uniquement
|
|
694
|
+
clientLayoutContent = `"use client";
|
|
695
|
+
|
|
696
|
+
import { ThemeProvider } from "next-themes";
|
|
697
|
+
import { AppProviders } from "./AppProviders";
|
|
698
|
+
import { AppHeader } from "./AppHeader";
|
|
699
|
+
import type { ReactNode } from "react";
|
|
700
|
+
|
|
701
|
+
export function ClientLayout({ children }: { children: ReactNode }) {
|
|
702
|
+
return (
|
|
703
|
+
<ThemeProvider
|
|
704
|
+
attribute="class"
|
|
705
|
+
defaultTheme="light"
|
|
706
|
+
enableSystem={false}
|
|
707
|
+
storageKey="lastbrain-theme"
|
|
708
|
+
>
|
|
709
|
+
<AppProviders>
|
|
710
|
+
<AppHeader />
|
|
711
|
+
<div className="min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-white">
|
|
712
|
+
{children}
|
|
713
|
+
</div>
|
|
714
|
+
</AppProviders>
|
|
715
|
+
</ThemeProvider>
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
`;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
await fs.writeFile(clientLayoutPath, clientLayoutContent);
|
|
722
|
+
console.log(chalk.green("✓ components/ClientLayout.tsx créé"));
|
|
723
|
+
} else {
|
|
724
|
+
console.log(
|
|
725
|
+
chalk.gray(
|
|
726
|
+
" components/ClientLayout.tsx existe déjà (utilisez --force pour écraser)",
|
|
727
|
+
),
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
656
732
|
async function createRoute(
|
|
657
733
|
appDir: string,
|
|
658
734
|
routeName: string,
|
|
@@ -700,12 +776,27 @@ export default function AuthLayout({
|
|
|
700
776
|
);
|
|
701
777
|
}`;
|
|
702
778
|
} else {
|
|
703
|
-
// Layout standard pour les autres routes
|
|
779
|
+
// Layout standard pour les autres routes (ex: docs = public)
|
|
704
780
|
const layoutComponent =
|
|
705
781
|
layoutType.charAt(0).toUpperCase() + layoutType.slice(1) + "Layout";
|
|
706
|
-
|
|
782
|
+
|
|
783
|
+
if (routeName === "docs") {
|
|
784
|
+
// Layout docs avec footer
|
|
785
|
+
layoutContent = `import { ${layoutComponent} } from "@lastbrain/app";
|
|
786
|
+
import { footerConfig } from "../../config/footer";
|
|
787
|
+
|
|
788
|
+
export default function DocsLayout({
|
|
789
|
+
children,
|
|
790
|
+
}: {
|
|
791
|
+
children: React.ReactNode;
|
|
792
|
+
}) {
|
|
793
|
+
return <${layoutComponent} footerConfig={footerConfig}>{children}</${layoutComponent}>;
|
|
794
|
+
}`;
|
|
795
|
+
} else {
|
|
796
|
+
layoutContent = `import { ${layoutComponent} } from "@lastbrain/app";
|
|
707
797
|
|
|
708
798
|
export default ${layoutComponent};`;
|
|
799
|
+
}
|
|
709
800
|
}
|
|
710
801
|
|
|
711
802
|
await fs.writeFile(layoutPath, layoutContent);
|
|
@@ -900,6 +991,7 @@ async function createConfigFiles(
|
|
|
900
991
|
targetDir: string,
|
|
901
992
|
force: boolean,
|
|
902
993
|
useHeroUI: boolean,
|
|
994
|
+
projectName?: string,
|
|
903
995
|
) {
|
|
904
996
|
console.log(chalk.yellow("\n⚙️ Création des fichiers de configuration..."));
|
|
905
997
|
|
|
@@ -913,6 +1005,24 @@ export async function middleware(request: NextRequest) {
|
|
|
913
1005
|
const { pathname } = request.nextUrl;
|
|
914
1006
|
const isApi = pathname.startsWith("/api/");
|
|
915
1007
|
|
|
1008
|
+
// Pages publiques d'authentification (ne pas protéger)
|
|
1009
|
+
const publicAuthPages = [
|
|
1010
|
+
"/signin",
|
|
1011
|
+
"/signup",
|
|
1012
|
+
"/reset-password",
|
|
1013
|
+
"/forgot-password",
|
|
1014
|
+
"/callback",
|
|
1015
|
+
];
|
|
1016
|
+
|
|
1017
|
+
const isPublicAuthPage = publicAuthPages.some((page) =>
|
|
1018
|
+
pathname.startsWith(page)
|
|
1019
|
+
);
|
|
1020
|
+
|
|
1021
|
+
// Ne pas protéger les pages publiques d'authentification
|
|
1022
|
+
if (isPublicAuthPage) {
|
|
1023
|
+
return NextResponse.next();
|
|
1024
|
+
}
|
|
1025
|
+
|
|
916
1026
|
// Protéger les routes /auth/* (espace membre)
|
|
917
1027
|
if (pathname.startsWith("/auth")) {
|
|
918
1028
|
try {
|
|
@@ -921,9 +1031,9 @@ export async function middleware(request: NextRequest) {
|
|
|
921
1031
|
data: { session },
|
|
922
1032
|
} = await supabase.auth.getSession();
|
|
923
1033
|
|
|
924
|
-
// Pas de session → nettoyage des cookies + redirection vers /
|
|
1034
|
+
// Pas de session → nettoyage des cookies + redirection vers /signin
|
|
925
1035
|
if (!session) {
|
|
926
|
-
const redirectUrl = new URL("/
|
|
1036
|
+
const redirectUrl = new URL("/signin", request.url);
|
|
927
1037
|
redirectUrl.searchParams.set("redirect", pathname);
|
|
928
1038
|
const res = NextResponse.redirect(redirectUrl);
|
|
929
1039
|
res.cookies.delete("sb-access-token");
|
|
@@ -936,7 +1046,7 @@ export async function middleware(request: NextRequest) {
|
|
|
936
1046
|
return response;
|
|
937
1047
|
} catch (error) {
|
|
938
1048
|
console.error("Middleware auth error:", error);
|
|
939
|
-
const res = NextResponse.redirect(new URL("/
|
|
1049
|
+
const res = NextResponse.redirect(new URL("/signin", request.url));
|
|
940
1050
|
res.cookies.delete("sb-access-token");
|
|
941
1051
|
res.cookies.delete("sb-refresh-token");
|
|
942
1052
|
res.cookies.delete("sb:token");
|
|
@@ -953,7 +1063,7 @@ export async function middleware(request: NextRequest) {
|
|
|
953
1063
|
data: { session },
|
|
954
1064
|
} = await supabase.auth.getSession();
|
|
955
1065
|
|
|
956
|
-
// Pas de session → 401 JSON pour API, sinon redirection vers /
|
|
1066
|
+
// Pas de session → 401 JSON pour API, sinon redirection vers /signin avec nettoyage cookies
|
|
957
1067
|
if (!session) {
|
|
958
1068
|
if (isApi) {
|
|
959
1069
|
const res = NextResponse.json({ error: "Non authentifié" }, { status: 401 });
|
|
@@ -963,7 +1073,7 @@ export async function middleware(request: NextRequest) {
|
|
|
963
1073
|
res.cookies.delete("sb:refresh-token");
|
|
964
1074
|
return res;
|
|
965
1075
|
}
|
|
966
|
-
const redirectUrl = new URL("/
|
|
1076
|
+
const redirectUrl = new URL("/signin", request.url);
|
|
967
1077
|
redirectUrl.searchParams.set("redirect", pathname);
|
|
968
1078
|
const res = NextResponse.redirect(redirectUrl);
|
|
969
1079
|
res.cookies.delete("sb-access-token");
|
|
@@ -1032,6 +1142,9 @@ export const config = {
|
|
|
1032
1142
|
const nextConfig = `/** @type {import('next').NextConfig} */
|
|
1033
1143
|
const nextConfig = {
|
|
1034
1144
|
reactStrictMode: true,
|
|
1145
|
+
devIndicators: {
|
|
1146
|
+
position: 'bottom-right',
|
|
1147
|
+
},
|
|
1035
1148
|
};
|
|
1036
1149
|
|
|
1037
1150
|
export default nextConfig;
|
|
@@ -1174,6 +1287,26 @@ export const menuConfig: MenuConfig = {
|
|
|
1174
1287
|
console.log(chalk.green("✓ config/menu.ts créé"));
|
|
1175
1288
|
}
|
|
1176
1289
|
|
|
1290
|
+
// config/footer.ts
|
|
1291
|
+
const footerConfigPath = path.join(configDir, "footer.ts");
|
|
1292
|
+
if (!fs.existsSync(footerConfigPath) || force) {
|
|
1293
|
+
const footerConfig = `// Auto-generated footer configuration
|
|
1294
|
+
// Run "node ../../scripts/generate-footer-config.js ./apps/[your-app]" to regenerate
|
|
1295
|
+
"use client";
|
|
1296
|
+
|
|
1297
|
+
import type { FooterConfig } from "@lastbrain/ui";
|
|
1298
|
+
|
|
1299
|
+
export const footerConfig: FooterConfig = {
|
|
1300
|
+
companyName: "${projectName}",
|
|
1301
|
+
companyDescription: "Application LastBrain",
|
|
1302
|
+
links: [],
|
|
1303
|
+
social: [],
|
|
1304
|
+
};
|
|
1305
|
+
`;
|
|
1306
|
+
await fs.writeFile(footerConfigPath, footerConfig);
|
|
1307
|
+
console.log(chalk.green("✓ config/footer.ts créé"));
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1177
1310
|
// Créer les hooks
|
|
1178
1311
|
await createHooksDirectory(targetDir, force);
|
|
1179
1312
|
}
|
|
@@ -47,7 +47,11 @@ async function loadModuleConfigs(): Promise<ModuleBuildConfig[]> {
|
|
|
47
47
|
const packageName = module.package;
|
|
48
48
|
|
|
49
49
|
try {
|
|
50
|
-
|
|
50
|
+
// Extraire le suffix du module (la partie après @lastbrain/module- ou @lastbrain-labs/module-)
|
|
51
|
+
const moduleSuffix = packageName
|
|
52
|
+
.replace("@lastbrain/module-", "")
|
|
53
|
+
.replace("@lastbrain-labs/module-", "");
|
|
54
|
+
|
|
51
55
|
const possibleConfigNames = [
|
|
52
56
|
`${moduleSuffix}.build.config`,
|
|
53
57
|
"build.config",
|
|
@@ -159,9 +163,10 @@ function toPascalCase(value: string) {
|
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
function buildPage(moduleConfig: ModuleBuildConfig, page: ModulePageConfig) {
|
|
162
|
-
// Extraire le préfixe du module (ex: @lastbrain/module-auth -> auth)
|
|
166
|
+
// Extraire le préfixe du module (ex: @lastbrain/module-auth -> auth, @lastbrain-labs/module-recipes-pro -> recipes-pro)
|
|
163
167
|
const modulePrefix = moduleConfig.moduleName
|
|
164
168
|
.replace(/^@lastbrain\/module-/, "")
|
|
169
|
+
.replace(/^@lastbrain-labs\/module-/, "")
|
|
165
170
|
.toLowerCase();
|
|
166
171
|
|
|
167
172
|
if (isDebugMode) {
|
|
@@ -593,7 +598,14 @@ function generateDocsPage(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
593
598
|
|
|
594
599
|
allModules.forEach((moduleEntry) => {
|
|
595
600
|
const moduleName = moduleEntry.package;
|
|
596
|
-
|
|
601
|
+
// Extraire le nom du module sans le scope et sans "module-"
|
|
602
|
+
// Ex: @lastbrain/module-auth -> auth
|
|
603
|
+
// Ex: @lastbrain-labs/module-recipes-pro -> recipes
|
|
604
|
+
const moduleId = moduleName
|
|
605
|
+
.replace("@lastbrain-labs/module-", "")
|
|
606
|
+
.replace("@lastbrain/module-", "")
|
|
607
|
+
.replace(/-pro$/, ""); // Retirer le suffix -pro pour avoir le nom de base
|
|
608
|
+
|
|
597
609
|
const docComponentName = `${toPascalCase(moduleId)}ModuleDoc`;
|
|
598
610
|
|
|
599
611
|
// Trouver la config du module pour obtenir la description
|
|
@@ -610,8 +622,8 @@ function generateDocsPage(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
610
622
|
}
|
|
611
623
|
|
|
612
624
|
const config = {
|
|
613
|
-
id:
|
|
614
|
-
name: `Module ${
|
|
625
|
+
id: moduleName, // Utiliser le nom complet du package comme ID
|
|
626
|
+
name: `Module ${toPascalCase(moduleId)}`,
|
|
615
627
|
description: description,
|
|
616
628
|
component: docComponentName,
|
|
617
629
|
active: moduleEntry.active,
|
|
@@ -1241,7 +1253,6 @@ import { NextRequest, NextResponse } from "next/server";
|
|
|
1241
1253
|
* GENERATED FILE - DO NOT EDIT MANUALLY
|
|
1242
1254
|
* Generated at: ${timestamp}
|
|
1243
1255
|
* Generated from module storage configurations
|
|
1244
|
-
*
|
|
1245
1256
|
* Buckets configurés:
|
|
1246
1257
|
${allBuckets.map((b) => ` * - ${b.name} (${b.public ? "public" : "private"})`).join("\n")}
|
|
1247
1258
|
*/
|
|
@@ -1350,6 +1361,60 @@ export async function GET(
|
|
|
1350
1361
|
}
|
|
1351
1362
|
}
|
|
1352
1363
|
|
|
1364
|
+
async function generateFooterConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
1365
|
+
try {
|
|
1366
|
+
// Extraire tous les liens footer des modules
|
|
1367
|
+
const allFooterLinks = moduleConfigs
|
|
1368
|
+
.filter((config) => config.footer && config.footer.length > 0)
|
|
1369
|
+
.flatMap((config) => config.footer!);
|
|
1370
|
+
|
|
1371
|
+
if (allFooterLinks.length === 0) {
|
|
1372
|
+
console.log(
|
|
1373
|
+
"⏭️ No footer links found, skipping footer config generation",
|
|
1374
|
+
);
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// Trier les liens par position et order
|
|
1379
|
+
allFooterLinks.sort((a, b) => {
|
|
1380
|
+
const posOrder = { left: 0, center: 1, right: 2 };
|
|
1381
|
+
const posA = posOrder[a.position || "left"] || 0;
|
|
1382
|
+
const posB = posOrder[b.position || "left"] || 0;
|
|
1383
|
+
if (posA !== posB) return posA - posB;
|
|
1384
|
+
return (a.order || 0) - (b.order || 0);
|
|
1385
|
+
});
|
|
1386
|
+
|
|
1387
|
+
const timestamp = new Date().toISOString();
|
|
1388
|
+
const content = `// Auto-generated footer configuration
|
|
1389
|
+
// Generated from module build configs
|
|
1390
|
+
// Generated at: ${timestamp}
|
|
1391
|
+
"use client";
|
|
1392
|
+
|
|
1393
|
+
import type { FooterConfig } from "@lastbrain/ui";
|
|
1394
|
+
|
|
1395
|
+
export const footerConfig: FooterConfig = {
|
|
1396
|
+
companyName: "LastBrain",
|
|
1397
|
+
companyDescription: "Plateforme de développement rapide d'applications",
|
|
1398
|
+
links: ${JSON.stringify(allFooterLinks, null, 2)},
|
|
1399
|
+
social: [],
|
|
1400
|
+
};
|
|
1401
|
+
`;
|
|
1402
|
+
|
|
1403
|
+
const configDir = path.join(projectRoot, "config");
|
|
1404
|
+
ensureDirectory(configDir);
|
|
1405
|
+
|
|
1406
|
+
const footerPath = path.join(configDir, "footer.ts");
|
|
1407
|
+
fs.writeFileSync(footerPath, content);
|
|
1408
|
+
|
|
1409
|
+
if (isDebugMode) {
|
|
1410
|
+
console.log(`✅ Generated footer config: ${footerPath}`);
|
|
1411
|
+
console.log(` - ${allFooterLinks.length} footer link(s)`);
|
|
1412
|
+
}
|
|
1413
|
+
} catch (error) {
|
|
1414
|
+
console.error("❌ Error generating footer config:", error);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1353
1418
|
export async function runModuleBuild() {
|
|
1354
1419
|
ensureDirectory(appDirectory);
|
|
1355
1420
|
|
|
@@ -1431,6 +1496,12 @@ export async function runModuleBuild() {
|
|
|
1431
1496
|
}
|
|
1432
1497
|
await generateStorageProxyApi(moduleConfigs);
|
|
1433
1498
|
|
|
1499
|
+
// Générer la configuration footer
|
|
1500
|
+
if (isDebugMode) {
|
|
1501
|
+
console.log("🦶 Generating footer configuration...");
|
|
1502
|
+
}
|
|
1503
|
+
await generateFooterConfig(moduleConfigs);
|
|
1504
|
+
|
|
1434
1505
|
// Message de succès final
|
|
1435
1506
|
if (!isDebugMode) {
|
|
1436
1507
|
console.log("\n✅ Module build completed successfully!");
|
|
@@ -1028,12 +1028,21 @@ ${pagesSection}${apisSection}${tablesSection}${installSection}${usageSection}${d
|
|
|
1028
1028
|
/**
|
|
1029
1029
|
* Generate README.md for the module
|
|
1030
1030
|
*/
|
|
1031
|
-
async function generateModuleReadme(
|
|
1031
|
+
async function generateModuleReadme(
|
|
1032
|
+
config: ModuleConfig & { description?: string },
|
|
1033
|
+
moduleDir: string,
|
|
1034
|
+
) {
|
|
1032
1035
|
const moduleNameClean = config.slug.replace("module-", "");
|
|
1033
1036
|
|
|
1034
1037
|
let md = `# 📦 Module ${moduleNameClean}\n\n`;
|
|
1035
1038
|
md += `> ${config.moduleName}\n\n`;
|
|
1036
1039
|
|
|
1040
|
+
// Description section
|
|
1041
|
+
if (config.description) {
|
|
1042
|
+
md += `## 📝 Description\n\n`;
|
|
1043
|
+
md += `${config.description}\n\n`;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1037
1046
|
// Information section
|
|
1038
1047
|
md += `## 📋 Informations\n\n`;
|
|
1039
1048
|
md += `- **Nom du package**: \`${config.moduleName}\`\n`;
|
|
@@ -1533,6 +1542,18 @@ export async function createModule() {
|
|
|
1533
1542
|
},
|
|
1534
1543
|
filter: (input: string) => input.trim().toLowerCase(),
|
|
1535
1544
|
},
|
|
1545
|
+
{
|
|
1546
|
+
type: "input",
|
|
1547
|
+
name: "description",
|
|
1548
|
+
message: "Description du module (une ligne):",
|
|
1549
|
+
default: "Module LastBrain",
|
|
1550
|
+
validate: (input) => {
|
|
1551
|
+
if (!input || input.trim() === "") {
|
|
1552
|
+
return "La description est requise";
|
|
1553
|
+
}
|
|
1554
|
+
return true;
|
|
1555
|
+
},
|
|
1556
|
+
},
|
|
1536
1557
|
{
|
|
1537
1558
|
type: "input",
|
|
1538
1559
|
name: "pagesPublic",
|
|
@@ -1565,6 +1586,7 @@ export async function createModule() {
|
|
|
1565
1586
|
// Construire la configuration du module
|
|
1566
1587
|
const slug = `module-${answers.slug}`;
|
|
1567
1588
|
const moduleName = `@lastbrain/${slug}`;
|
|
1589
|
+
const description = answers.description;
|
|
1568
1590
|
|
|
1569
1591
|
const pages: PageConfig[] = [];
|
|
1570
1592
|
|
|
@@ -1664,11 +1686,12 @@ export async function createModule() {
|
|
|
1664
1686
|
});
|
|
1665
1687
|
}
|
|
1666
1688
|
|
|
1667
|
-
const config: ModuleConfig = {
|
|
1689
|
+
const config: ModuleConfig & { description?: string } = {
|
|
1668
1690
|
slug,
|
|
1669
1691
|
moduleName,
|
|
1670
1692
|
pages,
|
|
1671
1693
|
tables,
|
|
1694
|
+
description,
|
|
1672
1695
|
};
|
|
1673
1696
|
|
|
1674
1697
|
// Trouver le répertoire racine du workspace (chercher pnpm-workspace.yaml)
|
|
@@ -38,7 +38,7 @@ export async function deleteModule() {
|
|
|
38
38
|
|
|
39
39
|
const answers = await inquirer.prompt([
|
|
40
40
|
{
|
|
41
|
-
type: "
|
|
41
|
+
type: "select",
|
|
42
42
|
name: "moduleName",
|
|
43
43
|
message: "Quel module voulez-vous supprimer du monorepo ?",
|
|
44
44
|
choices: AVAILABLE_MODULES.map((m) => ({
|
|
@@ -73,11 +73,13 @@ export async function deleteModule() {
|
|
|
73
73
|
);
|
|
74
74
|
|
|
75
75
|
// 1. Supprimer le répertoire du module
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
// Extraire le nom du package correctement (gérer @lastbrain et @lastbrain-labs)
|
|
77
|
+
const packageName = moduleMeta.package
|
|
78
|
+
.replace("@lastbrain-labs/", "")
|
|
79
|
+
.replace("@lastbrain/", "");
|
|
80
|
+
|
|
81
|
+
const moduleDir = path.join(rootDir, "packages", packageName);
|
|
82
|
+
|
|
81
83
|
if (fs.existsSync(moduleDir)) {
|
|
82
84
|
console.log(chalk.yellow(`📁 Suppression du répertoire: ${moduleDir}`));
|
|
83
85
|
await fs.remove(moduleDir);
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import fs from "fs-extra";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
import {
|
|
5
|
-
AVAILABLE_MODULES,
|
|
6
|
-
type ModuleMetadata,
|
|
7
|
-
} from "@lastbrain/core/config/modules";
|
|
4
|
+
import { AVAILABLE_MODULES } from "@lastbrain/core/config/modules";
|
|
8
5
|
|
|
9
6
|
export async function listModules(targetDir: string) {
|
|
10
7
|
console.log(chalk.blue("\n📦 Modules disponibles:\n"));
|