@lastbrain/app 2.0.31 → 2.0.35
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/analytics/registry.d.ts +7 -0
- package/dist/analytics/registry.d.ts.map +1 -0
- package/dist/analytics/registry.js +11 -0
- package/dist/auth/useAuthSession.d.ts.map +1 -1
- package/dist/auth/useAuthSession.js +85 -1
- package/dist/cli.js +19 -3
- package/dist/components/LanguageSwitcher.d.ts.map +1 -1
- package/dist/components/LanguageSwitcher.js +89 -5
- package/dist/config/version.d.ts.map +1 -1
- package/dist/config/version.js +30 -19
- package/dist/i18n/useLink.d.ts.map +1 -1
- package/dist/i18n/useLink.js +15 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/layouts/AdminLayoutWithSidebar.d.ts +3 -1
- package/dist/layouts/AdminLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/AdminLayoutWithSidebar.js +2 -2
- package/dist/layouts/AppProviders.d.ts +7 -1
- package/dist/layouts/AppProviders.d.ts.map +1 -1
- package/dist/layouts/AppProviders.js +24 -3
- package/dist/layouts/AuthLayout.js +1 -1
- package/dist/layouts/PublicLayout.js +1 -1
- package/dist/layouts/RootLayout.d.ts.map +1 -1
- package/dist/scripts/init-app.d.ts.map +1 -1
- package/dist/scripts/init-app.js +301 -138
- package/dist/scripts/module-build.d.ts.map +1 -1
- package/dist/scripts/module-build.js +402 -67
- package/dist/scripts/module-create.d.ts.map +1 -1
- package/dist/scripts/module-create.js +227 -10
- package/dist/scripts/sitemap-flat-generator.d.ts +39 -0
- package/dist/scripts/sitemap-flat-generator.d.ts.map +1 -0
- package/dist/scripts/sitemap-flat-generator.js +231 -0
- package/dist/scripts/sitemap-manifest-generator.d.ts +59 -0
- package/dist/scripts/sitemap-manifest-generator.d.ts.map +1 -0
- package/dist/scripts/sitemap-manifest-generator.js +290 -0
- package/dist/sitemap/manifest.d.ts +8 -0
- package/dist/sitemap/manifest.d.ts.map +1 -0
- package/dist/sitemap/manifest.js +6 -0
- package/dist/styles.css +2 -2
- package/dist/templates/AuthGuidePage.js +2 -0
- package/dist/templates/DefaultDoc.d.ts.map +1 -1
- package/dist/templates/DefaultDoc.js +9 -5
- package/dist/templates/DocPage.d.ts.map +1 -1
- package/dist/templates/DocPage.js +40 -0
- package/dist/templates/MigrationsGuidePage.js +2 -0
- package/dist/templates/ModuleGuidePage.d.ts.map +1 -1
- package/dist/templates/ModuleGuidePage.js +4 -1
- package/dist/templates/SimpleHomePage.js +2 -0
- package/package.json +11 -4
- package/src/analytics/registry.ts +14 -0
- package/src/auth/useAuthSession.ts +91 -1
- package/src/cli.ts +19 -3
- package/src/components/LanguageSwitcher.tsx +113 -23
- package/src/config/version.ts +30 -19
- package/src/i18n/useLink.ts +15 -0
- package/src/index.ts +17 -0
- package/src/layouts/AdminLayoutWithSidebar.tsx +4 -0
- package/src/layouts/AppProviders.tsx +66 -8
- package/src/layouts/AuthLayout.tsx +1 -1
- package/src/layouts/PublicLayout.tsx +1 -1
- package/src/layouts/RootLayout.tsx +0 -1
- package/src/scripts/init-app.ts +360 -149
- package/src/scripts/module-build.ts +458 -72
- package/src/scripts/module-create.ts +260 -10
- package/src/scripts/sitemap-flat-generator.ts +313 -0
- package/src/scripts/sitemap-manifest-generator.ts +476 -0
- package/src/sitemap/manifest.ts +17 -0
- package/src/templates/AuthGuidePage.tsx +1 -1
- package/src/templates/DefaultDoc.tsx +397 -6
- package/src/templates/DocPage.tsx +40 -0
- package/src/templates/MigrationsGuidePage.tsx +1 -1
- package/src/templates/ModuleGuidePage.tsx +3 -2
- package/src/templates/SimpleHomePage.tsx +1 -1
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
|
+
import { generateManifestBasedSitemaps } from "./sitemap-manifest-generator.js";
|
|
5
|
+
const LOCALE_MAP = {
|
|
6
|
+
fr: "fr_FR",
|
|
7
|
+
en: "en_US",
|
|
8
|
+
es: "es_ES",
|
|
9
|
+
};
|
|
4
10
|
// Utiliser PROJECT_ROOT si défini (pour pnpm --filter), sinon process.cwd()
|
|
5
11
|
const projectRoot = process.env.PROJECT_ROOT || process.cwd();
|
|
6
12
|
// Si on est dans une app, monter jusqu'à la racine du monorepo
|
|
@@ -186,7 +192,7 @@ function buildPage(moduleConfig, page) {
|
|
|
186
192
|
page.path.includes("users/[id]") &&
|
|
187
193
|
page.componentExport === "UserPage";
|
|
188
194
|
// Détecter les pages légales qui utilisent cookies (privacy, terms, returns)
|
|
189
|
-
const
|
|
195
|
+
const _isLegalPage = page.section === "public" &&
|
|
190
196
|
(page.path.includes("privacy") ||
|
|
191
197
|
page.path.includes("terms") ||
|
|
192
198
|
page.path.includes("returns"));
|
|
@@ -196,6 +202,7 @@ function buildPage(moduleConfig, page) {
|
|
|
196
202
|
// On importe directement depuis app/config au lieu de passer via props
|
|
197
203
|
content = `// GENERATED BY LASTBRAIN MODULE BUILD
|
|
198
204
|
import { UserDetailPage } from "${moduleConfig.moduleName}";
|
|
205
|
+
import { logger } from "@lastbrain/core";
|
|
199
206
|
|
|
200
207
|
interface UserPageProps { params: Promise<{ id: string }> }
|
|
201
208
|
|
|
@@ -205,7 +212,7 @@ async function getModuleUserTabs() {
|
|
|
205
212
|
const { moduleUserTabs } = await import("../../../../../../config/user-tabs");
|
|
206
213
|
return moduleUserTabs || [];
|
|
207
214
|
} catch (e) {
|
|
208
|
-
|
|
215
|
+
logger.warn("[user-detail-wrapper] erreur chargement user-tabs", e);
|
|
209
216
|
return [];
|
|
210
217
|
}
|
|
211
218
|
}
|
|
@@ -228,17 +235,6 @@ const ${page.componentExport} = dynamic(
|
|
|
228
235
|
{ ssr: false }
|
|
229
236
|
);
|
|
230
237
|
|
|
231
|
-
export default function ${wrapperName}${hasDynamicParams ? "(props: Record<string, unknown>)" : "()"} {
|
|
232
|
-
return <${page.componentExport} ${hasDynamicParams ? "{...props}" : ""} />;
|
|
233
|
-
}
|
|
234
|
-
`;
|
|
235
|
-
}
|
|
236
|
-
else if (isLegalPage) {
|
|
237
|
-
content = `// GENERATED BY LASTBRAIN MODULE BUILD
|
|
238
|
-
import { ${page.componentExport} } from "${moduleConfig.moduleName}";
|
|
239
|
-
|
|
240
|
-
export const dynamic = 'force-dynamic';
|
|
241
|
-
|
|
242
238
|
export default function ${wrapperName}${hasDynamicParams ? "(props: Record<string, unknown>)" : "()"} {
|
|
243
239
|
return <${page.componentExport} ${hasDynamicParams ? "{...props}" : ""} />;
|
|
244
240
|
}
|
|
@@ -256,7 +252,7 @@ export default function ${wrapperName}${hasDynamicParams ? "(props: Record<strin
|
|
|
256
252
|
.filter((seg) => seg.startsWith("[") && seg.endsWith("]"))
|
|
257
253
|
.map((seg) => seg.slice(1, -1)); // Enlève les []
|
|
258
254
|
// Détecter si le module path lui-même a des params dynamiques (pas seulement [lang])
|
|
259
|
-
const
|
|
255
|
+
const _moduleHasDynamicParams = segments.some((seg) => seg.startsWith("[") && seg.endsWith("]"));
|
|
260
256
|
// Générer le type des params si nécessaire
|
|
261
257
|
const paramsType = dynamicParamNames.length > 0
|
|
262
258
|
? `{ ${dynamicParamNames.map((name) => `${name}: string`).join("; ")} }`
|
|
@@ -437,6 +433,7 @@ export interface MenuItem {
|
|
|
437
433
|
shortcut?: string;
|
|
438
434
|
shortcutDisplay?: string;
|
|
439
435
|
type?: 'text' | 'icon' | 'textIcon';
|
|
436
|
+
keyboardOnly?: boolean;
|
|
440
437
|
position?: 'center' | 'end';
|
|
441
438
|
component?: React.ComponentType<any>;
|
|
442
439
|
componentExport?: string;
|
|
@@ -537,7 +534,7 @@ function copyModuleMigrations(moduleConfigs) {
|
|
|
537
534
|
fs.writeFileSync(modulesJsonPath, JSON.stringify(modulesConfig, null, 2));
|
|
538
535
|
}
|
|
539
536
|
function generateDocsPage(moduleConfigs) {
|
|
540
|
-
const docsDir = path.join(appDirectory, "docs");
|
|
537
|
+
const docsDir = path.join(appDirectory, "[lang]", "(public)", "docs");
|
|
541
538
|
ensureDirectory(docsDir);
|
|
542
539
|
const docsPagePath = path.join(docsDir, "page.tsx");
|
|
543
540
|
// Charger tous les modules depuis modules.json (actifs ET inactifs)
|
|
@@ -557,13 +554,13 @@ function generateDocsPage(moduleConfigs) {
|
|
|
557
554
|
const moduleConfigurations = [];
|
|
558
555
|
allModules.forEach((moduleEntry) => {
|
|
559
556
|
const moduleName = moduleEntry.package;
|
|
560
|
-
// Extraire le nom du module sans le scope et sans "
|
|
557
|
+
// Extraire le nom du module sans le scope et sans "-pro"
|
|
561
558
|
// Ex: @lastbrain/module-auth -> auth
|
|
562
|
-
// Ex: @lastbrain-labs/module-
|
|
559
|
+
// Ex: @lastbrain-labs/module-metrics-pro -> metrics
|
|
563
560
|
const moduleId = moduleName
|
|
564
561
|
.replace("@lastbrain-labs/module-", "")
|
|
565
562
|
.replace("@lastbrain/module-", "")
|
|
566
|
-
.replace(/-pro$/, ""); // Retirer le
|
|
563
|
+
.replace(/-pro$/, ""); // Retirer le suffixe -pro
|
|
567
564
|
const docComponentName = `${toPascalCase(moduleId)}ModuleDoc`;
|
|
568
565
|
// Trouver la config du module pour obtenir la description
|
|
569
566
|
const moduleConfig = moduleConfigs.find((mc) => mc.moduleName === moduleName);
|
|
@@ -606,6 +603,43 @@ ${moduleConfigurations.join(",\n")}
|
|
|
606
603
|
if (isDebugMode) {
|
|
607
604
|
console.log(`📚 Generated docs page: ${docsPagePath}`);
|
|
608
605
|
}
|
|
606
|
+
// Generate docs layout with translation support
|
|
607
|
+
const docsLayoutPath = path.join(docsDir, "layout.tsx");
|
|
608
|
+
const docsLayoutContent = `// Auto-generated docs layout with translation support
|
|
609
|
+
import { loadTranslations } from "@lastbrain/app/i18n/server-lang";
|
|
610
|
+
import { ClientLayout } from "../../../../components/ClientLayout";
|
|
611
|
+
import { headers } from "next/headers";
|
|
612
|
+
|
|
613
|
+
// Extract language from request headers
|
|
614
|
+
async function getLanguageFromRequest(): Promise<string> {
|
|
615
|
+
const headersList = await headers();
|
|
616
|
+
const acceptLanguage = headersList.get("accept-language") || "";
|
|
617
|
+
// Detect if French is requested
|
|
618
|
+
if (acceptLanguage.toLowerCase().includes("fr")) {
|
|
619
|
+
return "fr";
|
|
620
|
+
}
|
|
621
|
+
return "en";
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
export default async function DocsLayout({
|
|
625
|
+
children,
|
|
626
|
+
}: {
|
|
627
|
+
children: React.ReactNode;
|
|
628
|
+
}) {
|
|
629
|
+
const lang = await getLanguageFromRequest();
|
|
630
|
+
const translations = await loadTranslations(lang);
|
|
631
|
+
|
|
632
|
+
return (
|
|
633
|
+
<ClientLayout lang={lang} translations={translations}>
|
|
634
|
+
<div className="min-h-screen pt-12">{children}</div>
|
|
635
|
+
</ClientLayout>
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
`;
|
|
639
|
+
fs.writeFileSync(docsLayoutPath, docsLayoutContent);
|
|
640
|
+
if (isDebugMode) {
|
|
641
|
+
console.log(`📚 Generated docs layout: ${docsLayoutPath}`);
|
|
642
|
+
}
|
|
609
643
|
}
|
|
610
644
|
function buildGroupedApi(apis, routePath) {
|
|
611
645
|
const segments = routePath.replace(/^\/+/, "").split("/").filter(Boolean);
|
|
@@ -657,7 +691,8 @@ function cleanGeneratedFiles() {
|
|
|
657
691
|
"page.tsx", // Page racine seulement
|
|
658
692
|
"admin/page.tsx", // Page admin racine
|
|
659
693
|
"admin/layout.tsx", // Layout admin racine
|
|
660
|
-
"docs/page.tsx", // Page docs générée
|
|
694
|
+
"[lang]/(public)/docs/page.tsx", // Page docs générée
|
|
695
|
+
"[lang]/(public)/docs/layout.tsx", // Layout docs généré
|
|
661
696
|
// Middleware et autres fichiers core
|
|
662
697
|
"middleware.ts",
|
|
663
698
|
// Dossiers de lib et config
|
|
@@ -727,7 +762,14 @@ function cleanGeneratedFiles() {
|
|
|
727
762
|
}
|
|
728
763
|
};
|
|
729
764
|
// Nettoyer les dossiers de sections
|
|
730
|
-
const sectionsToClean = [
|
|
765
|
+
const sectionsToClean = [
|
|
766
|
+
"(public)",
|
|
767
|
+
"auth",
|
|
768
|
+
"admin",
|
|
769
|
+
"api",
|
|
770
|
+
"[lang]",
|
|
771
|
+
"sitemap",
|
|
772
|
+
];
|
|
731
773
|
sectionsToClean.forEach((section) => {
|
|
732
774
|
const sectionPath = path.join(appDirectory, section);
|
|
733
775
|
if (fs.existsSync(sectionPath)) {
|
|
@@ -796,7 +838,7 @@ function cleanOldRoutesWithoutLang() {
|
|
|
796
838
|
}
|
|
797
839
|
});
|
|
798
840
|
}
|
|
799
|
-
function
|
|
841
|
+
function _generateAppAside() {
|
|
800
842
|
const targetPath = path.join(appDirectory, "components", "AppAside.tsx");
|
|
801
843
|
const templateContent = `"use client";
|
|
802
844
|
|
|
@@ -828,9 +870,11 @@ export function AppAside({ className = "", isVisible = true }: AppAsideProps) {
|
|
|
828
870
|
console.error(`❌ Error generating AppAside component: ${error}`);
|
|
829
871
|
}
|
|
830
872
|
}
|
|
831
|
-
function generateLayouts() {
|
|
832
|
-
//
|
|
833
|
-
const
|
|
873
|
+
function generateLayouts(moduleConfigs) {
|
|
874
|
+
// Vérifier si le module billing-pro est actif
|
|
875
|
+
const hasBillingModule = moduleConfigs.some((config) => config.moduleName === "@lastbrain-labs/module-billing-pro");
|
|
876
|
+
// Générer ClientLayout wrapper client (dans components/ racine)
|
|
877
|
+
const clientLayoutPath = path.join(projectRoot, "components", "ClientLayout.tsx");
|
|
834
878
|
const clientLayoutContent = `"use client";
|
|
835
879
|
|
|
836
880
|
import { HeroUIProvider } from "@heroui/system";
|
|
@@ -878,17 +922,26 @@ export function ClientLayout({
|
|
|
878
922
|
catch (error) {
|
|
879
923
|
console.error(`❌ Error generating ClientLayout component: ${error}`);
|
|
880
924
|
}
|
|
881
|
-
// Générer AppProviders wrapper client
|
|
882
|
-
const appProvidersPath = path.join(
|
|
925
|
+
// Générer AppProviders wrapper client (dans components/ racine)
|
|
926
|
+
const appProvidersPath = path.join(projectRoot, "components", "AppProviders.tsx");
|
|
927
|
+
// Import conditionnel de EntitlementsProvider
|
|
928
|
+
const entitlementsImport = hasBillingModule
|
|
929
|
+
? `import { EntitlementsProvider } from "@lastbrain-labs/module-billing-pro";`
|
|
930
|
+
: "";
|
|
931
|
+
// Prop conditionnel pour EntitlementsProviderComponent
|
|
932
|
+
const entitlementsProp = hasBillingModule
|
|
933
|
+
? `EntitlementsProviderComponent={EntitlementsProvider}`
|
|
934
|
+
: "";
|
|
883
935
|
const appProvidersContent = `"use client";
|
|
884
936
|
|
|
885
937
|
import { AppProviders as BaseAppProviders } from "@lastbrain/app";
|
|
886
|
-
|
|
938
|
+
${entitlementsImport}
|
|
939
|
+
import { realtimeConfig } from "../config/realtime";
|
|
887
940
|
import type { ReactNode } from "react";
|
|
888
941
|
import type { Language } from "@lastbrain/core";
|
|
889
942
|
|
|
890
943
|
export function AppProviders({
|
|
891
|
-
children,
|
|
944
|
+
children,
|
|
892
945
|
lang = "fr",
|
|
893
946
|
translations = {},
|
|
894
947
|
availableLanguages = ["fr", "en"],
|
|
@@ -904,6 +957,7 @@ export function AppProviders({
|
|
|
904
957
|
lang={lang}
|
|
905
958
|
translations={translations}
|
|
906
959
|
availableLanguages={availableLanguages}
|
|
960
|
+
${entitlementsProp}
|
|
907
961
|
>
|
|
908
962
|
{children}
|
|
909
963
|
</BaseAppProviders>
|
|
@@ -915,19 +969,26 @@ export function AppProviders({
|
|
|
915
969
|
catch (error) {
|
|
916
970
|
console.error(`❌ Error generating AppProviders component: ${error}`);
|
|
917
971
|
}
|
|
918
|
-
// Générer AppHeader component
|
|
919
|
-
const appHeaderPath = path.join(
|
|
972
|
+
// Générer AppHeader component (dans components/ racine)
|
|
973
|
+
const appHeaderPath = path.join(projectRoot, "components", "AppHeader.tsx");
|
|
920
974
|
const appHeaderContent = `"use client";
|
|
921
975
|
|
|
922
976
|
import { Header } from "@lastbrain/ui";
|
|
923
|
-
import { LanguageSwitcher } from "@lastbrain/app";
|
|
924
|
-
import { menuConfig } from "
|
|
925
|
-
import {
|
|
977
|
+
import { LanguageSwitcher, useAuth } from "@lastbrain/app";
|
|
978
|
+
import { menuConfig } from "../config/menu";
|
|
979
|
+
import { menuIgnored } from "../config/menu-ignored";
|
|
980
|
+
import { LOCALE_CONFIG } from "../config/locales.generated";
|
|
926
981
|
|
|
927
982
|
export function AppHeader() {
|
|
983
|
+
const { user, loading, isSuperAdmin } = useAuth();
|
|
984
|
+
|
|
928
985
|
return (
|
|
929
986
|
<Header
|
|
987
|
+
user={user}
|
|
988
|
+
isSuperAdmin={isSuperAdmin}
|
|
930
989
|
menuConfig={menuConfig}
|
|
990
|
+
accountMenu={menuConfig.account}
|
|
991
|
+
menuIgnored={menuIgnored}
|
|
931
992
|
languageSwitcher={
|
|
932
993
|
<LanguageSwitcher
|
|
933
994
|
variant="minimal"
|
|
@@ -995,7 +1056,7 @@ export default async function LangLayout({
|
|
|
995
1056
|
|
|
996
1057
|
return (
|
|
997
1058
|
<ClientLayout lang={validLang} translations={translations} availableLanguages={[...LOCALE_CONFIG.languages]}>
|
|
998
|
-
<div className="min-h-screen
|
|
1059
|
+
<div className="min-h-screen ">
|
|
999
1060
|
{children}
|
|
1000
1061
|
</div>
|
|
1001
1062
|
</ClientLayout>
|
|
@@ -1021,6 +1082,7 @@ export default async function LangLayout({
|
|
|
1021
1082
|
|
|
1022
1083
|
import { AuthLayoutWithSidebar, langHref, useLanguage } from "@lastbrain/app";
|
|
1023
1084
|
import { menuConfig as fullMenuConfig } from "../../../config/menu";
|
|
1085
|
+
import { menuIgnored } from "../../../config/menu-ignored";
|
|
1024
1086
|
|
|
1025
1087
|
export default function SectionLayout({
|
|
1026
1088
|
children,
|
|
@@ -1041,7 +1103,7 @@ export default function SectionLayout({
|
|
|
1041
1103
|
};
|
|
1042
1104
|
|
|
1043
1105
|
return (
|
|
1044
|
-
<AuthLayoutWithSidebar menuConfig={menuConfig}>
|
|
1106
|
+
<AuthLayoutWithSidebar menuConfig={menuConfig} menuIgnored={menuIgnored}>
|
|
1045
1107
|
{children}
|
|
1046
1108
|
</AuthLayoutWithSidebar>
|
|
1047
1109
|
);
|
|
@@ -1052,12 +1114,36 @@ export default function SectionLayout({
|
|
|
1052
1114
|
catch (error) {
|
|
1053
1115
|
console.error(`❌ Error generating auth layout: ${error}`);
|
|
1054
1116
|
}
|
|
1117
|
+
// Générer layout public avec footer
|
|
1118
|
+
const publicLayoutPath = path.join(appDirectory, "[lang]", "(public)", "layout.tsx");
|
|
1119
|
+
const publicLayoutContent = `"use client";
|
|
1120
|
+
|
|
1121
|
+
import type React from "react";
|
|
1122
|
+
import { PublicLayout } from "@lastbrain/app";
|
|
1123
|
+
import { footerConfig } from "../../../config/footer";
|
|
1124
|
+
|
|
1125
|
+
export default function PublicSectionLayout({
|
|
1126
|
+
children,
|
|
1127
|
+
}: {
|
|
1128
|
+
children: React.ReactNode;
|
|
1129
|
+
}) {
|
|
1130
|
+
return <PublicLayout footerConfig={footerConfig}>
|
|
1131
|
+
{children}
|
|
1132
|
+
</PublicLayout>;
|
|
1133
|
+
}`;
|
|
1134
|
+
try {
|
|
1135
|
+
writeScaffoldFile(publicLayoutPath, publicLayoutContent, "public layout with footer");
|
|
1136
|
+
}
|
|
1137
|
+
catch (error) {
|
|
1138
|
+
console.error(`❌ Error generating public layout: ${error}`);
|
|
1139
|
+
}
|
|
1055
1140
|
// Générer layout admin avec sidebar
|
|
1056
1141
|
const adminLayoutPath = path.join(appDirectory, "[lang]", "admin", "layout.tsx");
|
|
1057
1142
|
const adminLayoutContent = `"use client";
|
|
1058
1143
|
|
|
1059
1144
|
import { AdminLayoutWithSidebar, langHref, useLanguage } from "@lastbrain/app";
|
|
1060
1145
|
import { menuConfig as fullMenuConfig } from "../../../config/menu";
|
|
1146
|
+
import { menuIgnored } from "../../../config/menu-ignored";
|
|
1061
1147
|
|
|
1062
1148
|
export default function AdminLayout({
|
|
1063
1149
|
children,
|
|
@@ -1078,7 +1164,7 @@ export default function AdminLayout({
|
|
|
1078
1164
|
};
|
|
1079
1165
|
|
|
1080
1166
|
return (
|
|
1081
|
-
<AdminLayoutWithSidebar menuConfig={menuConfig}>
|
|
1167
|
+
<AdminLayoutWithSidebar menuConfig={menuConfig} menuIgnored={menuIgnored}>
|
|
1082
1168
|
{children}
|
|
1083
1169
|
</AdminLayoutWithSidebar>
|
|
1084
1170
|
);
|
|
@@ -1158,7 +1244,11 @@ async function generateUserTabsConfig(moduleConfigs) {
|
|
|
1158
1244
|
else {
|
|
1159
1245
|
// Générer les imports statiques (Next/dynamic pour chaque composant)
|
|
1160
1246
|
const importsForApp = userTabsConfigs
|
|
1161
|
-
.map((tab) =>
|
|
1247
|
+
.map((tab) => {
|
|
1248
|
+
// UserTokenTab est un Client Component, utiliser ssr: false
|
|
1249
|
+
const ssrMode = tab.componentExport === "UserTokenTab" ? "false" : "true";
|
|
1250
|
+
return `const ${tab.componentExport} = dynamic(() => import("${tab.moduleName}").then(mod => ({ default: mod.${tab.componentExport} })), { ssr: ${ssrMode} });`;
|
|
1251
|
+
})
|
|
1162
1252
|
.join("\n");
|
|
1163
1253
|
// Générer le tableau des tabs
|
|
1164
1254
|
const tabsArray = userTabsConfigs
|
|
@@ -1500,7 +1590,6 @@ export async function GET(
|
|
|
1500
1590
|
.createSignedUrl(storagePath, 3600); // 1 heure
|
|
1501
1591
|
|
|
1502
1592
|
if (error) {
|
|
1503
|
-
console.error(\`[storage] Error creating signed URL for public image:\`, error);
|
|
1504
1593
|
return new NextResponse("Not found", { status: 404 });
|
|
1505
1594
|
}
|
|
1506
1595
|
|
|
@@ -1521,16 +1610,23 @@ export async function GET(
|
|
|
1521
1610
|
|
|
1522
1611
|
// Cas spécial: si le chemin commence par /product/ ou /recipe/,
|
|
1523
1612
|
// c'est une image avec format court qui nécessite le préfixe userId
|
|
1613
|
+
// Sinon le chemin contient déjà le userId
|
|
1524
1614
|
let actualStoragePath = storagePath;
|
|
1525
|
-
|
|
1615
|
+
const pathParts = storagePath.split("/");
|
|
1526
1616
|
|
|
1527
|
-
|
|
1617
|
+
// Vérifier si le premier segment est un UUID (userId déjà présent)
|
|
1618
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1619
|
+
const firstSegmentIsUuid = uuidRegex.test(pathParts[0]);
|
|
1528
1620
|
|
|
1621
|
+
if (!firstSegmentIsUuid) {
|
|
1622
|
+
// Le chemin ne contient pas le userId, on l'ajoute
|
|
1623
|
+
actualStoragePath = \`\${user.id}/\${storagePath}\`;
|
|
1624
|
+
}
|
|
1529
1625
|
|
|
1530
1626
|
// Vérifier que l'utilisateur a accès à cette image
|
|
1531
1627
|
// Format: {userId}/recipe/{recipeId}/{filename} ou {userId}/product/{productId}/{filename}
|
|
1532
|
-
const
|
|
1533
|
-
const pathUserId =
|
|
1628
|
+
const actualPathParts = actualStoragePath.split("/");
|
|
1629
|
+
const pathUserId = actualPathParts[0];
|
|
1534
1630
|
|
|
1535
1631
|
if (pathUserId !== user.id) {
|
|
1536
1632
|
return new NextResponse("Forbidden", { status: 403 });
|
|
@@ -1539,17 +1635,31 @@ export async function GET(
|
|
|
1539
1635
|
// Créer une URL signée pour l'image privée
|
|
1540
1636
|
const { data, error } = await supabase.storage
|
|
1541
1637
|
.from(bucket)
|
|
1542
|
-
.createSignedUrl(actualStoragePath,
|
|
1638
|
+
.createSignedUrl(actualStoragePath, 60); // 1 minute seulement
|
|
1543
1639
|
|
|
1544
1640
|
if (error) {
|
|
1545
|
-
console.error(\`[storage] Error creating signed URL:\`, error);
|
|
1546
1641
|
return new NextResponse("Not found", { status: 404 });
|
|
1547
1642
|
}
|
|
1548
1643
|
|
|
1549
|
-
//
|
|
1550
|
-
|
|
1644
|
+
// Télécharger l'image et la retourner avec des headers no-cache
|
|
1645
|
+
const imageResponse = await fetch(data.signedUrl);
|
|
1646
|
+
if (!imageResponse.ok) {
|
|
1647
|
+
return new NextResponse("Not found", { status: 404 });
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
const imageBuffer = await imageResponse.arrayBuffer();
|
|
1651
|
+
const contentType = imageResponse.headers.get("content-type") || "image/jpeg";
|
|
1652
|
+
|
|
1653
|
+
return new NextResponse(imageBuffer, {
|
|
1654
|
+
status: 200,
|
|
1655
|
+
headers: {
|
|
1656
|
+
"Content-Type": contentType,
|
|
1657
|
+
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
1658
|
+
"Pragma": "no-cache",
|
|
1659
|
+
"Expires": "0",
|
|
1660
|
+
},
|
|
1661
|
+
});
|
|
1551
1662
|
} catch (error) {
|
|
1552
|
-
console.error("[storage] Error:", error);
|
|
1553
1663
|
return new NextResponse("Internal server error", { status: 500 });
|
|
1554
1664
|
}
|
|
1555
1665
|
}
|
|
@@ -1616,6 +1726,230 @@ export const footerConfig: FooterConfig = {
|
|
|
1616
1726
|
console.error("❌ Error generating footer config:", error);
|
|
1617
1727
|
}
|
|
1618
1728
|
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Génère les routes sitemap à partir des configurations des modules
|
|
1731
|
+
*/
|
|
1732
|
+
async function generateSitemapRoutes(moduleConfigs) {
|
|
1733
|
+
try {
|
|
1734
|
+
// Collecter tous les sitemaps de tous les modules
|
|
1735
|
+
const allSitemaps = [];
|
|
1736
|
+
moduleConfigs.forEach((config) => {
|
|
1737
|
+
if (config.sitemaps && config.sitemaps.length > 0) {
|
|
1738
|
+
config.sitemaps.forEach((sitemap) => {
|
|
1739
|
+
allSitemaps.push({ moduleConfig: config, sitemap });
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
});
|
|
1743
|
+
if (allSitemaps.length === 0) {
|
|
1744
|
+
if (isDebugMode) {
|
|
1745
|
+
console.log("⏭️ No sitemaps found, skipping sitemap generation");
|
|
1746
|
+
}
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
// Détecter les sitemaps index des modules (sitemap.xml ou sitemap-index)
|
|
1750
|
+
const moduleSitemapIndexes = [];
|
|
1751
|
+
// Renommer les /sitemap.xml en /sitemap-{moduleName}.xml pour éviter les conflits
|
|
1752
|
+
allSitemaps.forEach(({ moduleConfig, sitemap }) => {
|
|
1753
|
+
if (sitemap.path === "/sitemap.xml" ||
|
|
1754
|
+
sitemap.path.endsWith("-index.xml")) {
|
|
1755
|
+
// Extraire le nom court du module (ex: module-blog -> blog)
|
|
1756
|
+
const moduleShortName = moduleConfig.moduleName
|
|
1757
|
+
.replace("@lastbrain/", "")
|
|
1758
|
+
.replace("@lastbrain-labs/", "")
|
|
1759
|
+
.replace("module-", "")
|
|
1760
|
+
.replace("module-core-", "");
|
|
1761
|
+
// Renommer le path
|
|
1762
|
+
sitemap.path = `/sitemap-${moduleShortName}.xml`;
|
|
1763
|
+
moduleSitemapIndexes.push(sitemap.path);
|
|
1764
|
+
if (isDebugMode) {
|
|
1765
|
+
console.log(`🔄 Renamed sitemap from /sitemap.xml to ${sitemap.path} for ${moduleConfig.moduleName}`);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
});
|
|
1769
|
+
// Générer chaque route sitemap
|
|
1770
|
+
for (const { moduleConfig, sitemap } of allSitemaps) {
|
|
1771
|
+
const sitemapPath = sitemap.path;
|
|
1772
|
+
// Déterminer le chemin du fichier route.ts
|
|
1773
|
+
let routeDir;
|
|
1774
|
+
if (sitemapPath === "/sitemap.xml") {
|
|
1775
|
+
// Route principale sitemap.xml à la racine de app
|
|
1776
|
+
routeDir = path.join(appDirectory, "sitemap.xml");
|
|
1777
|
+
}
|
|
1778
|
+
else {
|
|
1779
|
+
// Sous-sitemaps (ex: /sitemap/recipes.xml -> app/sitemap/recipes.xml)
|
|
1780
|
+
const segments = sitemapPath
|
|
1781
|
+
.replace(/^\//, "")
|
|
1782
|
+
.replace(/\.xml$/, ".xml")
|
|
1783
|
+
.split("/");
|
|
1784
|
+
routeDir = path.join(appDirectory, ...segments);
|
|
1785
|
+
}
|
|
1786
|
+
const routeFilePath = path.join(routeDir, "route.ts");
|
|
1787
|
+
ensureDirectory(routeDir);
|
|
1788
|
+
// Déterminer le type de sitemap et générer le code approprié
|
|
1789
|
+
const entryPoint = sitemap.entryPoint;
|
|
1790
|
+
let content;
|
|
1791
|
+
if (entryPoint === "sitemap/index") {
|
|
1792
|
+
// Sitemap index principal
|
|
1793
|
+
content = `// GENERATED BY LASTBRAIN MODULE BUILD - Sitemap Index Route
|
|
1794
|
+
// Module: ${moduleConfig.moduleName}
|
|
1795
|
+
// Path: ${sitemapPath}
|
|
1796
|
+
import { generateSitemapIndex } from "${moduleConfig.moduleName}/sitemap";
|
|
1797
|
+
|
|
1798
|
+
export async function GET(): Promise<Response> {
|
|
1799
|
+
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || (process.env.VERCEL_URL ? \`https://\${process.env.VERCEL_URL}\` : "https://example.com");
|
|
1800
|
+
|
|
1801
|
+
// Liste des sous-sitemaps
|
|
1802
|
+
const sitemapPaths = [
|
|
1803
|
+
"/sitemap/pages.xml",
|
|
1804
|
+
"/sitemap/recipes.xml",
|
|
1805
|
+
"/sitemap/chefs.xml",
|
|
1806
|
+
];
|
|
1807
|
+
|
|
1808
|
+
const xml = generateSitemapIndex(baseUrl, sitemapPaths);
|
|
1809
|
+
|
|
1810
|
+
return new Response(xml, {
|
|
1811
|
+
headers: {
|
|
1812
|
+
"Content-Type": "application/xml",
|
|
1813
|
+
"Cache-Control": "public, max-age=3600, s-maxage=3600",
|
|
1814
|
+
},
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
`;
|
|
1818
|
+
}
|
|
1819
|
+
else if (entryPoint === "sitemap/pages") {
|
|
1820
|
+
// Sitemap des pages statiques
|
|
1821
|
+
content = `// GENERATED BY LASTBRAIN MODULE BUILD - Static Pages Sitemap Route
|
|
1822
|
+
// Module: ${moduleConfig.moduleName}
|
|
1823
|
+
// Path: ${sitemapPath}
|
|
1824
|
+
import { generateStaticPagesSitemap, generateSitemapXml } from "${moduleConfig.moduleName}/sitemap";
|
|
1825
|
+
|
|
1826
|
+
export async function GET(): Promise<Response> {
|
|
1827
|
+
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || (process.env.VERCEL_URL ? \`https://\${process.env.VERCEL_URL}\` : "https://example.com");
|
|
1828
|
+
|
|
1829
|
+
const sitemap = await generateStaticPagesSitemap(baseUrl);
|
|
1830
|
+
const xml = generateSitemapXml(sitemap);
|
|
1831
|
+
|
|
1832
|
+
return new Response(xml, {
|
|
1833
|
+
headers: {
|
|
1834
|
+
"Content-Type": "application/xml",
|
|
1835
|
+
"Cache-Control": "public, max-age=86400, s-maxage=86400",
|
|
1836
|
+
},
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
`;
|
|
1840
|
+
}
|
|
1841
|
+
else if (entryPoint === "sitemap/recipes") {
|
|
1842
|
+
// Sitemap des recettes
|
|
1843
|
+
content = `// GENERATED BY LASTBRAIN MODULE BUILD - Recipes Sitemap Route
|
|
1844
|
+
// Module: ${moduleConfig.moduleName}
|
|
1845
|
+
// Path: ${sitemapPath}
|
|
1846
|
+
import { generateRecipesSitemap, generateSitemapXml } from "${moduleConfig.moduleName}/sitemap";
|
|
1847
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
1848
|
+
|
|
1849
|
+
export async function GET(): Promise<Response> {
|
|
1850
|
+
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || (process.env.VERCEL_URL ? \`https://\${process.env.VERCEL_URL}\` : "https://example.com");
|
|
1851
|
+
const supabase = await getSupabaseServiceClient();
|
|
1852
|
+
|
|
1853
|
+
const sitemap = await generateRecipesSitemap(baseUrl, supabase);
|
|
1854
|
+
const xml = generateSitemapXml(sitemap);
|
|
1855
|
+
|
|
1856
|
+
return new Response(xml, {
|
|
1857
|
+
headers: {
|
|
1858
|
+
"Content-Type": "application/xml",
|
|
1859
|
+
"Cache-Control": "public, max-age=3600, s-maxage=3600",
|
|
1860
|
+
},
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
`;
|
|
1864
|
+
}
|
|
1865
|
+
else if (entryPoint === "sitemap/chefs") {
|
|
1866
|
+
// Sitemap des chefs
|
|
1867
|
+
content = `// GENERATED BY LASTBRAIN MODULE BUILD - Chefs Sitemap Route
|
|
1868
|
+
// Module: ${moduleConfig.moduleName}
|
|
1869
|
+
// Path: ${sitemapPath}
|
|
1870
|
+
import { generateChefsSitemap, generateSitemapXml } from "${moduleConfig.moduleName}/sitemap";
|
|
1871
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
1872
|
+
|
|
1873
|
+
export async function GET(): Promise<Response> {
|
|
1874
|
+
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || (process.env.VERCEL_URL ? \`https://\${process.env.VERCEL_URL}\` : "https://example.com");
|
|
1875
|
+
const supabase = await getSupabaseServiceClient();
|
|
1876
|
+
|
|
1877
|
+
const sitemap = await generateChefsSitemap(baseUrl, supabase);
|
|
1878
|
+
const xml = generateSitemapXml(sitemap);
|
|
1879
|
+
|
|
1880
|
+
return new Response(xml, {
|
|
1881
|
+
headers: {
|
|
1882
|
+
"Content-Type": "application/xml",
|
|
1883
|
+
"Cache-Control": "public, max-age=3600, s-maxage=3600",
|
|
1884
|
+
},
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
`;
|
|
1888
|
+
}
|
|
1889
|
+
else {
|
|
1890
|
+
// Sitemap générique - export direct du handler
|
|
1891
|
+
content = `// GENERATED BY LASTBRAIN MODULE BUILD - Sitemap route
|
|
1892
|
+
// Module: ${moduleConfig.moduleName}
|
|
1893
|
+
// Path: ${sitemapPath}
|
|
1894
|
+
export { ${sitemap.handlerExport} } from "${moduleConfig.moduleName}/${sitemap.entryPoint}";
|
|
1895
|
+
`;
|
|
1896
|
+
}
|
|
1897
|
+
fs.writeFileSync(routeFilePath, content);
|
|
1898
|
+
if (isDebugMode) {
|
|
1899
|
+
console.log(`🗺️ Generated sitemap route: ${routeFilePath}`);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
// Générer le sitemap.xml global qui agrège tous les sitemaps des modules
|
|
1903
|
+
if (moduleSitemapIndexes.length > 0) {
|
|
1904
|
+
const globalSitemapDir = path.join(appDirectory, "sitemap.xml");
|
|
1905
|
+
const globalSitemapFile = path.join(globalSitemapDir, "route.ts");
|
|
1906
|
+
ensureDirectory(globalSitemapDir);
|
|
1907
|
+
const sitemapUrls = moduleSitemapIndexes
|
|
1908
|
+
.map((path) => {
|
|
1909
|
+
return ` {
|
|
1910
|
+
url: \`\${baseUrl}${path}\`,
|
|
1911
|
+
lastModified: new Date(),
|
|
1912
|
+
}`;
|
|
1913
|
+
})
|
|
1914
|
+
.join(",\n");
|
|
1915
|
+
const globalSitemapContent = `// GENERATED BY LASTBRAIN MODULE BUILD - Global Sitemap Index
|
|
1916
|
+
// This file aggregates all module sitemaps
|
|
1917
|
+
// Module sitemaps: ${moduleSitemapIndexes.join(", ")}
|
|
1918
|
+
|
|
1919
|
+
export async function GET(): Promise<Response> {
|
|
1920
|
+
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || (process.env.VERCEL_URL ? \`https://\${process.env.VERCEL_URL}\` : "https://example.com");
|
|
1921
|
+
|
|
1922
|
+
const sitemapIndexXml = \`<?xml version="1.0" encoding="UTF-8"?>
|
|
1923
|
+
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
1924
|
+
${moduleSitemapIndexes
|
|
1925
|
+
.map((path) => ` <sitemap>
|
|
1926
|
+
<loc>\${baseUrl}${path}</loc>
|
|
1927
|
+
<lastmod>\${new Date().toISOString()}</lastmod>
|
|
1928
|
+
</sitemap>`)
|
|
1929
|
+
.join("\n")}
|
|
1930
|
+
</sitemapindex>\`;
|
|
1931
|
+
|
|
1932
|
+
return new Response(sitemapIndexXml, {
|
|
1933
|
+
headers: {
|
|
1934
|
+
"Content-Type": "application/xml",
|
|
1935
|
+
"Cache-Control": "public, max-age=3600, s-maxage=3600",
|
|
1936
|
+
},
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
`;
|
|
1940
|
+
fs.writeFileSync(globalSitemapFile, globalSitemapContent);
|
|
1941
|
+
if (isDebugMode) {
|
|
1942
|
+
console.log(`🌍 Generated global sitemap.xml aggregating ${moduleSitemapIndexes.length} module sitemap(s)`);
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
if (isDebugMode) {
|
|
1946
|
+
console.log(`✅ Generated ${allSitemaps.length} sitemap route(s)`);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
catch (error) {
|
|
1950
|
+
console.error("❌ Error generating sitemap routes:", error);
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1619
1953
|
/**
|
|
1620
1954
|
* Génère les fichiers i18n en concaténant les traductions des modules
|
|
1621
1955
|
* et en incluant les traductions personnalisées de i18n/default
|
|
@@ -1623,21 +1957,6 @@ export const footerConfig: FooterConfig = {
|
|
|
1623
1957
|
async function generateLocaleConfig() {
|
|
1624
1958
|
const appI18nDir = path.join(projectRoot, "i18n");
|
|
1625
1959
|
const appConfigDir = path.join(projectRoot, "config");
|
|
1626
|
-
// Mapping standard des codes de langue vers les locales
|
|
1627
|
-
const LOCALE_MAP = {
|
|
1628
|
-
fr: "fr_FR",
|
|
1629
|
-
en: "en_US",
|
|
1630
|
-
es: "es_ES",
|
|
1631
|
-
de: "de_DE",
|
|
1632
|
-
it: "it_IT",
|
|
1633
|
-
pt: "pt_PT",
|
|
1634
|
-
nl: "nl_NL",
|
|
1635
|
-
ru: "ru_RU",
|
|
1636
|
-
ja: "ja_JP",
|
|
1637
|
-
zh: "zh_CN",
|
|
1638
|
-
ar: "ar_SA",
|
|
1639
|
-
hi: "hi_IN",
|
|
1640
|
-
};
|
|
1641
1960
|
try {
|
|
1642
1961
|
// Scanner les fichiers i18n disponibles
|
|
1643
1962
|
let languages = ["fr"]; // Fallback par défaut
|
|
@@ -1856,15 +2175,19 @@ function generateHomePage(moduleConfigs) {
|
|
|
1856
2175
|
const content = `// GENERATED BY LASTBRAIN MODULE BUILD
|
|
1857
2176
|
// Homepage from ${homePageModule.moduleName}
|
|
1858
2177
|
import { ${importComponentName} as ModuleHomePage${homePageConfig.metadataExport ? `, ${metadataImportAlias}` : ""} } from "${importPath}";
|
|
1859
|
-
|
|
2178
|
+
|
|
1860
2179
|
import { LOCALE_CONFIG } from "../../../config/locales.generated";
|
|
1861
2180
|
${homePageConfig.metadataExport
|
|
1862
2181
|
? `\nexport async function generateMetadata(props: any) {
|
|
1863
2182
|
return ${homePageConfig.metadataExport}Original({ ...props, localeConfig: LOCALE_CONFIG });
|
|
1864
2183
|
}`
|
|
1865
2184
|
: ""}
|
|
1866
|
-
export default function ${wrapperComponentName}(
|
|
1867
|
-
|
|
2185
|
+
export default function ${wrapperComponentName}({
|
|
2186
|
+
params,
|
|
2187
|
+
}: {
|
|
2188
|
+
params: Promise<{ lang: string }>;
|
|
2189
|
+
}) {
|
|
2190
|
+
return <ModuleHomePage params={params} localeConfig={LOCALE_CONFIG} />;
|
|
1868
2191
|
}
|
|
1869
2192
|
`;
|
|
1870
2193
|
fs.writeFileSync(homePagePath, content, "utf-8");
|
|
@@ -1923,8 +2246,9 @@ export async function runModuleBuild() {
|
|
|
1923
2246
|
dumpNavigation();
|
|
1924
2247
|
generateMenuConfig(moduleConfigs);
|
|
1925
2248
|
generateDocsPage(moduleConfigs);
|
|
1926
|
-
|
|
1927
|
-
|
|
2249
|
+
// Note: AppAside, AppHeader, etc. sont maintenant générés dans components/ à la racine
|
|
2250
|
+
// et non plus dupliqués dans app/components/
|
|
2251
|
+
generateLayouts(moduleConfigs);
|
|
1928
2252
|
copyModuleMigrations(moduleConfigs);
|
|
1929
2253
|
// Générer la page d'accueil "/" si un module la définit
|
|
1930
2254
|
if (isDebugMode) {
|
|
@@ -1961,6 +2285,17 @@ export async function runModuleBuild() {
|
|
|
1961
2285
|
console.log("🦶 Generating footer configuration...");
|
|
1962
2286
|
}
|
|
1963
2287
|
await generateFooterConfig(moduleConfigs);
|
|
2288
|
+
// Générer les routes sitemap (ancien système)
|
|
2289
|
+
if (isDebugMode) {
|
|
2290
|
+
console.log("🗺️ Generating sitemap routes (legacy)...");
|
|
2291
|
+
}
|
|
2292
|
+
//await generateSitemapRoutes(moduleConfigs);
|
|
2293
|
+
// Générer les sitemaps basés sur manifests (nouveau système)
|
|
2294
|
+
if (isDebugMode) {
|
|
2295
|
+
console.log("🗺️ Generating manifest-based sitemaps...");
|
|
2296
|
+
}
|
|
2297
|
+
const availableLanguages = Object.keys(LOCALE_MAP);
|
|
2298
|
+
await generateManifestBasedSitemaps(appDirectory, moduleConfigs, availableLanguages, projectRequire, isDebugMode);
|
|
1964
2299
|
// Générer les fichiers i18n
|
|
1965
2300
|
if (isDebugMode) {
|
|
1966
2301
|
console.log("🌍 Building i18n files...");
|