@lastbrain/app 2.0.18 → 2.0.21
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/app-shell/not-found.js +2 -2
- package/dist/components/LanguageSwitcher.d.ts +7 -0
- package/dist/components/LanguageSwitcher.d.ts.map +1 -0
- package/dist/components/LanguageSwitcher.js +62 -0
- package/dist/config/version.d.ts.map +1 -1
- package/dist/config/version.js +19 -21
- package/dist/i18n/LanguageProvider.d.ts +4 -0
- package/dist/i18n/LanguageProvider.d.ts.map +1 -0
- package/dist/i18n/LanguageProvider.js +6 -0
- package/dist/i18n/TranslationsScript.d.ts +8 -0
- package/dist/i18n/TranslationsScript.d.ts.map +1 -0
- package/dist/i18n/TranslationsScript.js +10 -0
- package/dist/i18n/cookies.d.ts +6 -0
- package/dist/i18n/cookies.d.ts.map +1 -0
- package/dist/i18n/cookies.js +24 -0
- package/dist/i18n/langHrefHelper.d.ts +36 -0
- package/dist/i18n/langHrefHelper.d.ts.map +1 -0
- package/dist/i18n/langHrefHelper.js +41 -0
- package/dist/i18n/server-helpers.d.ts +33 -0
- package/dist/i18n/server-helpers.d.ts.map +1 -0
- package/dist/i18n/server-helpers.js +39 -0
- package/dist/i18n/server-lang.d.ts +10 -0
- package/dist/i18n/server-lang.d.ts.map +1 -0
- package/dist/i18n/server-lang.js +42 -0
- package/dist/i18n/server.d.ts +10 -0
- package/dist/i18n/server.d.ts.map +1 -0
- package/dist/i18n/server.js +8 -0
- package/dist/i18n/types.d.ts +38 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +4 -0
- package/dist/i18n/useLangRouter.d.ts +12 -0
- package/dist/i18n/useLangRouter.d.ts.map +1 -0
- package/dist/i18n/useLangRouter.js +18 -0
- package/dist/i18n/useLink.d.ts +34 -0
- package/dist/i18n/useLink.d.ts.map +1 -0
- package/dist/i18n/useLink.js +58 -0
- package/dist/i18n/useModuleTranslation.d.ts +11 -0
- package/dist/i18n/useModuleTranslation.d.ts.map +1 -0
- package/dist/i18n/useModuleTranslation.js +23 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/layouts/AdminLayout.js +1 -1
- package/dist/layouts/AppProviders.d.ts +4 -1
- package/dist/layouts/AppProviders.d.ts.map +1 -1
- package/dist/layouts/AppProviders.js +18 -8
- package/dist/layouts/AuthLayout.js +1 -1
- package/dist/layouts/AuthLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/AuthLayoutWithSidebar.js +5 -3
- package/dist/layouts/RootLayout.d.ts +4 -1
- package/dist/layouts/RootLayout.d.ts.map +1 -1
- package/dist/layouts/RootLayout.js +2 -2
- package/dist/scripts/i18n-build.d.ts +7 -0
- package/dist/scripts/i18n-build.d.ts.map +1 -0
- package/dist/scripts/i18n-build.js +100 -0
- package/dist/scripts/init-app.js +12 -12
- package/dist/scripts/module-build.d.ts.map +1 -1
- package/dist/scripts/module-build.js +384 -81
- package/dist/styles.css +1 -1
- package/dist/templates/DefaultDoc.d.ts.map +1 -1
- package/dist/templates/DefaultDoc.js +90 -2
- package/dist/templates/middleware-i18n.example.d.ts +16 -0
- package/dist/templates/middleware-i18n.example.d.ts.map +1 -0
- package/dist/templates/middleware-i18n.example.js +72 -0
- package/package.json +14 -3
- package/src/app-shell/not-found.tsx +2 -2
- package/src/components/LanguageSwitcher.tsx +156 -0
- package/src/config/version.ts +19 -21
- package/src/i18n/LanguageProvider.tsx +7 -0
- package/src/i18n/README_LANG_HELPERS.md +187 -0
- package/src/i18n/TranslationsScript.tsx +17 -0
- package/src/i18n/cookies.ts +24 -0
- package/src/i18n/langHrefHelper.ts +51 -0
- package/src/i18n/server-helpers.ts +48 -0
- package/src/i18n/server-lang.ts +51 -0
- package/src/i18n/server.ts +13 -0
- package/src/i18n/types.ts +39 -0
- package/src/i18n/useLangRouter.ts +21 -0
- package/src/i18n/useLink.ts +60 -0
- package/src/i18n/useModuleTranslation.ts +27 -0
- package/src/index.ts +20 -0
- package/src/layouts/AdminLayout.tsx +1 -1
- package/src/layouts/AppProviders.tsx +35 -16
- package/src/layouts/AuthLayout.tsx +1 -1
- package/src/layouts/AuthLayoutWithSidebar.tsx +5 -3
- package/src/layouts/RootLayout.tsx +12 -5
- package/src/scripts/i18n-build.ts +122 -0
- package/src/scripts/init-app.ts +12 -12
- package/src/scripts/module-build.ts +476 -94
- package/src/templates/DefaultDoc.tsx +511 -1
- package/src/templates/middleware-i18n.example.ts +92 -0
- package/dist/app-shell/(admin)/dashboard/page.d.ts +0 -2
- package/dist/app-shell/(admin)/dashboard/page.d.ts.map +0 -1
- package/dist/app-shell/(admin)/dashboard/page.js +0 -5
- package/dist/app-shell/(admin)/page.d.ts +0 -2
- package/dist/app-shell/(admin)/page.d.ts.map +0 -1
- package/dist/app-shell/(admin)/page.js +0 -5
- package/dist/app-shell/(auth)/dashboard/page.d.ts +0 -2
- package/dist/app-shell/(auth)/dashboard/page.d.ts.map +0 -1
- package/dist/app-shell/(auth)/dashboard/page.js +0 -5
- package/dist/app-shell/(auth)/page.d.ts +0 -2
- package/dist/app-shell/(auth)/page.d.ts.map +0 -1
- package/dist/app-shell/(auth)/page.js +0 -5
- package/dist/components/TableStructure.d.ts +0 -8
- package/dist/components/TableStructure.d.ts.map +0 -1
- package/dist/components/TableStructure.js +0 -37
- package/dist/hooks/useNotificationsSimple.d.ts +0 -20
- package/dist/hooks/useNotificationsSimple.d.ts.map +0 -1
- package/dist/hooks/useNotificationsSimple.js +0 -37
- package/dist/module-build.d.ts +0 -2
- package/dist/module-build.d.ts.map +0 -1
- package/dist/module-build.js +0 -50
- package/dist/modules/index.d.ts +0 -3
- package/dist/modules/index.d.ts.map +0 -1
- package/dist/modules/index.js +0 -2
- package/dist/src/__tests__/module-registry.test.d.ts +0 -2
- package/dist/src/__tests__/module-registry.test.d.ts.map +0 -1
- package/dist/src/__tests__/module-registry.test.js +0 -53
- package/dist/src/app-shell/(admin)/layout.d.ts +0 -4
- package/dist/src/app-shell/(admin)/layout.d.ts.map +0 -1
- package/dist/src/app-shell/(admin)/layout.js +0 -5
- package/dist/src/app-shell/(auth)/layout.d.ts +0 -4
- package/dist/src/app-shell/(auth)/layout.d.ts.map +0 -1
- package/dist/src/app-shell/(auth)/layout.js +0 -5
- package/dist/src/app-shell/(public)/page.d.ts +0 -2
- package/dist/src/app-shell/(public)/page.d.ts.map +0 -1
- package/dist/src/app-shell/(public)/page.js +0 -5
- package/dist/src/app-shell/layout.d.ts +0 -3
- package/dist/src/app-shell/layout.d.ts.map +0 -1
- package/dist/src/app-shell/layout.js +0 -3
- package/dist/src/app-shell/not-found.d.ts +0 -2
- package/dist/src/app-shell/not-found.d.ts.map +0 -1
- package/dist/src/app-shell/not-found.js +0 -10
- package/dist/src/auth/authHelpers.d.ts +0 -7
- package/dist/src/auth/authHelpers.d.ts.map +0 -1
- package/dist/src/auth/authHelpers.js +0 -19
- package/dist/src/auth/useAuthSession.d.ts +0 -7
- package/dist/src/auth/useAuthSession.d.ts.map +0 -1
- package/dist/src/auth/useAuthSession.js +0 -49
- package/dist/src/cli.d.ts +0 -3
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js +0 -143
- package/dist/src/components/NotificationContainer.d.ts +0 -2
- package/dist/src/components/NotificationContainer.d.ts.map +0 -1
- package/dist/src/components/NotificationContainer.js +0 -8
- package/dist/src/hooks/useNotifications.d.ts +0 -30
- package/dist/src/hooks/useNotifications.d.ts.map +0 -1
- package/dist/src/hooks/useNotifications.js +0 -165
- package/dist/src/index.d.ts +0 -22
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -22
- package/dist/src/layouts/AdminLayout.d.ts +0 -4
- package/dist/src/layouts/AdminLayout.d.ts.map +0 -1
- package/dist/src/layouts/AdminLayout.js +0 -4
- package/dist/src/layouts/AdminLayoutWithSidebar.d.ts +0 -10
- package/dist/src/layouts/AdminLayoutWithSidebar.d.ts.map +0 -1
- package/dist/src/layouts/AdminLayoutWithSidebar.js +0 -62
- package/dist/src/layouts/AppProviders.d.ts +0 -27
- package/dist/src/layouts/AppProviders.d.ts.map +0 -1
- package/dist/src/layouts/AppProviders.js +0 -48
- package/dist/src/layouts/AuthLayout.d.ts +0 -4
- package/dist/src/layouts/AuthLayout.d.ts.map +0 -1
- package/dist/src/layouts/AuthLayout.js +0 -4
- package/dist/src/layouts/AuthLayoutWithSidebar.d.ts +0 -12
- package/dist/src/layouts/AuthLayoutWithSidebar.d.ts.map +0 -1
- package/dist/src/layouts/AuthLayoutWithSidebar.js +0 -60
- package/dist/src/layouts/PublicLayout.d.ts +0 -8
- package/dist/src/layouts/PublicLayout.d.ts.map +0 -1
- package/dist/src/layouts/PublicLayout.js +0 -6
- package/dist/src/layouts/PublicLayoutWithSidebar.d.ts +0 -9
- package/dist/src/layouts/PublicLayoutWithSidebar.d.ts.map +0 -1
- package/dist/src/layouts/PublicLayoutWithSidebar.js +0 -60
- package/dist/src/layouts/RootLayout.d.ts +0 -6
- package/dist/src/layouts/RootLayout.d.ts.map +0 -1
- package/dist/src/layouts/RootLayout.js +0 -9
- package/dist/src/modules/module-loader.d.ts +0 -5
- package/dist/src/modules/module-loader.d.ts.map +0 -1
- package/dist/src/modules/module-loader.js +0 -10
- package/dist/src/scripts/db-init.d.ts +0 -2
- package/dist/src/scripts/db-init.d.ts.map +0 -1
- package/dist/src/scripts/db-init.js +0 -300
- package/dist/src/scripts/db-migrations-sync.d.ts +0 -2
- package/dist/src/scripts/db-migrations-sync.d.ts.map +0 -1
- package/dist/src/scripts/db-migrations-sync.js +0 -84
- package/dist/src/scripts/dev-sync.d.ts +0 -2
- package/dist/src/scripts/dev-sync.d.ts.map +0 -1
- package/dist/src/scripts/dev-sync.js +0 -194
- package/dist/src/scripts/init-app.d.ts +0 -12
- package/dist/src/scripts/init-app.d.ts.map +0 -1
- package/dist/src/scripts/init-app.js +0 -2175
- package/dist/src/scripts/module-add.d.ts +0 -2
- package/dist/src/scripts/module-add.d.ts.map +0 -1
- package/dist/src/scripts/module-add.js +0 -232
- package/dist/src/scripts/module-build.d.ts +0 -2
- package/dist/src/scripts/module-build.d.ts.map +0 -1
- package/dist/src/scripts/module-build.js +0 -1280
- package/dist/src/scripts/module-create.d.ts +0 -28
- package/dist/src/scripts/module-create.d.ts.map +0 -1
- package/dist/src/scripts/module-create.js +0 -1429
- package/dist/src/scripts/module-delete.d.ts +0 -6
- package/dist/src/scripts/module-delete.d.ts.map +0 -1
- package/dist/src/scripts/module-delete.js +0 -147
- package/dist/src/scripts/module-list.d.ts +0 -2
- package/dist/src/scripts/module-list.d.ts.map +0 -1
- package/dist/src/scripts/module-list.js +0 -61
- package/dist/src/scripts/module-remove.d.ts +0 -2
- package/dist/src/scripts/module-remove.d.ts.map +0 -1
- package/dist/src/scripts/module-remove.js +0 -311
- package/dist/src/scripts/readme-build.d.ts +0 -2
- package/dist/src/scripts/readme-build.d.ts.map +0 -1
- package/dist/src/scripts/readme-build.js +0 -39
- package/dist/src/scripts/script-runner.d.ts +0 -5
- package/dist/src/scripts/script-runner.d.ts.map +0 -1
- package/dist/src/scripts/script-runner.js +0 -25
- package/dist/src/templates/AuthGuidePage.d.ts +0 -2
- package/dist/src/templates/AuthGuidePage.d.ts.map +0 -1
- package/dist/src/templates/AuthGuidePage.js +0 -9
- package/dist/src/templates/DefaultDoc.d.ts +0 -2
- package/dist/src/templates/DefaultDoc.d.ts.map +0 -1
- package/dist/src/templates/DefaultDoc.js +0 -240
- package/dist/src/templates/DocPage.d.ts +0 -17
- package/dist/src/templates/DocPage.d.ts.map +0 -1
- package/dist/src/templates/DocPage.js +0 -193
- package/dist/src/templates/DocsPageWithModules.d.ts +0 -2
- package/dist/src/templates/DocsPageWithModules.d.ts.map +0 -1
- package/dist/src/templates/DocsPageWithModules.js +0 -8
- package/dist/src/templates/MigrationsGuidePage.d.ts +0 -2
- package/dist/src/templates/MigrationsGuidePage.d.ts.map +0 -1
- package/dist/src/templates/MigrationsGuidePage.js +0 -11
- package/dist/src/templates/ModuleGuidePage.d.ts +0 -2
- package/dist/src/templates/ModuleGuidePage.d.ts.map +0 -1
- package/dist/src/templates/ModuleGuidePage.js +0 -14
- package/dist/src/templates/SimpleDocPage.d.ts +0 -2
- package/dist/src/templates/SimpleDocPage.d.ts.map +0 -1
- package/dist/src/templates/SimpleDocPage.js +0 -28
- package/dist/src/templates/SimpleHomePage.d.ts +0 -6
- package/dist/src/templates/SimpleHomePage.d.ts.map +0 -1
- package/dist/src/templates/SimpleHomePage.js +0 -7
- package/dist/src/types/menu.d.ts +0 -23
- package/dist/src/types/menu.d.ts.map +0 -1
- package/dist/src/types/menu.js +0 -1
- package/dist/templates/HomePage.d.ts +0 -6
- package/dist/templates/HomePage.d.ts.map +0 -1
- package/dist/templates/HomePage.js +0 -6
- package/dist/templates/components/AppAside.d.ts +0 -6
- package/dist/templates/components/AppAside.d.ts.map +0 -1
- package/dist/templates/components/AppAside.js +0 -9
- package/dist/templates/layouts/admin-layout.d.ts +0 -4
- package/dist/templates/layouts/admin-layout.d.ts.map +0 -1
- package/dist/templates/layouts/admin-layout.js +0 -6
- package/dist/templates/layouts/auth-layout.d.ts +0 -4
- package/dist/templates/layouts/auth-layout.d.ts.map +0 -1
- package/dist/templates/layouts/auth-layout.js +0 -6
- package/dist/templates/migrations/20201010100000_init.sql +0 -123
|
@@ -22,6 +22,22 @@ const appDirectory = path.join(projectRoot, "app");
|
|
|
22
22
|
// Mode debug activé via --debug
|
|
23
23
|
const isDebugMode = process.argv.includes("--debug");
|
|
24
24
|
|
|
25
|
+
// Helper pour écrire (générer ou mettre à jour) un fichier de scaffold
|
|
26
|
+
function ensureDirectory(dir: string) {
|
|
27
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function writeScaffoldFile(targetPath: string, content: string, label: string) {
|
|
31
|
+
const existed = fs.existsSync(targetPath);
|
|
32
|
+
ensureDirectory(path.dirname(targetPath));
|
|
33
|
+
fs.writeFileSync(targetPath, content, "utf-8");
|
|
34
|
+
|
|
35
|
+
if (isDebugMode) {
|
|
36
|
+
const status = existed ? "♻️ Updated" : "✅ Generated";
|
|
37
|
+
console.log(`${status} ${label}: ${targetPath}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
25
41
|
// Créer un require dans le contexte de l'application pour résoudre les modules installés dans l'app
|
|
26
42
|
const projectRequire = createRequire(path.join(projectRoot, "package.json"));
|
|
27
43
|
|
|
@@ -93,10 +109,10 @@ async function loadModuleConfigs(): Promise<ModuleBuildConfig[]> {
|
|
|
93
109
|
}
|
|
94
110
|
|
|
95
111
|
const sectionDirectoryMap: Record<ModuleSection, string[]> = {
|
|
96
|
-
public: ["(public)"],
|
|
97
|
-
auth: ["auth"],
|
|
98
|
-
admin: ["admin"],
|
|
99
|
-
user: ["auth", "user"],
|
|
112
|
+
public: ["[lang]", "(public)"],
|
|
113
|
+
auth: ["[lang]", "auth"],
|
|
114
|
+
admin: ["[lang]", "admin"],
|
|
115
|
+
user: ["[lang]", "auth", "user"],
|
|
100
116
|
};
|
|
101
117
|
|
|
102
118
|
const sectionLayoutMap: Record<string, string> = {
|
|
@@ -121,10 +137,6 @@ interface MenuEntry {
|
|
|
121
137
|
section: ModuleSection;
|
|
122
138
|
}
|
|
123
139
|
|
|
124
|
-
function ensureDirectory(dir: string) {
|
|
125
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
126
|
-
}
|
|
127
|
-
|
|
128
140
|
function ensureSectionLayout(sectionPath: string[]) {
|
|
129
141
|
const sectionDir = path.join(appDirectory, ...sectionPath);
|
|
130
142
|
const sectionKey = sectionPath[0];
|
|
@@ -139,16 +151,14 @@ function ensureSectionLayout(sectionPath: string[]) {
|
|
|
139
151
|
const layoutName = sectionLayoutMap[sectionKey];
|
|
140
152
|
if (layoutName) {
|
|
141
153
|
const layoutPath = path.join(sectionDir, "layout.tsx");
|
|
142
|
-
|
|
143
|
-
|
|
154
|
+
const layoutContent = `import { ${layoutName} } from "@lastbrain/app";
|
|
155
|
+
import type React from "react";
|
|
144
156
|
|
|
145
157
|
export default function SectionLayout({ children }: { children: React.ReactNode }) {
|
|
146
158
|
return <${layoutName}>{children}</${layoutName}>;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
console.log(`📐 Generated section layout: ${layoutPath}`);
|
|
151
|
-
}
|
|
159
|
+
}`;
|
|
160
|
+
|
|
161
|
+
writeScaffoldFile(layoutPath, layoutContent, `${sectionKey} layout`);
|
|
152
162
|
}
|
|
153
163
|
|
|
154
164
|
generatedSections.add(sectionKey);
|
|
@@ -217,7 +227,9 @@ function buildPage(moduleConfig: ModuleBuildConfig, page: ModulePageConfig) {
|
|
|
217
227
|
const wrapperName = `${page.componentExport}${wrapperSuffix}Route`;
|
|
218
228
|
|
|
219
229
|
// Vérifier si la route a des paramètres dynamiques (segments avec [])
|
|
220
|
-
|
|
230
|
+
// Inclure les segments du sectionPath (ex: [lang]) ET les segments du module
|
|
231
|
+
const allSegments = [...sectionPath, ...segments];
|
|
232
|
+
const hasDynamicParams = allSegments.some(
|
|
221
233
|
(seg) => seg.startsWith("[") && seg.endsWith("]")
|
|
222
234
|
);
|
|
223
235
|
|
|
@@ -255,7 +267,7 @@ interface UserPageProps { params: Promise<{ id: string }> }
|
|
|
255
267
|
async function getModuleUserTabs() {
|
|
256
268
|
try {
|
|
257
269
|
// Depuis /app/admin/auth/users/[id]/ vers /apps/test-01/config/user-tabs
|
|
258
|
-
const { moduleUserTabs } = await import("
|
|
270
|
+
const { moduleUserTabs } = await import("../../../../../../config/user-tabs");
|
|
259
271
|
return moduleUserTabs || [];
|
|
260
272
|
} catch (e) {
|
|
261
273
|
console.warn("[user-detail-wrapper] erreur chargement user-tabs", e);
|
|
@@ -300,11 +312,35 @@ export default function ${wrapperName}${hasDynamicParams ? "(props: Record<strin
|
|
|
300
312
|
? `${moduleConfig.moduleName}/${page.entryPoint}`
|
|
301
313
|
: moduleConfig.moduleName;
|
|
302
314
|
|
|
315
|
+
// Extraire les noms des paramètres dynamiques pour le typage
|
|
316
|
+
// Inclure les segments du sectionPath (ex: [lang]) ET les segments du module
|
|
317
|
+
const allSegments = [...sectionPath, ...segments];
|
|
318
|
+
const dynamicParamNames = allSegments
|
|
319
|
+
.filter((seg) => seg.startsWith("[") && seg.endsWith("]"))
|
|
320
|
+
.map((seg) => seg.slice(1, -1)); // Enlève les []
|
|
321
|
+
|
|
322
|
+
// Générer le type des params si nécessaire
|
|
323
|
+
const paramsType =
|
|
324
|
+
dynamicParamNames.length > 0
|
|
325
|
+
? `{ ${dynamicParamNames.map((name) => `${name}: string`).join("; ")} }`
|
|
326
|
+
: "";
|
|
327
|
+
|
|
328
|
+
const propsSignature =
|
|
329
|
+
hasDynamicParams && paramsType
|
|
330
|
+
? `({\n params,\n}: {\n params: Promise<${paramsType}>;\n})`
|
|
331
|
+
: "()";
|
|
332
|
+
|
|
333
|
+
// Les composants utilisent useParams() en interne, on ne passe jamais params en props
|
|
334
|
+
// On fait juste l'unwrap pour Next.js 15 si des params existent
|
|
335
|
+
const awaitParams = hasDynamicParams
|
|
336
|
+
? "await params; // Unwrap params for Next.js 15\n "
|
|
337
|
+
: "";
|
|
338
|
+
|
|
303
339
|
content = `// GENERATED BY LASTBRAIN MODULE BUILD
|
|
304
340
|
import { ${page.componentExport} } from "${importPath}";
|
|
305
341
|
${page.metadataExport ? `\nexport { ${page.metadataExport} as generateMetadata } from "${importPath}";\n` : ""}
|
|
306
|
-
export default
|
|
307
|
-
return <${page.componentExport}
|
|
342
|
+
export default ${hasDynamicParams ? "async " : ""}function ${wrapperName}${propsSignature} {
|
|
343
|
+
${awaitParams}return <${page.componentExport} />;
|
|
308
344
|
}
|
|
309
345
|
`;
|
|
310
346
|
}
|
|
@@ -349,12 +385,16 @@ function generateMenuConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
349
385
|
ensureDirectory(configDir);
|
|
350
386
|
|
|
351
387
|
const menuPath = path.join(configDir, "menu.ts");
|
|
388
|
+
type AugmentedMenuItem = ModuleMenuItemConfig & {
|
|
389
|
+
moduleName?: string;
|
|
390
|
+
__componentRef?: string;
|
|
391
|
+
};
|
|
352
392
|
|
|
353
393
|
// Collecter les menus de tous les modules avec leurs modules sources
|
|
354
|
-
const publicMenus:
|
|
355
|
-
const authMenus:
|
|
356
|
-
const adminMenus:
|
|
357
|
-
const accountMenus:
|
|
394
|
+
const publicMenus: AugmentedMenuItem[] = [];
|
|
395
|
+
const authMenus: AugmentedMenuItem[] = [];
|
|
396
|
+
const adminMenus: AugmentedMenuItem[] = [];
|
|
397
|
+
const accountMenus: AugmentedMenuItem[] = [];
|
|
358
398
|
|
|
359
399
|
moduleConfigs.forEach((moduleConfig: ModuleBuildConfig) => {
|
|
360
400
|
if (moduleConfig.menu) {
|
|
@@ -419,14 +459,14 @@ function generateMenuConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
419
459
|
`const ${componentName} = dynamic(() => import("${menu.moduleName}").then(mod => ({ default: mod.${menu.componentExport} })), { ssr: false });`
|
|
420
460
|
);
|
|
421
461
|
// Ajouter une référence au composant
|
|
422
|
-
|
|
462
|
+
menu.__componentRef = componentName;
|
|
423
463
|
}
|
|
424
464
|
});
|
|
425
465
|
|
|
426
466
|
// Fonction pour préparer les menus avec les composants
|
|
427
|
-
const prepareMenusForExport = (menus:
|
|
467
|
+
const prepareMenusForExport = (menus: AugmentedMenuItem[]) => {
|
|
428
468
|
return menus.map((menu) => {
|
|
429
|
-
const { moduleName, __componentRef, ...cleanMenu } = menu;
|
|
469
|
+
const { moduleName: _moduleName, __componentRef, ...cleanMenu } = menu;
|
|
430
470
|
if (__componentRef) {
|
|
431
471
|
// Retourner une référence au composant au lieu de la sérialiser
|
|
432
472
|
return `{ ...${JSON.stringify(cleanMenu)}, component: ${__componentRef} }`;
|
|
@@ -698,10 +738,13 @@ function buildGroupedApi(
|
|
|
698
738
|
|
|
699
739
|
apis.forEach(({ moduleConfig, api }) => {
|
|
700
740
|
const handler = `${moduleConfig.moduleName}/${api.entryPoint}`;
|
|
701
|
-
|
|
702
|
-
|
|
741
|
+
const currentExports = exportsBySource.get(handler);
|
|
742
|
+
if (currentExports) {
|
|
743
|
+
currentExports.push(api.handlerExport);
|
|
744
|
+
return;
|
|
703
745
|
}
|
|
704
|
-
|
|
746
|
+
|
|
747
|
+
exportsBySource.set(handler, [api.handlerExport]);
|
|
705
748
|
});
|
|
706
749
|
|
|
707
750
|
// Générer les exports - un export statement par source
|
|
@@ -822,7 +865,7 @@ function cleanGeneratedFiles() {
|
|
|
822
865
|
};
|
|
823
866
|
|
|
824
867
|
// Nettoyer les dossiers de sections
|
|
825
|
-
const sectionsToClean = ["(public)", "auth", "admin", "api"];
|
|
868
|
+
const sectionsToClean = ["(public)", "auth", "admin", "api", "[lang]"];
|
|
826
869
|
sectionsToClean.forEach((section) => {
|
|
827
870
|
const sectionPath = path.join(appDirectory, section);
|
|
828
871
|
if (fs.existsSync(sectionPath)) {
|
|
@@ -847,17 +890,59 @@ function cleanGeneratedFiles() {
|
|
|
847
890
|
}
|
|
848
891
|
}
|
|
849
892
|
|
|
850
|
-
|
|
851
|
-
|
|
893
|
+
/**
|
|
894
|
+
* Supprime les anciennes routes sans [lang] et les layouts générés
|
|
895
|
+
*/
|
|
896
|
+
function cleanOldRoutesWithoutLang() {
|
|
897
|
+
const generatedComment = "// GENERATED BY LASTBRAIN MODULE BUILD";
|
|
852
898
|
|
|
853
|
-
//
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
899
|
+
// Supprimer les anciens dossiers de sections sans [lang]
|
|
900
|
+
const oldSections = ["(public)", "auth", "admin"];
|
|
901
|
+
|
|
902
|
+
oldSections.forEach((section) => {
|
|
903
|
+
const sectionPath = path.join(appDirectory, section);
|
|
904
|
+
if (fs.existsSync(sectionPath)) {
|
|
905
|
+
try {
|
|
906
|
+
// Supprimer récursivement tout le dossier
|
|
907
|
+
fs.rmSync(sectionPath, { recursive: true, force: true });
|
|
908
|
+
if (isDebugMode) {
|
|
909
|
+
console.log(`🗑️ Removed old section without [lang]: ${section}`);
|
|
910
|
+
}
|
|
911
|
+
} catch (error) {
|
|
912
|
+
console.warn(`⚠️ Could not remove ${section}:`, error);
|
|
913
|
+
}
|
|
857
914
|
}
|
|
858
|
-
|
|
859
|
-
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
// Supprimer les layouts générés dans [lang]
|
|
918
|
+
const langLayoutsToRemove = [
|
|
919
|
+
path.join(appDirectory, "[lang]", "layout.tsx"),
|
|
920
|
+
path.join(appDirectory, "[lang]", "auth", "layout.tsx"),
|
|
921
|
+
path.join(appDirectory, "[lang]", "admin", "layout.tsx"),
|
|
922
|
+
];
|
|
860
923
|
|
|
924
|
+
langLayoutsToRemove.forEach((layoutPath) => {
|
|
925
|
+
if (fs.existsSync(layoutPath)) {
|
|
926
|
+
try {
|
|
927
|
+
const content = fs.readFileSync(layoutPath, "utf-8");
|
|
928
|
+
// Supprimer si c'est un fichier généré
|
|
929
|
+
if (
|
|
930
|
+
content.includes(generatedComment) ||
|
|
931
|
+
content.includes("AppProviders")
|
|
932
|
+
) {
|
|
933
|
+
fs.unlinkSync(layoutPath);
|
|
934
|
+
if (isDebugMode) {
|
|
935
|
+
console.log(`🗑️ Removed generated layout: ${layoutPath}`);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
} catch (error) {
|
|
939
|
+
console.warn(`⚠️ Could not remove layout ${layoutPath}:`, error);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
function generateAppAside() {
|
|
945
|
+
const targetPath = path.join(appDirectory, "components", "AppAside.tsx");
|
|
861
946
|
const templateContent = `"use client";
|
|
862
947
|
|
|
863
948
|
import { AppAside as UIAppAside } from "@lastbrain/app";
|
|
@@ -883,59 +968,253 @@ export function AppAside({ className = "", isVisible = true }: AppAsideProps) {
|
|
|
883
968
|
}`;
|
|
884
969
|
|
|
885
970
|
try {
|
|
886
|
-
|
|
887
|
-
fs.writeFileSync(targetPath, templateContent, "utf-8");
|
|
888
|
-
if (isDebugMode) {
|
|
889
|
-
console.log(`✅ Generated AppAside component: ${targetPath}`);
|
|
890
|
-
}
|
|
971
|
+
writeScaffoldFile(targetPath, templateContent, "AppAside component");
|
|
891
972
|
} catch (error) {
|
|
892
973
|
console.error(`❌ Error generating AppAside component: ${error}`);
|
|
893
974
|
}
|
|
894
975
|
}
|
|
895
976
|
|
|
896
977
|
function generateLayouts() {
|
|
897
|
-
// Générer
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
978
|
+
// Générer ClientLayout wrapper client
|
|
979
|
+
const clientLayoutPath = path.join(
|
|
980
|
+
appDirectory,
|
|
981
|
+
"components",
|
|
982
|
+
"ClientLayout.tsx"
|
|
983
|
+
);
|
|
984
|
+
const clientLayoutContent = `"use client";
|
|
985
|
+
|
|
986
|
+
import { HeroUIProvider } from "@heroui/system";
|
|
987
|
+
import { ThemeProvider } from "next-themes";
|
|
988
|
+
import { AppProviders } from "./AppProviders";
|
|
989
|
+
import { AppHeader } from "./AppHeader";
|
|
990
|
+
import type { ReactNode } from "react";
|
|
991
|
+
import { useLocalizedRouter, type Language } from "@lastbrain/core";
|
|
992
|
+
|
|
993
|
+
|
|
994
|
+
export function ClientLayout({
|
|
995
|
+
children,
|
|
996
|
+
lang = "fr",
|
|
997
|
+
translations = {},
|
|
998
|
+
}: {
|
|
999
|
+
children: ReactNode;
|
|
1000
|
+
lang?: Language;
|
|
1001
|
+
translations?: Record<string, string>;
|
|
1002
|
+
}) {
|
|
1003
|
+
const router = useLocalizedRouter();
|
|
1004
|
+
|
|
1005
|
+
return (
|
|
1006
|
+
<HeroUIProvider navigate={router.push}>
|
|
1007
|
+
<ThemeProvider
|
|
1008
|
+
attribute="class"
|
|
1009
|
+
defaultTheme="dark"
|
|
1010
|
+
enableSystem={false}
|
|
1011
|
+
storageKey="lastbrain-theme"
|
|
1012
|
+
>
|
|
1013
|
+
<AppProviders lang={lang} translations={translations}>
|
|
1014
|
+
<AppHeader />
|
|
1015
|
+
<div className="min-h-screen text-foreground bg-background">
|
|
1016
|
+
{children}
|
|
1017
|
+
</div>
|
|
1018
|
+
</AppProviders>
|
|
1019
|
+
</ThemeProvider>
|
|
1020
|
+
</HeroUIProvider>
|
|
1021
|
+
);
|
|
1022
|
+
}`;
|
|
1023
|
+
|
|
1024
|
+
try {
|
|
1025
|
+
writeScaffoldFile(
|
|
1026
|
+
clientLayoutPath,
|
|
1027
|
+
clientLayoutContent,
|
|
1028
|
+
"ClientLayout component"
|
|
1029
|
+
);
|
|
1030
|
+
} catch (error) {
|
|
1031
|
+
console.error(`❌ Error generating ClientLayout component: ${error}`);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// Générer AppProviders wrapper client
|
|
1035
|
+
const appProvidersPath = path.join(
|
|
1036
|
+
appDirectory,
|
|
1037
|
+
"components",
|
|
1038
|
+
"AppProviders.tsx"
|
|
1039
|
+
);
|
|
1040
|
+
const appProvidersContent = `"use client";
|
|
1041
|
+
|
|
1042
|
+
import { AppProviders as BaseAppProviders } from "@lastbrain/app";
|
|
1043
|
+
import { realtimeConfig } from "../../config/realtime";
|
|
1044
|
+
import type { ReactNode } from "react";
|
|
1045
|
+
import type { Language } from "@lastbrain/core";
|
|
1046
|
+
|
|
1047
|
+
export function AppProviders({
|
|
1048
|
+
children,
|
|
1049
|
+
lang = "fr",
|
|
1050
|
+
translations = {},
|
|
1051
|
+
}: {
|
|
1052
|
+
children: ReactNode;
|
|
1053
|
+
lang?: Language;
|
|
1054
|
+
translations?: Record<string, string>;
|
|
1055
|
+
}) {
|
|
1056
|
+
return (
|
|
1057
|
+
<BaseAppProviders
|
|
1058
|
+
realtimeConfig={realtimeConfig}
|
|
1059
|
+
lang={lang}
|
|
1060
|
+
translations={translations}
|
|
1061
|
+
>
|
|
1062
|
+
{children}
|
|
1063
|
+
</BaseAppProviders>
|
|
1064
|
+
);
|
|
1065
|
+
}`;
|
|
1066
|
+
|
|
1067
|
+
try {
|
|
1068
|
+
writeScaffoldFile(
|
|
1069
|
+
appProvidersPath,
|
|
1070
|
+
appProvidersContent,
|
|
1071
|
+
"AppProviders component"
|
|
1072
|
+
);
|
|
1073
|
+
} catch (error) {
|
|
1074
|
+
console.error(`❌ Error generating AppProviders component: ${error}`);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// Générer AppHeader component
|
|
1078
|
+
const appHeaderPath = path.join(appDirectory, "components", "AppHeader.tsx");
|
|
1079
|
+
const appHeaderContent = `"use client";
|
|
1080
|
+
|
|
1081
|
+
import { Header } from "@lastbrain/ui";
|
|
1082
|
+
import { LanguageSwitcher } from "@lastbrain/app";
|
|
901
1083
|
import { menuConfig } from "../../config/menu";
|
|
902
1084
|
|
|
903
|
-
export
|
|
1085
|
+
export function AppHeader() {
|
|
1086
|
+
return (
|
|
1087
|
+
<Header
|
|
1088
|
+
menuConfig={menuConfig}
|
|
1089
|
+
languageSwitcher={<LanguageSwitcher variant="minimal" />}
|
|
1090
|
+
/>
|
|
1091
|
+
);
|
|
1092
|
+
}`;
|
|
1093
|
+
|
|
1094
|
+
try {
|
|
1095
|
+
writeScaffoldFile(appHeaderPath, appHeaderContent, "AppHeader component");
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
console.error(`❌ Error generating AppHeader component: ${error}`);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// Générer layout [lang] pour la gestion i18n
|
|
1101
|
+
const langLayoutPath = path.join(appDirectory, "[lang]", "layout.tsx");
|
|
1102
|
+
if (!fs.existsSync(langLayoutPath)) {
|
|
1103
|
+
const langLayoutContent = `import { loadTranslations } from "@lastbrain/app/i18n/server-lang";
|
|
1104
|
+
import { ClientLayout } from "../../components/ClientLayout";
|
|
1105
|
+
|
|
1106
|
+
export default async function LangLayout({
|
|
904
1107
|
children,
|
|
1108
|
+
params,
|
|
905
1109
|
}: {
|
|
906
1110
|
children: React.ReactNode;
|
|
1111
|
+
params: Promise<{ lang: string }>;
|
|
907
1112
|
}) {
|
|
1113
|
+
const { lang } = await params;
|
|
1114
|
+
const validLang = lang === "en" || lang === "fr" ? lang : "fr";
|
|
1115
|
+
|
|
1116
|
+
// Charger les traductions pour cette langue
|
|
1117
|
+
const translations = await loadTranslations(validLang);
|
|
1118
|
+
|
|
908
1119
|
return (
|
|
909
|
-
<
|
|
910
|
-
|
|
911
|
-
|
|
1120
|
+
<ClientLayout lang={validLang} translations={translations}>
|
|
1121
|
+
<div className="min-h-screen pt-16">
|
|
1122
|
+
{children}
|
|
1123
|
+
</div>
|
|
1124
|
+
</ClientLayout>
|
|
912
1125
|
);
|
|
913
1126
|
}`;
|
|
914
1127
|
|
|
915
1128
|
try {
|
|
916
|
-
ensureDirectory(path.dirname(
|
|
917
|
-
fs.writeFileSync(
|
|
1129
|
+
ensureDirectory(path.dirname(langLayoutPath));
|
|
1130
|
+
fs.writeFileSync(langLayoutPath, langLayoutContent, "utf-8");
|
|
918
1131
|
if (isDebugMode) {
|
|
919
|
-
console.log(`✅ Generated
|
|
1132
|
+
console.log(`✅ Generated [lang] layout: ${langLayoutPath}`);
|
|
920
1133
|
}
|
|
921
1134
|
} catch (error) {
|
|
922
|
-
console.error(`❌ Error generating
|
|
1135
|
+
console.error(`❌ Error generating [lang] layout: ${error}`);
|
|
923
1136
|
}
|
|
924
1137
|
} else if (isDebugMode) {
|
|
925
|
-
console.log(`⏭️
|
|
1138
|
+
console.log(`⏭️ [lang] layout already exists, skipping: ${langLayoutPath}`);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// Générer layout auth avec sidebar
|
|
1142
|
+
const authLayoutPath = path.join(
|
|
1143
|
+
appDirectory,
|
|
1144
|
+
"[lang]",
|
|
1145
|
+
"auth",
|
|
1146
|
+
"layout.tsx"
|
|
1147
|
+
);
|
|
1148
|
+
const authLayoutContent = `"use client";
|
|
1149
|
+
|
|
1150
|
+
import { AuthLayoutWithSidebar, langHref, useLanguage } from "@lastbrain/app";
|
|
1151
|
+
import { menuConfig as fullMenuConfig } from "../../../config/menu";
|
|
1152
|
+
|
|
1153
|
+
export default function SectionLayout({
|
|
1154
|
+
children,
|
|
1155
|
+
}: {
|
|
1156
|
+
children: React.ReactNode;
|
|
1157
|
+
}) {
|
|
1158
|
+
const { lang } = useLanguage();
|
|
1159
|
+
|
|
1160
|
+
// Transformer les paths du menu avec la langue
|
|
1161
|
+
const menuConfig = {
|
|
1162
|
+
...fullMenuConfig,
|
|
1163
|
+
auth: (fullMenuConfig.auth || []).map(item => ({
|
|
1164
|
+
...item,
|
|
1165
|
+
path: item.path?.startsWith("/") && !item.path?.startsWith("/api/")
|
|
1166
|
+
? langHref(item.path, lang)
|
|
1167
|
+
: item.path
|
|
1168
|
+
})),
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
return (
|
|
1172
|
+
<AuthLayoutWithSidebar menuConfig={menuConfig}>
|
|
1173
|
+
{children}
|
|
1174
|
+
</AuthLayoutWithSidebar>
|
|
1175
|
+
);
|
|
1176
|
+
}`;
|
|
1177
|
+
|
|
1178
|
+
try {
|
|
1179
|
+
writeScaffoldFile(
|
|
1180
|
+
authLayoutPath,
|
|
1181
|
+
authLayoutContent,
|
|
1182
|
+
"auth layout with sidebar"
|
|
1183
|
+
);
|
|
1184
|
+
} catch (error) {
|
|
1185
|
+
console.error(`❌ Error generating auth layout: ${error}`);
|
|
926
1186
|
}
|
|
927
1187
|
|
|
928
1188
|
// Générer layout admin avec sidebar
|
|
929
|
-
const adminLayoutPath = path.join(
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
1189
|
+
const adminLayoutPath = path.join(
|
|
1190
|
+
appDirectory,
|
|
1191
|
+
"[lang]",
|
|
1192
|
+
"admin",
|
|
1193
|
+
"layout.tsx"
|
|
1194
|
+
);
|
|
1195
|
+
const adminLayoutContent = `"use client";
|
|
1196
|
+
|
|
1197
|
+
import { AdminLayoutWithSidebar, langHref, useLanguage } from "@lastbrain/app";
|
|
1198
|
+
import { menuConfig as fullMenuConfig } from "../../../config/menu";
|
|
933
1199
|
|
|
934
1200
|
export default function AdminLayout({
|
|
935
1201
|
children,
|
|
936
1202
|
}: {
|
|
937
1203
|
children: React.ReactNode;
|
|
938
1204
|
}) {
|
|
1205
|
+
const { lang } = useLanguage();
|
|
1206
|
+
|
|
1207
|
+
// Transformer les paths du menu avec la langue
|
|
1208
|
+
const menuConfig = {
|
|
1209
|
+
...fullMenuConfig,
|
|
1210
|
+
admin: (fullMenuConfig.admin || []).map(item => ({
|
|
1211
|
+
...item,
|
|
1212
|
+
path: item.path?.startsWith("/") && !item.path?.startsWith("/api/")
|
|
1213
|
+
? langHref(item.path, lang)
|
|
1214
|
+
: item.path
|
|
1215
|
+
})),
|
|
1216
|
+
};
|
|
1217
|
+
|
|
939
1218
|
return (
|
|
940
1219
|
<AdminLayoutWithSidebar menuConfig={menuConfig}>
|
|
941
1220
|
{children}
|
|
@@ -943,28 +1222,23 @@ export default function AdminLayout({
|
|
|
943
1222
|
);
|
|
944
1223
|
}`;
|
|
945
1224
|
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
} catch (error) {
|
|
955
|
-
console.error(`❌ Error generating admin layout: ${error}`);
|
|
956
|
-
}
|
|
957
|
-
} else if (isDebugMode) {
|
|
958
|
-
console.log(`⏭️ Admin layout already exists, skipping: ${adminLayoutPath}`);
|
|
1225
|
+
try {
|
|
1226
|
+
writeScaffoldFile(
|
|
1227
|
+
adminLayoutPath,
|
|
1228
|
+
adminLayoutContent,
|
|
1229
|
+
"admin layout with sidebar"
|
|
1230
|
+
);
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
console.error(`❌ Error generating admin layout: ${error}`);
|
|
959
1233
|
}
|
|
960
1234
|
}
|
|
961
1235
|
|
|
962
1236
|
async function generateRealtimeConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
963
1237
|
try {
|
|
964
1238
|
// Extraire les configurations realtime des modules
|
|
965
|
-
const realtimeConfigs: ModuleRealtimeConfig[] = moduleConfigs
|
|
966
|
-
|
|
967
|
-
|
|
1239
|
+
const realtimeConfigs: ModuleRealtimeConfig[] = moduleConfigs.flatMap(
|
|
1240
|
+
(config) => (config.realtime ? [config.realtime] : [])
|
|
1241
|
+
);
|
|
968
1242
|
|
|
969
1243
|
if (realtimeConfigs.length === 0) {
|
|
970
1244
|
console.log("⏭️ No realtime configuration found in modules");
|
|
@@ -1025,9 +1299,8 @@ async function generateUserTabsConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
1025
1299
|
try {
|
|
1026
1300
|
// Extraire les configurations user tabs des modules
|
|
1027
1301
|
const userTabsConfigs = moduleConfigs
|
|
1028
|
-
.filter((config) => config.userTabs && config.userTabs.length > 0)
|
|
1029
1302
|
.flatMap((config) =>
|
|
1030
|
-
config.userTabs
|
|
1303
|
+
(config.userTabs ?? []).map((tab) => ({
|
|
1031
1304
|
...tab,
|
|
1032
1305
|
moduleName: config.moduleName,
|
|
1033
1306
|
}))
|
|
@@ -1100,11 +1373,9 @@ async function generateUserTabsConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
1100
1373
|
async function generateBucketsConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
1101
1374
|
try {
|
|
1102
1375
|
// Extraire les configurations storage des modules
|
|
1103
|
-
const allBuckets = moduleConfigs
|
|
1104
|
-
.
|
|
1105
|
-
|
|
1106
|
-
)
|
|
1107
|
-
.flatMap((config) => config.storage!.buckets);
|
|
1376
|
+
const allBuckets = moduleConfigs.flatMap(
|
|
1377
|
+
(config) => config.storage?.buckets ?? []
|
|
1378
|
+
);
|
|
1108
1379
|
|
|
1109
1380
|
if (allBuckets.length === 0) {
|
|
1110
1381
|
console.log("⏭️ No storage buckets configuration found in modules");
|
|
@@ -1236,11 +1507,9 @@ export function isValidFileSize(bucketName: string, fileSize: number): boolean {
|
|
|
1236
1507
|
async function generateStorageProxyApi(moduleConfigs: ModuleBuildConfig[]) {
|
|
1237
1508
|
try {
|
|
1238
1509
|
// Extraire les configurations storage des modules
|
|
1239
|
-
const allBuckets = moduleConfigs
|
|
1240
|
-
.
|
|
1241
|
-
|
|
1242
|
-
)
|
|
1243
|
-
.flatMap((config) => config.storage!.buckets);
|
|
1510
|
+
const allBuckets = moduleConfigs.flatMap(
|
|
1511
|
+
(config) => config.storage?.buckets ?? []
|
|
1512
|
+
);
|
|
1244
1513
|
|
|
1245
1514
|
if (allBuckets.length === 0) {
|
|
1246
1515
|
console.log("⏭️ No storage buckets found, skipping proxy API generation");
|
|
@@ -1393,9 +1662,9 @@ export async function GET(
|
|
|
1393
1662
|
async function generateFooterConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
1394
1663
|
try {
|
|
1395
1664
|
// Extraire tous les liens footer des modules
|
|
1396
|
-
const allFooterLinks = moduleConfigs
|
|
1397
|
-
|
|
1398
|
-
|
|
1665
|
+
const allFooterLinks = moduleConfigs.flatMap(
|
|
1666
|
+
(config) => config.footer ?? []
|
|
1667
|
+
);
|
|
1399
1668
|
|
|
1400
1669
|
if (allFooterLinks.length === 0) {
|
|
1401
1670
|
console.log(
|
|
@@ -1444,6 +1713,104 @@ export const footerConfig: FooterConfig = {
|
|
|
1444
1713
|
}
|
|
1445
1714
|
}
|
|
1446
1715
|
|
|
1716
|
+
/**
|
|
1717
|
+
* Génère les fichiers i18n en concaténant les traductions des modules
|
|
1718
|
+
*/
|
|
1719
|
+
async function generateI18nFiles() {
|
|
1720
|
+
const appI18nDir = path.join(projectRoot, "i18n");
|
|
1721
|
+
const packagesDir = path.join(_monorepoRoot, "packages");
|
|
1722
|
+
|
|
1723
|
+
interface TranslationMap {
|
|
1724
|
+
[key: string]: Record<string, string>;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
const translations: TranslationMap = {
|
|
1728
|
+
fr: {},
|
|
1729
|
+
en: {},
|
|
1730
|
+
};
|
|
1731
|
+
|
|
1732
|
+
try {
|
|
1733
|
+
// Importer glob dynamiquement
|
|
1734
|
+
const { glob } = await import("glob");
|
|
1735
|
+
|
|
1736
|
+
// Rechercher tous les fichiers i18n dans packages/**/src/i18n/*.json
|
|
1737
|
+
const pattern = path.join(packagesDir, "**/src/i18n/*.json");
|
|
1738
|
+
const files = await glob(pattern, {
|
|
1739
|
+
ignore: ["**/node_modules/**", "**/dist/**"],
|
|
1740
|
+
});
|
|
1741
|
+
|
|
1742
|
+
if (isDebugMode) {
|
|
1743
|
+
console.log(`\n🔍 Found ${files.length} i18n files\n`);
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
for (const file of files) {
|
|
1747
|
+
const fileName = path.basename(file, ".json");
|
|
1748
|
+
const lang = fileName; // fr.json -> fr, en.json -> en
|
|
1749
|
+
|
|
1750
|
+
if (!translations[lang]) {
|
|
1751
|
+
translations[lang] = {};
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
try {
|
|
1755
|
+
const content = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
1756
|
+
const moduleName = extractModuleNameFromPath(file);
|
|
1757
|
+
|
|
1758
|
+
if (isDebugMode) {
|
|
1759
|
+
console.log(` ✓ ${moduleName} (${lang})`);
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
// Préfixer uniquement si la clé n'est pas déjà namespacée (avec ou sans suffixe -pro)
|
|
1763
|
+
const moduleNamespace = moduleName.replace(/-pro$/, "");
|
|
1764
|
+
for (const [key, value] of Object.entries(content)) {
|
|
1765
|
+
const keyRoot = key.split(".")[0];
|
|
1766
|
+
const isGenericNamespaced = keyRoot.startsWith("module-");
|
|
1767
|
+
const isAlreadyNamespaced =
|
|
1768
|
+
isGenericNamespaced ||
|
|
1769
|
+
key.startsWith(`${moduleName}.`) ||
|
|
1770
|
+
key.startsWith(`${moduleNamespace}.`);
|
|
1771
|
+
|
|
1772
|
+
const normalizedKey = isAlreadyNamespaced
|
|
1773
|
+
? key
|
|
1774
|
+
: `${moduleNamespace}.${key}`;
|
|
1775
|
+
|
|
1776
|
+
translations[lang][normalizedKey] = value as string;
|
|
1777
|
+
}
|
|
1778
|
+
} catch (error) {
|
|
1779
|
+
console.warn(` ⚠️ Error reading ${file}:`, error);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
// Créer le dossier i18n s'il n'existe pas
|
|
1784
|
+
ensureDirectory(appI18nDir);
|
|
1785
|
+
|
|
1786
|
+
// Écrire les fichiers de traduction
|
|
1787
|
+
for (const [lang, content] of Object.entries(translations)) {
|
|
1788
|
+
const filePath = path.join(appI18nDir, `${lang}.json`);
|
|
1789
|
+
fs.writeFileSync(filePath, JSON.stringify(content, null, 2), "utf-8");
|
|
1790
|
+
const keyCount = Object.keys(content).length;
|
|
1791
|
+
|
|
1792
|
+
if (isDebugMode) {
|
|
1793
|
+
console.log(`✅ Generated ${filePath} (${keyCount} keys)`);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
} catch (error) {
|
|
1797
|
+
console.error("❌ Error generating i18n files:", error);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
/**
|
|
1802
|
+
* Extrait le nom du module depuis le chemin du fichier
|
|
1803
|
+
*/
|
|
1804
|
+
function extractModuleNameFromPath(filePath: string): string {
|
|
1805
|
+
// packages/module-auth/src/i18n/fr.json -> module-auth
|
|
1806
|
+
const parts = filePath.split(path.sep);
|
|
1807
|
+
const packagesIndex = parts.indexOf("packages");
|
|
1808
|
+
if (packagesIndex !== -1 && packagesIndex < parts.length - 1) {
|
|
1809
|
+
return parts[packagesIndex + 1];
|
|
1810
|
+
}
|
|
1811
|
+
return "unknown";
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1447
1814
|
export async function runModuleBuild() {
|
|
1448
1815
|
ensureDirectory(appDirectory);
|
|
1449
1816
|
|
|
@@ -1453,6 +1820,12 @@ export async function runModuleBuild() {
|
|
|
1453
1820
|
}
|
|
1454
1821
|
cleanGeneratedFiles();
|
|
1455
1822
|
|
|
1823
|
+
// Supprimer les anciennes routes sans [lang]
|
|
1824
|
+
if (isDebugMode) {
|
|
1825
|
+
console.log("🗑️ Removing old routes without [lang]...");
|
|
1826
|
+
}
|
|
1827
|
+
cleanOldRoutesWithoutLang();
|
|
1828
|
+
|
|
1456
1829
|
const moduleConfigs = await loadModuleConfigs();
|
|
1457
1830
|
if (isDebugMode) {
|
|
1458
1831
|
console.log(`🔍 Loaded ${moduleConfigs.length} module configurations`);
|
|
@@ -1482,10 +1855,13 @@ export async function runModuleBuild() {
|
|
|
1482
1855
|
|
|
1483
1856
|
moduleConfigs.forEach((moduleConfig) => {
|
|
1484
1857
|
moduleConfig.apis.forEach((api) => {
|
|
1485
|
-
|
|
1486
|
-
|
|
1858
|
+
const apisForPath = apisByPath.get(api.path);
|
|
1859
|
+
if (apisForPath) {
|
|
1860
|
+
apisForPath.push({ moduleConfig, api });
|
|
1861
|
+
return;
|
|
1487
1862
|
}
|
|
1488
|
-
|
|
1863
|
+
|
|
1864
|
+
apisByPath.set(api.path, [{ moduleConfig, api }]);
|
|
1489
1865
|
});
|
|
1490
1866
|
});
|
|
1491
1867
|
|
|
@@ -1531,6 +1907,12 @@ export async function runModuleBuild() {
|
|
|
1531
1907
|
}
|
|
1532
1908
|
await generateFooterConfig(moduleConfigs);
|
|
1533
1909
|
|
|
1910
|
+
// Générer les fichiers i18n
|
|
1911
|
+
if (isDebugMode) {
|
|
1912
|
+
console.log("🌍 Building i18n files...");
|
|
1913
|
+
}
|
|
1914
|
+
await generateI18nFiles();
|
|
1915
|
+
|
|
1534
1916
|
// Message de succès final
|
|
1535
1917
|
if (!isDebugMode) {
|
|
1536
1918
|
console.log("\n✅ Module build completed successfully!");
|