@lastbrain/app 2.0.15 → 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/PublicLayout.d.ts.map +1 -1
- package/dist/layouts/PublicLayout.js +1 -1
- 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 +390 -83
- 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/PublicLayout.tsx +3 -1
- package/src/layouts/RootLayout.tsx +13 -6
- package/src/scripts/i18n-build.ts +122 -0
- package/src/scripts/init-app.ts +12 -12
- package/src/scripts/module-build.ts +483 -96
- 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);
|
|
@@ -295,11 +307,40 @@ export default function ${wrapperName}${hasDynamicParams ? "(props: Record<strin
|
|
|
295
307
|
}
|
|
296
308
|
`;
|
|
297
309
|
} else {
|
|
298
|
-
|
|
299
|
-
|
|
310
|
+
// Déterminer le chemin d'import
|
|
311
|
+
const importPath = page.entryPoint
|
|
312
|
+
? `${moduleConfig.moduleName}/${page.entryPoint}`
|
|
313
|
+
: moduleConfig.moduleName;
|
|
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
|
+
: "";
|
|
300
338
|
|
|
301
|
-
|
|
302
|
-
|
|
339
|
+
content = `// GENERATED BY LASTBRAIN MODULE BUILD
|
|
340
|
+
import { ${page.componentExport} } from "${importPath}";
|
|
341
|
+
${page.metadataExport ? `\nexport { ${page.metadataExport} as generateMetadata } from "${importPath}";\n` : ""}
|
|
342
|
+
export default ${hasDynamicParams ? "async " : ""}function ${wrapperName}${propsSignature} {
|
|
343
|
+
${awaitParams}return <${page.componentExport} />;
|
|
303
344
|
}
|
|
304
345
|
`;
|
|
305
346
|
}
|
|
@@ -344,12 +385,16 @@ function generateMenuConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
344
385
|
ensureDirectory(configDir);
|
|
345
386
|
|
|
346
387
|
const menuPath = path.join(configDir, "menu.ts");
|
|
388
|
+
type AugmentedMenuItem = ModuleMenuItemConfig & {
|
|
389
|
+
moduleName?: string;
|
|
390
|
+
__componentRef?: string;
|
|
391
|
+
};
|
|
347
392
|
|
|
348
393
|
// Collecter les menus de tous les modules avec leurs modules sources
|
|
349
|
-
const publicMenus:
|
|
350
|
-
const authMenus:
|
|
351
|
-
const adminMenus:
|
|
352
|
-
const accountMenus:
|
|
394
|
+
const publicMenus: AugmentedMenuItem[] = [];
|
|
395
|
+
const authMenus: AugmentedMenuItem[] = [];
|
|
396
|
+
const adminMenus: AugmentedMenuItem[] = [];
|
|
397
|
+
const accountMenus: AugmentedMenuItem[] = [];
|
|
353
398
|
|
|
354
399
|
moduleConfigs.forEach((moduleConfig: ModuleBuildConfig) => {
|
|
355
400
|
if (moduleConfig.menu) {
|
|
@@ -414,14 +459,14 @@ function generateMenuConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
414
459
|
`const ${componentName} = dynamic(() => import("${menu.moduleName}").then(mod => ({ default: mod.${menu.componentExport} })), { ssr: false });`
|
|
415
460
|
);
|
|
416
461
|
// Ajouter une référence au composant
|
|
417
|
-
|
|
462
|
+
menu.__componentRef = componentName;
|
|
418
463
|
}
|
|
419
464
|
});
|
|
420
465
|
|
|
421
466
|
// Fonction pour préparer les menus avec les composants
|
|
422
|
-
const prepareMenusForExport = (menus:
|
|
467
|
+
const prepareMenusForExport = (menus: AugmentedMenuItem[]) => {
|
|
423
468
|
return menus.map((menu) => {
|
|
424
|
-
const { moduleName, __componentRef, ...cleanMenu } = menu;
|
|
469
|
+
const { moduleName: _moduleName, __componentRef, ...cleanMenu } = menu;
|
|
425
470
|
if (__componentRef) {
|
|
426
471
|
// Retourner une référence au composant au lieu de la sérialiser
|
|
427
472
|
return `{ ...${JSON.stringify(cleanMenu)}, component: ${__componentRef} }`;
|
|
@@ -693,10 +738,13 @@ function buildGroupedApi(
|
|
|
693
738
|
|
|
694
739
|
apis.forEach(({ moduleConfig, api }) => {
|
|
695
740
|
const handler = `${moduleConfig.moduleName}/${api.entryPoint}`;
|
|
696
|
-
|
|
697
|
-
|
|
741
|
+
const currentExports = exportsBySource.get(handler);
|
|
742
|
+
if (currentExports) {
|
|
743
|
+
currentExports.push(api.handlerExport);
|
|
744
|
+
return;
|
|
698
745
|
}
|
|
699
|
-
|
|
746
|
+
|
|
747
|
+
exportsBySource.set(handler, [api.handlerExport]);
|
|
700
748
|
});
|
|
701
749
|
|
|
702
750
|
// Générer les exports - un export statement par source
|
|
@@ -817,7 +865,7 @@ function cleanGeneratedFiles() {
|
|
|
817
865
|
};
|
|
818
866
|
|
|
819
867
|
// Nettoyer les dossiers de sections
|
|
820
|
-
const sectionsToClean = ["(public)", "auth", "admin", "api"];
|
|
868
|
+
const sectionsToClean = ["(public)", "auth", "admin", "api", "[lang]"];
|
|
821
869
|
sectionsToClean.forEach((section) => {
|
|
822
870
|
const sectionPath = path.join(appDirectory, section);
|
|
823
871
|
if (fs.existsSync(sectionPath)) {
|
|
@@ -842,17 +890,59 @@ function cleanGeneratedFiles() {
|
|
|
842
890
|
}
|
|
843
891
|
}
|
|
844
892
|
|
|
845
|
-
|
|
846
|
-
|
|
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";
|
|
847
898
|
|
|
848
|
-
//
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
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
|
+
}
|
|
852
914
|
}
|
|
853
|
-
|
|
854
|
-
}
|
|
915
|
+
});
|
|
855
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
|
+
];
|
|
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");
|
|
856
946
|
const templateContent = `"use client";
|
|
857
947
|
|
|
858
948
|
import { AppAside as UIAppAside } from "@lastbrain/app";
|
|
@@ -878,59 +968,253 @@ export function AppAside({ className = "", isVisible = true }: AppAsideProps) {
|
|
|
878
968
|
}`;
|
|
879
969
|
|
|
880
970
|
try {
|
|
881
|
-
|
|
882
|
-
fs.writeFileSync(targetPath, templateContent, "utf-8");
|
|
883
|
-
if (isDebugMode) {
|
|
884
|
-
console.log(`✅ Generated AppAside component: ${targetPath}`);
|
|
885
|
-
}
|
|
971
|
+
writeScaffoldFile(targetPath, templateContent, "AppAside component");
|
|
886
972
|
} catch (error) {
|
|
887
973
|
console.error(`❌ Error generating AppAside component: ${error}`);
|
|
888
974
|
}
|
|
889
975
|
}
|
|
890
976
|
|
|
891
977
|
function generateLayouts() {
|
|
892
|
-
// Générer
|
|
893
|
-
const
|
|
894
|
-
|
|
895
|
-
|
|
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";
|
|
896
1083
|
import { menuConfig } from "../../config/menu";
|
|
897
1084
|
|
|
898
|
-
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({
|
|
899
1107
|
children,
|
|
1108
|
+
params,
|
|
900
1109
|
}: {
|
|
901
1110
|
children: React.ReactNode;
|
|
1111
|
+
params: Promise<{ lang: string }>;
|
|
902
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
|
+
|
|
903
1119
|
return (
|
|
904
|
-
<
|
|
905
|
-
|
|
906
|
-
|
|
1120
|
+
<ClientLayout lang={validLang} translations={translations}>
|
|
1121
|
+
<div className="min-h-screen pt-16">
|
|
1122
|
+
{children}
|
|
1123
|
+
</div>
|
|
1124
|
+
</ClientLayout>
|
|
907
1125
|
);
|
|
908
1126
|
}`;
|
|
909
1127
|
|
|
910
1128
|
try {
|
|
911
|
-
ensureDirectory(path.dirname(
|
|
912
|
-
fs.writeFileSync(
|
|
1129
|
+
ensureDirectory(path.dirname(langLayoutPath));
|
|
1130
|
+
fs.writeFileSync(langLayoutPath, langLayoutContent, "utf-8");
|
|
913
1131
|
if (isDebugMode) {
|
|
914
|
-
console.log(`✅ Generated
|
|
1132
|
+
console.log(`✅ Generated [lang] layout: ${langLayoutPath}`);
|
|
915
1133
|
}
|
|
916
1134
|
} catch (error) {
|
|
917
|
-
console.error(`❌ Error generating
|
|
1135
|
+
console.error(`❌ Error generating [lang] layout: ${error}`);
|
|
918
1136
|
}
|
|
919
1137
|
} else if (isDebugMode) {
|
|
920
|
-
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}`);
|
|
921
1186
|
}
|
|
922
1187
|
|
|
923
1188
|
// Générer layout admin avec sidebar
|
|
924
|
-
const adminLayoutPath = path.join(
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
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";
|
|
928
1199
|
|
|
929
1200
|
export default function AdminLayout({
|
|
930
1201
|
children,
|
|
931
1202
|
}: {
|
|
932
1203
|
children: React.ReactNode;
|
|
933
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
|
+
|
|
934
1218
|
return (
|
|
935
1219
|
<AdminLayoutWithSidebar menuConfig={menuConfig}>
|
|
936
1220
|
{children}
|
|
@@ -938,28 +1222,23 @@ export default function AdminLayout({
|
|
|
938
1222
|
);
|
|
939
1223
|
}`;
|
|
940
1224
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
} catch (error) {
|
|
950
|
-
console.error(`❌ Error generating admin layout: ${error}`);
|
|
951
|
-
}
|
|
952
|
-
} else if (isDebugMode) {
|
|
953
|
-
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}`);
|
|
954
1233
|
}
|
|
955
1234
|
}
|
|
956
1235
|
|
|
957
1236
|
async function generateRealtimeConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
958
1237
|
try {
|
|
959
1238
|
// Extraire les configurations realtime des modules
|
|
960
|
-
const realtimeConfigs: ModuleRealtimeConfig[] = moduleConfigs
|
|
961
|
-
|
|
962
|
-
|
|
1239
|
+
const realtimeConfigs: ModuleRealtimeConfig[] = moduleConfigs.flatMap(
|
|
1240
|
+
(config) => (config.realtime ? [config.realtime] : [])
|
|
1241
|
+
);
|
|
963
1242
|
|
|
964
1243
|
if (realtimeConfigs.length === 0) {
|
|
965
1244
|
console.log("⏭️ No realtime configuration found in modules");
|
|
@@ -1020,9 +1299,8 @@ async function generateUserTabsConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
1020
1299
|
try {
|
|
1021
1300
|
// Extraire les configurations user tabs des modules
|
|
1022
1301
|
const userTabsConfigs = moduleConfigs
|
|
1023
|
-
.filter((config) => config.userTabs && config.userTabs.length > 0)
|
|
1024
1302
|
.flatMap((config) =>
|
|
1025
|
-
config.userTabs
|
|
1303
|
+
(config.userTabs ?? []).map((tab) => ({
|
|
1026
1304
|
...tab,
|
|
1027
1305
|
moduleName: config.moduleName,
|
|
1028
1306
|
}))
|
|
@@ -1095,11 +1373,9 @@ async function generateUserTabsConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
1095
1373
|
async function generateBucketsConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
1096
1374
|
try {
|
|
1097
1375
|
// Extraire les configurations storage des modules
|
|
1098
|
-
const allBuckets = moduleConfigs
|
|
1099
|
-
.
|
|
1100
|
-
|
|
1101
|
-
)
|
|
1102
|
-
.flatMap((config) => config.storage!.buckets);
|
|
1376
|
+
const allBuckets = moduleConfigs.flatMap(
|
|
1377
|
+
(config) => config.storage?.buckets ?? []
|
|
1378
|
+
);
|
|
1103
1379
|
|
|
1104
1380
|
if (allBuckets.length === 0) {
|
|
1105
1381
|
console.log("⏭️ No storage buckets configuration found in modules");
|
|
@@ -1231,11 +1507,9 @@ export function isValidFileSize(bucketName: string, fileSize: number): boolean {
|
|
|
1231
1507
|
async function generateStorageProxyApi(moduleConfigs: ModuleBuildConfig[]) {
|
|
1232
1508
|
try {
|
|
1233
1509
|
// Extraire les configurations storage des modules
|
|
1234
|
-
const allBuckets = moduleConfigs
|
|
1235
|
-
.
|
|
1236
|
-
|
|
1237
|
-
)
|
|
1238
|
-
.flatMap((config) => config.storage!.buckets);
|
|
1510
|
+
const allBuckets = moduleConfigs.flatMap(
|
|
1511
|
+
(config) => config.storage?.buckets ?? []
|
|
1512
|
+
);
|
|
1239
1513
|
|
|
1240
1514
|
if (allBuckets.length === 0) {
|
|
1241
1515
|
console.log("⏭️ No storage buckets found, skipping proxy API generation");
|
|
@@ -1388,9 +1662,9 @@ export async function GET(
|
|
|
1388
1662
|
async function generateFooterConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
1389
1663
|
try {
|
|
1390
1664
|
// Extraire tous les liens footer des modules
|
|
1391
|
-
const allFooterLinks = moduleConfigs
|
|
1392
|
-
|
|
1393
|
-
|
|
1665
|
+
const allFooterLinks = moduleConfigs.flatMap(
|
|
1666
|
+
(config) => config.footer ?? []
|
|
1667
|
+
);
|
|
1394
1668
|
|
|
1395
1669
|
if (allFooterLinks.length === 0) {
|
|
1396
1670
|
console.log(
|
|
@@ -1439,6 +1713,104 @@ export const footerConfig: FooterConfig = {
|
|
|
1439
1713
|
}
|
|
1440
1714
|
}
|
|
1441
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
|
+
|
|
1442
1814
|
export async function runModuleBuild() {
|
|
1443
1815
|
ensureDirectory(appDirectory);
|
|
1444
1816
|
|
|
@@ -1448,6 +1820,12 @@ export async function runModuleBuild() {
|
|
|
1448
1820
|
}
|
|
1449
1821
|
cleanGeneratedFiles();
|
|
1450
1822
|
|
|
1823
|
+
// Supprimer les anciennes routes sans [lang]
|
|
1824
|
+
if (isDebugMode) {
|
|
1825
|
+
console.log("🗑️ Removing old routes without [lang]...");
|
|
1826
|
+
}
|
|
1827
|
+
cleanOldRoutesWithoutLang();
|
|
1828
|
+
|
|
1451
1829
|
const moduleConfigs = await loadModuleConfigs();
|
|
1452
1830
|
if (isDebugMode) {
|
|
1453
1831
|
console.log(`🔍 Loaded ${moduleConfigs.length} module configurations`);
|
|
@@ -1477,10 +1855,13 @@ export async function runModuleBuild() {
|
|
|
1477
1855
|
|
|
1478
1856
|
moduleConfigs.forEach((moduleConfig) => {
|
|
1479
1857
|
moduleConfig.apis.forEach((api) => {
|
|
1480
|
-
|
|
1481
|
-
|
|
1858
|
+
const apisForPath = apisByPath.get(api.path);
|
|
1859
|
+
if (apisForPath) {
|
|
1860
|
+
apisForPath.push({ moduleConfig, api });
|
|
1861
|
+
return;
|
|
1482
1862
|
}
|
|
1483
|
-
|
|
1863
|
+
|
|
1864
|
+
apisByPath.set(api.path, [{ moduleConfig, api }]);
|
|
1484
1865
|
});
|
|
1485
1866
|
});
|
|
1486
1867
|
|
|
@@ -1526,6 +1907,12 @@ export async function runModuleBuild() {
|
|
|
1526
1907
|
}
|
|
1527
1908
|
await generateFooterConfig(moduleConfigs);
|
|
1528
1909
|
|
|
1910
|
+
// Générer les fichiers i18n
|
|
1911
|
+
if (isDebugMode) {
|
|
1912
|
+
console.log("🌍 Building i18n files...");
|
|
1913
|
+
}
|
|
1914
|
+
await generateI18nFiles();
|
|
1915
|
+
|
|
1529
1916
|
// Message de succès final
|
|
1530
1917
|
if (!isDebugMode) {
|
|
1531
1918
|
console.log("\n✅ Module build completed successfully!");
|