@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/dist/scripts/init-app.js
CHANGED
|
@@ -53,7 +53,7 @@ export async function initApp(options) {
|
|
|
53
53
|
// 3. Créer la structure Next.js
|
|
54
54
|
await createNextStructure(targetDir, force, useHeroUI, withAuth);
|
|
55
55
|
// 4. Créer les fichiers de configuration
|
|
56
|
-
await createConfigFiles(targetDir, force, useHeroUI);
|
|
56
|
+
await createConfigFiles(targetDir, force, useHeroUI, projectName);
|
|
57
57
|
// 5. Créer le système de proxy storage
|
|
58
58
|
await createStorageProxy(targetDir, force);
|
|
59
59
|
// 6. Créer .gitignore et .env.local.example
|
|
@@ -90,6 +90,17 @@ export async function initApp(options) {
|
|
|
90
90
|
console.log(chalk.yellow("🔧 Génération des routes des modules...\n"));
|
|
91
91
|
execSync("pnpm build:modules", { cwd: targetDir, stdio: "inherit" });
|
|
92
92
|
console.log(chalk.green("\n✓ Routes des modules générées\n"));
|
|
93
|
+
console.log(chalk.yellow("📜 Synchronisation des migrations des modules...\n"));
|
|
94
|
+
try {
|
|
95
|
+
execSync("pnpm db:migrations:sync", {
|
|
96
|
+
cwd: targetDir,
|
|
97
|
+
stdio: "inherit",
|
|
98
|
+
});
|
|
99
|
+
console.log(chalk.green("\n✓ Migrations synchronisées\n"));
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.log(chalk.yellow("\n⚠️ Erreur de synchronisation des migrations\n"));
|
|
103
|
+
}
|
|
93
104
|
console.log(chalk.yellow("🗄️ Initialisation de la base de données...\n"));
|
|
94
105
|
try {
|
|
95
106
|
execSync("pnpm db:init", { cwd: targetDir, stdio: "inherit" });
|
|
@@ -132,6 +143,7 @@ export async function initApp(options) {
|
|
|
132
143
|
console.log(chalk.white(` cd ${relativePath}`));
|
|
133
144
|
console.log(chalk.white(" pnpm install"));
|
|
134
145
|
console.log(chalk.white(" pnpm build:modules"));
|
|
146
|
+
console.log(chalk.white(" pnpm db:migrations:sync"));
|
|
135
147
|
console.log(chalk.white(" pnpm db:init"));
|
|
136
148
|
console.log(chalk.white(" pnpm dev\n"));
|
|
137
149
|
}
|
|
@@ -141,10 +153,9 @@ export async function initApp(options) {
|
|
|
141
153
|
console.log(chalk.white(" 1. cd " + relativePath));
|
|
142
154
|
console.log(chalk.white(" 2. pnpm install (installer les dépendances)"));
|
|
143
155
|
console.log(chalk.white(" 3. pnpm build:modules (générer les routes des modules)"));
|
|
144
|
-
console.log(chalk.white(" 4. pnpm db:
|
|
145
|
-
console.log(chalk.white(" 5. pnpm
|
|
146
|
-
console.log(chalk.white("
|
|
147
|
-
console.log(chalk.white(" 5. pnpm dev (démarrer le serveur)\n"));
|
|
156
|
+
console.log(chalk.white(" 4. pnpm db:migrations:sync (synchroniser les migrations)"));
|
|
157
|
+
console.log(chalk.white(" 5. pnpm db:init (initialiser la base de données)"));
|
|
158
|
+
console.log(chalk.white(" 6. pnpm dev (lancer le serveur)\n"));
|
|
148
159
|
console.log(chalk.gray("Prérequis pour Supabase :"));
|
|
149
160
|
console.log(chalk.white(" - Docker Desktop installé et lancé"));
|
|
150
161
|
console.log(chalk.white(" - Supabase CLI : brew install supabase/tap/supabase\n"));
|
|
@@ -359,41 +370,19 @@ async function createNextStructure(targetDir, force, useHeroUI, withAuth) {
|
|
|
359
370
|
if (!fs.existsSync(layoutDest) || force) {
|
|
360
371
|
let layoutContent = "";
|
|
361
372
|
if (useHeroUI) {
|
|
362
|
-
// Layout avec HeroUI
|
|
373
|
+
// Layout avec HeroUI - Server Component
|
|
363
374
|
layoutContent = `// GENERATED BY LASTBRAIN APP-SHELL
|
|
364
|
-
|
|
365
|
-
"use client";
|
|
375
|
+
// Server Component pour permettre le SSR des pages enfants
|
|
366
376
|
|
|
367
377
|
import "../styles/globals.css";
|
|
368
|
-
import {
|
|
369
|
-
|
|
370
|
-
import { ThemeProvider } from "next-themes";
|
|
371
|
-
import { useRouter } from "next/navigation";
|
|
372
|
-
import { AppProviders } from "../components/AppProviders";
|
|
378
|
+
import { ClientLayout } from "../components/ClientLayout";
|
|
373
379
|
import type { PropsWithChildren } from "react";
|
|
374
|
-
import { AppHeader } from "../components/AppHeader";
|
|
375
380
|
|
|
376
381
|
export default function RootLayout({ children }: PropsWithChildren<{}>) {
|
|
377
|
-
const router = useRouter();
|
|
378
|
-
|
|
379
382
|
return (
|
|
380
383
|
<html lang="fr" suppressHydrationWarning>
|
|
381
384
|
<body className="min-h-screen">
|
|
382
|
-
<
|
|
383
|
-
<ThemeProvider
|
|
384
|
-
attribute="class"
|
|
385
|
-
defaultTheme="dark"
|
|
386
|
-
enableSystem={false}
|
|
387
|
-
storageKey="lastbrain-theme"
|
|
388
|
-
>
|
|
389
|
-
<AppProviders>
|
|
390
|
-
<AppHeader />
|
|
391
|
-
<div className="min-h-screen text-foreground bg-background">
|
|
392
|
-
{children}
|
|
393
|
-
</div>
|
|
394
|
-
</AppProviders>
|
|
395
|
-
</ThemeProvider>
|
|
396
|
-
</HeroUIProvider>
|
|
385
|
+
<ClientLayout>{children}</ClientLayout>
|
|
397
386
|
</body>
|
|
398
387
|
</html>
|
|
399
388
|
);
|
|
@@ -401,34 +390,19 @@ export default function RootLayout({ children }: PropsWithChildren<{}>) {
|
|
|
401
390
|
`;
|
|
402
391
|
}
|
|
403
392
|
else {
|
|
404
|
-
// Layout Tailwind CSS uniquement
|
|
393
|
+
// Layout Tailwind CSS uniquement - Server Component
|
|
405
394
|
layoutContent = `// GENERATED BY LASTBRAIN APP-SHELL
|
|
406
|
-
|
|
407
|
-
"use client";
|
|
395
|
+
// Server Component pour permettre le SSR des pages enfants
|
|
408
396
|
|
|
409
397
|
import "../styles/globals.css";
|
|
410
|
-
import {
|
|
411
|
-
import { AppProviders } from "../components/AppProviders";
|
|
398
|
+
import { ClientLayout } from "../components/ClientLayout";
|
|
412
399
|
import type { PropsWithChildren } from "react";
|
|
413
|
-
import { AppHeader } from "../components/AppHeader";
|
|
414
400
|
|
|
415
401
|
export default function RootLayout({ children }: PropsWithChildren<{}>) {
|
|
416
402
|
return (
|
|
417
403
|
<html lang="fr" suppressHydrationWarning>
|
|
418
404
|
<body className="min-h-screen">
|
|
419
|
-
<
|
|
420
|
-
attribute="class"
|
|
421
|
-
defaultTheme="light"
|
|
422
|
-
enableSystem={false}
|
|
423
|
-
storageKey="lastbrain-theme"
|
|
424
|
-
>
|
|
425
|
-
<AppProviders>
|
|
426
|
-
<AppHeader />
|
|
427
|
-
<div className="min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-white">
|
|
428
|
-
{children}
|
|
429
|
-
</div>
|
|
430
|
-
</AppProviders>
|
|
431
|
-
</ThemeProvider>
|
|
405
|
+
<ClientLayout>{children}</ClientLayout>
|
|
432
406
|
</body>
|
|
433
407
|
</html>
|
|
434
408
|
);
|
|
@@ -457,9 +431,16 @@ export default function RootLayout({ children }: PropsWithChildren<{}>) {
|
|
|
457
431
|
const homePageContent = `// GENERATED BY LASTBRAIN APP-SHELL
|
|
458
432
|
|
|
459
433
|
import { SimpleHomePage } from "@lastbrain/app";
|
|
434
|
+
import { Footer } from "@lastbrain/ui";
|
|
435
|
+
import { footerConfig } from "../config/footer";
|
|
460
436
|
|
|
461
437
|
export default function RootPage() {
|
|
462
|
-
return
|
|
438
|
+
return (
|
|
439
|
+
<>
|
|
440
|
+
<SimpleHomePage showAuth={${withAuth}} />
|
|
441
|
+
<Footer config={footerConfig} />
|
|
442
|
+
</>
|
|
443
|
+
);
|
|
463
444
|
}
|
|
464
445
|
`;
|
|
465
446
|
await fs.writeFile(homePagePath, homePageContent);
|
|
@@ -504,6 +485,8 @@ export default function NotFound() {
|
|
|
504
485
|
await createRoute(appDir, "admin", "admin", force);
|
|
505
486
|
await createRoute(appDir, "auth", "auth", force);
|
|
506
487
|
await createRoute(appDir, "docs", "public", force);
|
|
488
|
+
// Créer le composant ClientLayout (wrapper client avec providers)
|
|
489
|
+
await createClientLayout(targetDir, force, useHeroUI);
|
|
507
490
|
// Créer le composant AppHeader
|
|
508
491
|
await createAppHeader(targetDir, force);
|
|
509
492
|
// Créer le composant AppAside
|
|
@@ -511,6 +494,81 @@ export default function NotFound() {
|
|
|
511
494
|
// Créer le wrapper AppProviders avec configuration realtime
|
|
512
495
|
await createAppProvidersWrapper(targetDir, force);
|
|
513
496
|
}
|
|
497
|
+
async function createClientLayout(targetDir, force, useHeroUI) {
|
|
498
|
+
const componentsDir = path.join(targetDir, "components");
|
|
499
|
+
await fs.ensureDir(componentsDir);
|
|
500
|
+
const clientLayoutPath = path.join(componentsDir, "ClientLayout.tsx");
|
|
501
|
+
if (!fs.existsSync(clientLayoutPath) || force) {
|
|
502
|
+
let clientLayoutContent = "";
|
|
503
|
+
if (useHeroUI) {
|
|
504
|
+
// ClientLayout avec HeroUI
|
|
505
|
+
clientLayoutContent = `"use client";
|
|
506
|
+
|
|
507
|
+
import { HeroUIProvider } from "@heroui/system";
|
|
508
|
+
import { ThemeProvider } from "next-themes";
|
|
509
|
+
import { useRouter } from "next/navigation";
|
|
510
|
+
import { AppProviders } from "./AppProviders";
|
|
511
|
+
import { AppHeader } from "./AppHeader";
|
|
512
|
+
import type { ReactNode } from "react";
|
|
513
|
+
|
|
514
|
+
export function ClientLayout({ children }: { children: ReactNode }) {
|
|
515
|
+
const router = useRouter();
|
|
516
|
+
|
|
517
|
+
return (
|
|
518
|
+
<HeroUIProvider navigate={router.push}>
|
|
519
|
+
<ThemeProvider
|
|
520
|
+
attribute="class"
|
|
521
|
+
defaultTheme="dark"
|
|
522
|
+
enableSystem={false}
|
|
523
|
+
storageKey="lastbrain-theme"
|
|
524
|
+
>
|
|
525
|
+
<AppProviders>
|
|
526
|
+
<AppHeader />
|
|
527
|
+
<div className="min-h-screen text-foreground bg-background">
|
|
528
|
+
{children}
|
|
529
|
+
</div>
|
|
530
|
+
</AppProviders>
|
|
531
|
+
</ThemeProvider>
|
|
532
|
+
</HeroUIProvider>
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
`;
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
// ClientLayout Tailwind CSS uniquement
|
|
539
|
+
clientLayoutContent = `"use client";
|
|
540
|
+
|
|
541
|
+
import { ThemeProvider } from "next-themes";
|
|
542
|
+
import { AppProviders } from "./AppProviders";
|
|
543
|
+
import { AppHeader } from "./AppHeader";
|
|
544
|
+
import type { ReactNode } from "react";
|
|
545
|
+
|
|
546
|
+
export function ClientLayout({ children }: { children: ReactNode }) {
|
|
547
|
+
return (
|
|
548
|
+
<ThemeProvider
|
|
549
|
+
attribute="class"
|
|
550
|
+
defaultTheme="light"
|
|
551
|
+
enableSystem={false}
|
|
552
|
+
storageKey="lastbrain-theme"
|
|
553
|
+
>
|
|
554
|
+
<AppProviders>
|
|
555
|
+
<AppHeader />
|
|
556
|
+
<div className="min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-white">
|
|
557
|
+
{children}
|
|
558
|
+
</div>
|
|
559
|
+
</AppProviders>
|
|
560
|
+
</ThemeProvider>
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
`;
|
|
564
|
+
}
|
|
565
|
+
await fs.writeFile(clientLayoutPath, clientLayoutContent);
|
|
566
|
+
console.log(chalk.green("✓ components/ClientLayout.tsx créé"));
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
console.log(chalk.gray(" components/ClientLayout.tsx existe déjà (utilisez --force pour écraser)"));
|
|
570
|
+
}
|
|
571
|
+
}
|
|
514
572
|
async function createRoute(appDir, routeName, layoutType, force) {
|
|
515
573
|
const routeDir = path.join(appDir, routeName);
|
|
516
574
|
await fs.ensureDir(routeDir);
|
|
@@ -553,11 +611,26 @@ export default function AuthLayout({
|
|
|
553
611
|
}`;
|
|
554
612
|
}
|
|
555
613
|
else {
|
|
556
|
-
// Layout standard pour les autres routes
|
|
614
|
+
// Layout standard pour les autres routes (ex: docs = public)
|
|
557
615
|
const layoutComponent = layoutType.charAt(0).toUpperCase() + layoutType.slice(1) + "Layout";
|
|
558
|
-
|
|
616
|
+
if (routeName === "docs") {
|
|
617
|
+
// Layout docs avec footer
|
|
618
|
+
layoutContent = `import { ${layoutComponent} } from "@lastbrain/app";
|
|
619
|
+
import { footerConfig } from "../../config/footer";
|
|
620
|
+
|
|
621
|
+
export default function DocsLayout({
|
|
622
|
+
children,
|
|
623
|
+
}: {
|
|
624
|
+
children: React.ReactNode;
|
|
625
|
+
}) {
|
|
626
|
+
return <${layoutComponent} footerConfig={footerConfig}>{children}</${layoutComponent}>;
|
|
627
|
+
}`;
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
layoutContent = `import { ${layoutComponent} } from "@lastbrain/app";
|
|
559
631
|
|
|
560
632
|
export default ${layoutComponent};`;
|
|
633
|
+
}
|
|
561
634
|
}
|
|
562
635
|
await fs.writeFile(layoutPath, layoutContent);
|
|
563
636
|
console.log(chalk.green(`✓ app/${routeName}/layout.tsx créé`));
|
|
@@ -721,7 +794,7 @@ export function AppProviders({ children }: { children: ReactNode }) {
|
|
|
721
794
|
console.log(chalk.gray(" components/AppProviders.tsx existe déjà (utilisez --force pour écraser)"));
|
|
722
795
|
}
|
|
723
796
|
}
|
|
724
|
-
async function createConfigFiles(targetDir, force, useHeroUI) {
|
|
797
|
+
async function createConfigFiles(targetDir, force, useHeroUI, projectName) {
|
|
725
798
|
console.log(chalk.yellow("\n⚙️ Création des fichiers de configuration..."));
|
|
726
799
|
// middleware.ts - Protection des routes /auth/*, /admin/* et /api/admin/*
|
|
727
800
|
const middlewarePath = path.join(targetDir, "middleware.ts");
|
|
@@ -733,6 +806,24 @@ export async function middleware(request: NextRequest) {
|
|
|
733
806
|
const { pathname } = request.nextUrl;
|
|
734
807
|
const isApi = pathname.startsWith("/api/");
|
|
735
808
|
|
|
809
|
+
// Pages publiques d'authentification (ne pas protéger)
|
|
810
|
+
const publicAuthPages = [
|
|
811
|
+
"/signin",
|
|
812
|
+
"/signup",
|
|
813
|
+
"/reset-password",
|
|
814
|
+
"/forgot-password",
|
|
815
|
+
"/callback",
|
|
816
|
+
];
|
|
817
|
+
|
|
818
|
+
const isPublicAuthPage = publicAuthPages.some((page) =>
|
|
819
|
+
pathname.startsWith(page)
|
|
820
|
+
);
|
|
821
|
+
|
|
822
|
+
// Ne pas protéger les pages publiques d'authentification
|
|
823
|
+
if (isPublicAuthPage) {
|
|
824
|
+
return NextResponse.next();
|
|
825
|
+
}
|
|
826
|
+
|
|
736
827
|
// Protéger les routes /auth/* (espace membre)
|
|
737
828
|
if (pathname.startsWith("/auth")) {
|
|
738
829
|
try {
|
|
@@ -741,9 +832,9 @@ export async function middleware(request: NextRequest) {
|
|
|
741
832
|
data: { session },
|
|
742
833
|
} = await supabase.auth.getSession();
|
|
743
834
|
|
|
744
|
-
// Pas de session → nettoyage des cookies + redirection vers /
|
|
835
|
+
// Pas de session → nettoyage des cookies + redirection vers /signin
|
|
745
836
|
if (!session) {
|
|
746
|
-
const redirectUrl = new URL("/
|
|
837
|
+
const redirectUrl = new URL("/signin", request.url);
|
|
747
838
|
redirectUrl.searchParams.set("redirect", pathname);
|
|
748
839
|
const res = NextResponse.redirect(redirectUrl);
|
|
749
840
|
res.cookies.delete("sb-access-token");
|
|
@@ -756,7 +847,7 @@ export async function middleware(request: NextRequest) {
|
|
|
756
847
|
return response;
|
|
757
848
|
} catch (error) {
|
|
758
849
|
console.error("Middleware auth error:", error);
|
|
759
|
-
const res = NextResponse.redirect(new URL("/
|
|
850
|
+
const res = NextResponse.redirect(new URL("/signin", request.url));
|
|
760
851
|
res.cookies.delete("sb-access-token");
|
|
761
852
|
res.cookies.delete("sb-refresh-token");
|
|
762
853
|
res.cookies.delete("sb:token");
|
|
@@ -773,7 +864,7 @@ export async function middleware(request: NextRequest) {
|
|
|
773
864
|
data: { session },
|
|
774
865
|
} = await supabase.auth.getSession();
|
|
775
866
|
|
|
776
|
-
// Pas de session → 401 JSON pour API, sinon redirection vers /
|
|
867
|
+
// Pas de session → 401 JSON pour API, sinon redirection vers /signin avec nettoyage cookies
|
|
777
868
|
if (!session) {
|
|
778
869
|
if (isApi) {
|
|
779
870
|
const res = NextResponse.json({ error: "Non authentifié" }, { status: 401 });
|
|
@@ -783,7 +874,7 @@ export async function middleware(request: NextRequest) {
|
|
|
783
874
|
res.cookies.delete("sb:refresh-token");
|
|
784
875
|
return res;
|
|
785
876
|
}
|
|
786
|
-
const redirectUrl = new URL("/
|
|
877
|
+
const redirectUrl = new URL("/signin", request.url);
|
|
787
878
|
redirectUrl.searchParams.set("redirect", pathname);
|
|
788
879
|
const res = NextResponse.redirect(redirectUrl);
|
|
789
880
|
res.cookies.delete("sb-access-token");
|
|
@@ -847,6 +938,9 @@ export const config = {
|
|
|
847
938
|
const nextConfig = `/** @type {import('next').NextConfig} */
|
|
848
939
|
const nextConfig = {
|
|
849
940
|
reactStrictMode: true,
|
|
941
|
+
devIndicators: {
|
|
942
|
+
position: 'bottom-right',
|
|
943
|
+
},
|
|
850
944
|
};
|
|
851
945
|
|
|
852
946
|
export default nextConfig;
|
|
@@ -983,6 +1077,25 @@ export const menuConfig: MenuConfig = {
|
|
|
983
1077
|
await fs.writeFile(menuConfigPath, menuConfig);
|
|
984
1078
|
console.log(chalk.green("✓ config/menu.ts créé"));
|
|
985
1079
|
}
|
|
1080
|
+
// config/footer.ts
|
|
1081
|
+
const footerConfigPath = path.join(configDir, "footer.ts");
|
|
1082
|
+
if (!fs.existsSync(footerConfigPath) || force) {
|
|
1083
|
+
const footerConfig = `// Auto-generated footer configuration
|
|
1084
|
+
// Run "node ../../scripts/generate-footer-config.js ./apps/[your-app]" to regenerate
|
|
1085
|
+
"use client";
|
|
1086
|
+
|
|
1087
|
+
import type { FooterConfig } from "@lastbrain/ui";
|
|
1088
|
+
|
|
1089
|
+
export const footerConfig: FooterConfig = {
|
|
1090
|
+
companyName: "${projectName}",
|
|
1091
|
+
companyDescription: "Application LastBrain",
|
|
1092
|
+
links: [],
|
|
1093
|
+
social: [],
|
|
1094
|
+
};
|
|
1095
|
+
`;
|
|
1096
|
+
await fs.writeFile(footerConfigPath, footerConfig);
|
|
1097
|
+
console.log(chalk.green("✓ config/footer.ts créé"));
|
|
1098
|
+
}
|
|
986
1099
|
// Créer les hooks
|
|
987
1100
|
await createHooksDirectory(targetDir, force);
|
|
988
1101
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AAy4CA,wBAAsB,cAAc,kBA+FnC"}
|
|
@@ -29,7 +29,10 @@ async function loadModuleConfigs() {
|
|
|
29
29
|
}
|
|
30
30
|
const packageName = module.package;
|
|
31
31
|
try {
|
|
32
|
-
|
|
32
|
+
// Extraire le suffix du module (la partie après @lastbrain/module- ou @lastbrain-labs/module-)
|
|
33
|
+
const moduleSuffix = packageName
|
|
34
|
+
.replace("@lastbrain/module-", "")
|
|
35
|
+
.replace("@lastbrain-labs/module-", "");
|
|
33
36
|
const possibleConfigNames = [
|
|
34
37
|
`${moduleSuffix}.build.config`,
|
|
35
38
|
"build.config",
|
|
@@ -119,9 +122,10 @@ function toPascalCase(value) {
|
|
|
119
122
|
.join("");
|
|
120
123
|
}
|
|
121
124
|
function buildPage(moduleConfig, page) {
|
|
122
|
-
// Extraire le préfixe du module (ex: @lastbrain/module-auth -> auth)
|
|
125
|
+
// Extraire le préfixe du module (ex: @lastbrain/module-auth -> auth, @lastbrain-labs/module-recipes-pro -> recipes-pro)
|
|
123
126
|
const modulePrefix = moduleConfig.moduleName
|
|
124
127
|
.replace(/^@lastbrain\/module-/, "")
|
|
128
|
+
.replace(/^@lastbrain-labs\/module-/, "")
|
|
125
129
|
.toLowerCase();
|
|
126
130
|
if (isDebugMode) {
|
|
127
131
|
console.log(`🔄 Building page for module ${modulePrefix}: ${page.path}`);
|
|
@@ -468,7 +472,13 @@ function generateDocsPage(moduleConfigs) {
|
|
|
468
472
|
const moduleConfigurations = [];
|
|
469
473
|
allModules.forEach((moduleEntry) => {
|
|
470
474
|
const moduleName = moduleEntry.package;
|
|
471
|
-
|
|
475
|
+
// Extraire le nom du module sans le scope et sans "module-"
|
|
476
|
+
// Ex: @lastbrain/module-auth -> auth
|
|
477
|
+
// Ex: @lastbrain-labs/module-recipes-pro -> recipes
|
|
478
|
+
const moduleId = moduleName
|
|
479
|
+
.replace("@lastbrain-labs/module-", "")
|
|
480
|
+
.replace("@lastbrain/module-", "")
|
|
481
|
+
.replace(/-pro$/, ""); // Retirer le suffix -pro pour avoir le nom de base
|
|
472
482
|
const docComponentName = `${toPascalCase(moduleId)}ModuleDoc`;
|
|
473
483
|
// Trouver la config du module pour obtenir la description
|
|
474
484
|
const moduleConfig = moduleConfigs.find((mc) => mc.moduleName === moduleName);
|
|
@@ -480,8 +490,8 @@ function generateDocsPage(moduleConfigs) {
|
|
|
480
490
|
docImports.push(`import { ${docComponentName} } from "${moduleName}";`);
|
|
481
491
|
}
|
|
482
492
|
const config = {
|
|
483
|
-
id:
|
|
484
|
-
name: `Module ${
|
|
493
|
+
id: moduleName, // Utiliser le nom complet du package comme ID
|
|
494
|
+
name: `Module ${toPascalCase(moduleId)}`,
|
|
485
495
|
description: description,
|
|
486
496
|
component: docComponentName,
|
|
487
497
|
active: moduleEntry.active,
|
|
@@ -1027,7 +1037,6 @@ import { NextRequest, NextResponse } from "next/server";
|
|
|
1027
1037
|
* GENERATED FILE - DO NOT EDIT MANUALLY
|
|
1028
1038
|
* Generated at: ${timestamp}
|
|
1029
1039
|
* Generated from module storage configurations
|
|
1030
|
-
*
|
|
1031
1040
|
* Buckets configurés:
|
|
1032
1041
|
${allBuckets.map((b) => ` * - ${b.name} (${b.public ? "public" : "private"})`).join("\n")}
|
|
1033
1042
|
*/
|
|
@@ -1120,6 +1129,53 @@ export async function GET(
|
|
|
1120
1129
|
console.error("❌ Error generating storage proxy API:", error);
|
|
1121
1130
|
}
|
|
1122
1131
|
}
|
|
1132
|
+
async function generateFooterConfig(moduleConfigs) {
|
|
1133
|
+
try {
|
|
1134
|
+
// Extraire tous les liens footer des modules
|
|
1135
|
+
const allFooterLinks = moduleConfigs
|
|
1136
|
+
.filter((config) => config.footer && config.footer.length > 0)
|
|
1137
|
+
.flatMap((config) => config.footer);
|
|
1138
|
+
if (allFooterLinks.length === 0) {
|
|
1139
|
+
console.log("⏭️ No footer links found, skipping footer config generation");
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
// Trier les liens par position et order
|
|
1143
|
+
allFooterLinks.sort((a, b) => {
|
|
1144
|
+
const posOrder = { left: 0, center: 1, right: 2 };
|
|
1145
|
+
const posA = posOrder[a.position || "left"] || 0;
|
|
1146
|
+
const posB = posOrder[b.position || "left"] || 0;
|
|
1147
|
+
if (posA !== posB)
|
|
1148
|
+
return posA - posB;
|
|
1149
|
+
return (a.order || 0) - (b.order || 0);
|
|
1150
|
+
});
|
|
1151
|
+
const timestamp = new Date().toISOString();
|
|
1152
|
+
const content = `// Auto-generated footer configuration
|
|
1153
|
+
// Generated from module build configs
|
|
1154
|
+
// Generated at: ${timestamp}
|
|
1155
|
+
"use client";
|
|
1156
|
+
|
|
1157
|
+
import type { FooterConfig } from "@lastbrain/ui";
|
|
1158
|
+
|
|
1159
|
+
export const footerConfig: FooterConfig = {
|
|
1160
|
+
companyName: "LastBrain",
|
|
1161
|
+
companyDescription: "Plateforme de développement rapide d'applications",
|
|
1162
|
+
links: ${JSON.stringify(allFooterLinks, null, 2)},
|
|
1163
|
+
social: [],
|
|
1164
|
+
};
|
|
1165
|
+
`;
|
|
1166
|
+
const configDir = path.join(projectRoot, "config");
|
|
1167
|
+
ensureDirectory(configDir);
|
|
1168
|
+
const footerPath = path.join(configDir, "footer.ts");
|
|
1169
|
+
fs.writeFileSync(footerPath, content);
|
|
1170
|
+
if (isDebugMode) {
|
|
1171
|
+
console.log(`✅ Generated footer config: ${footerPath}`);
|
|
1172
|
+
console.log(` - ${allFooterLinks.length} footer link(s)`);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
catch (error) {
|
|
1176
|
+
console.error("❌ Error generating footer config:", error);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1123
1179
|
export async function runModuleBuild() {
|
|
1124
1180
|
ensureDirectory(appDirectory);
|
|
1125
1181
|
// Nettoyer les fichiers générés précédemment
|
|
@@ -1184,6 +1240,11 @@ export async function runModuleBuild() {
|
|
|
1184
1240
|
console.log("🔌 Generating storage proxy API...");
|
|
1185
1241
|
}
|
|
1186
1242
|
await generateStorageProxyApi(moduleConfigs);
|
|
1243
|
+
// Générer la configuration footer
|
|
1244
|
+
if (isDebugMode) {
|
|
1245
|
+
console.log("🦶 Generating footer configuration...");
|
|
1246
|
+
}
|
|
1247
|
+
await generateFooterConfig(moduleConfigs);
|
|
1187
1248
|
// Message de succès final
|
|
1188
1249
|
if (!isDebugMode) {
|
|
1189
1250
|
console.log("\n✅ Module build completed successfully!");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAwwCD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAiB1C;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,iBAoLhB;AAED;;GAEG;AACH,wBAAsB,YAAY,kBAsLjC"}
|
|
@@ -911,6 +911,11 @@ async function generateModuleReadme(config, moduleDir) {
|
|
|
911
911
|
const moduleNameClean = config.slug.replace("module-", "");
|
|
912
912
|
let md = `# 📦 Module ${moduleNameClean}\n\n`;
|
|
913
913
|
md += `> ${config.moduleName}\n\n`;
|
|
914
|
+
// Description section
|
|
915
|
+
if (config.description) {
|
|
916
|
+
md += `## 📝 Description\n\n`;
|
|
917
|
+
md += `${config.description}\n\n`;
|
|
918
|
+
}
|
|
914
919
|
// Information section
|
|
915
920
|
md += `## 📋 Informations\n\n`;
|
|
916
921
|
md += `- **Nom du package**: \`${config.moduleName}\`\n`;
|
|
@@ -1286,6 +1291,18 @@ export async function createModule() {
|
|
|
1286
1291
|
},
|
|
1287
1292
|
filter: (input) => input.trim().toLowerCase(),
|
|
1288
1293
|
},
|
|
1294
|
+
{
|
|
1295
|
+
type: "input",
|
|
1296
|
+
name: "description",
|
|
1297
|
+
message: "Description du module (une ligne):",
|
|
1298
|
+
default: "Module LastBrain",
|
|
1299
|
+
validate: (input) => {
|
|
1300
|
+
if (!input || input.trim() === "") {
|
|
1301
|
+
return "La description est requise";
|
|
1302
|
+
}
|
|
1303
|
+
return true;
|
|
1304
|
+
},
|
|
1305
|
+
},
|
|
1289
1306
|
{
|
|
1290
1307
|
type: "input",
|
|
1291
1308
|
name: "pagesPublic",
|
|
@@ -1314,6 +1331,7 @@ export async function createModule() {
|
|
|
1314
1331
|
// Construire la configuration du module
|
|
1315
1332
|
const slug = `module-${answers.slug}`;
|
|
1316
1333
|
const moduleName = `@lastbrain/${slug}`;
|
|
1334
|
+
const description = answers.description;
|
|
1317
1335
|
const pages = [];
|
|
1318
1336
|
// Pages publiques
|
|
1319
1337
|
const publicPages = parsePagesList(answers.pagesPublic);
|
|
@@ -1388,6 +1406,7 @@ export async function createModule() {
|
|
|
1388
1406
|
moduleName,
|
|
1389
1407
|
pages,
|
|
1390
1408
|
tables,
|
|
1409
|
+
description,
|
|
1391
1410
|
};
|
|
1392
1411
|
// Trouver le répertoire racine du workspace (chercher pnpm-workspace.yaml)
|
|
1393
1412
|
let rootDir;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-delete.d.ts","sourceRoot":"","sources":["../../src/scripts/module-delete.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,wBAAsB,YAAY,
|
|
1
|
+
{"version":3,"file":"module-delete.d.ts","sourceRoot":"","sources":["../../src/scripts/module-delete.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,wBAAsB,YAAY,kBAkLjC"}
|
|
@@ -22,7 +22,7 @@ export async function deleteModule() {
|
|
|
22
22
|
}
|
|
23
23
|
const answers = await inquirer.prompt([
|
|
24
24
|
{
|
|
25
|
-
type: "
|
|
25
|
+
type: "select",
|
|
26
26
|
name: "moduleName",
|
|
27
27
|
message: "Quel module voulez-vous supprimer du monorepo ?",
|
|
28
28
|
choices: AVAILABLE_MODULES.map((m) => ({
|
|
@@ -49,7 +49,11 @@ export async function deleteModule() {
|
|
|
49
49
|
}
|
|
50
50
|
console.log(chalk.blue(`\n🗑️ Suppression du module ${moduleMeta.package}...\n`));
|
|
51
51
|
// 1. Supprimer le répertoire du module
|
|
52
|
-
|
|
52
|
+
// Extraire le nom du package correctement (gérer @lastbrain et @lastbrain-labs)
|
|
53
|
+
const packageName = moduleMeta.package
|
|
54
|
+
.replace("@lastbrain-labs/", "")
|
|
55
|
+
.replace("@lastbrain/", "");
|
|
56
|
+
const moduleDir = path.join(rootDir, "packages", packageName);
|
|
53
57
|
if (fs.existsSync(moduleDir)) {
|
|
54
58
|
console.log(chalk.yellow(`📁 Suppression du répertoire: ${moduleDir}`));
|
|
55
59
|
await fs.remove(moduleDir);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-list.d.ts","sourceRoot":"","sources":["../../src/scripts/module-list.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"module-list.d.ts","sourceRoot":"","sources":["../../src/scripts/module-list.ts"],"names":[],"mappings":"AAKA,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,iBAwElD"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "fs-extra";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
import { AVAILABLE_MODULES
|
|
4
|
+
import { AVAILABLE_MODULES } from "@lastbrain/core/config/modules";
|
|
5
5
|
export async function listModules(targetDir) {
|
|
6
6
|
console.log(chalk.blue("\n📦 Modules disponibles:\n"));
|
|
7
7
|
// Lire la config des modules installés
|