@lastbrain/app 2.0.18 → 2.0.23
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 +390 -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 +36 -23
- 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 +485 -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
|
@@ -10,6 +10,19 @@ const _monorepoRoot = projectRoot.includes("/apps/")
|
|
|
10
10
|
const appDirectory = path.join(projectRoot, "app");
|
|
11
11
|
// Mode debug activé via --debug
|
|
12
12
|
const isDebugMode = process.argv.includes("--debug");
|
|
13
|
+
// Helper pour écrire (générer ou mettre à jour) un fichier de scaffold
|
|
14
|
+
function ensureDirectory(dir) {
|
|
15
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
function writeScaffoldFile(targetPath, content, label) {
|
|
18
|
+
const existed = fs.existsSync(targetPath);
|
|
19
|
+
ensureDirectory(path.dirname(targetPath));
|
|
20
|
+
fs.writeFileSync(targetPath, content, "utf-8");
|
|
21
|
+
if (isDebugMode) {
|
|
22
|
+
const status = existed ? "♻️ Updated" : "✅ Generated";
|
|
23
|
+
console.log(`${status} ${label}: ${targetPath}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
13
26
|
// Créer un require dans le contexte de l'application pour résoudre les modules installés dans l'app
|
|
14
27
|
const projectRequire = createRequire(path.join(projectRoot, "package.json"));
|
|
15
28
|
// Charger les modules depuis modules.json
|
|
@@ -70,10 +83,10 @@ async function loadModuleConfigs() {
|
|
|
70
83
|
return moduleConfigs;
|
|
71
84
|
}
|
|
72
85
|
const sectionDirectoryMap = {
|
|
73
|
-
public: ["(public)"],
|
|
74
|
-
auth: ["auth"],
|
|
75
|
-
admin: ["admin"],
|
|
76
|
-
user: ["auth", "user"],
|
|
86
|
+
public: ["[lang]", "(public)"],
|
|
87
|
+
auth: ["[lang]", "auth"],
|
|
88
|
+
admin: ["[lang]", "admin"],
|
|
89
|
+
user: ["[lang]", "auth", "user"],
|
|
77
90
|
};
|
|
78
91
|
const sectionLayoutMap = {
|
|
79
92
|
auth: "AuthLayout",
|
|
@@ -87,9 +100,6 @@ const navigation = {
|
|
|
87
100
|
admin: [],
|
|
88
101
|
};
|
|
89
102
|
const userMenu = [];
|
|
90
|
-
function ensureDirectory(dir) {
|
|
91
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
92
|
-
}
|
|
93
103
|
function ensureSectionLayout(sectionPath) {
|
|
94
104
|
const sectionDir = path.join(appDirectory, ...sectionPath);
|
|
95
105
|
const sectionKey = sectionPath[0];
|
|
@@ -101,16 +111,13 @@ function ensureSectionLayout(sectionPath) {
|
|
|
101
111
|
const layoutName = sectionLayoutMap[sectionKey];
|
|
102
112
|
if (layoutName) {
|
|
103
113
|
const layoutPath = path.join(sectionDir, "layout.tsx");
|
|
104
|
-
|
|
105
|
-
|
|
114
|
+
const layoutContent = `import { ${layoutName} } from "@lastbrain/app";
|
|
115
|
+
import type React from "react";
|
|
106
116
|
|
|
107
117
|
export default function SectionLayout({ children }: { children: React.ReactNode }) {
|
|
108
118
|
return <${layoutName}>{children}</${layoutName}>;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
fs.writeFileSync(layoutPath, layoutContent);
|
|
112
|
-
console.log(`📐 Generated section layout: ${layoutPath}`);
|
|
113
|
-
}
|
|
119
|
+
}`;
|
|
120
|
+
writeScaffoldFile(layoutPath, layoutContent, `${sectionKey} layout`);
|
|
114
121
|
}
|
|
115
122
|
generatedSections.add(sectionKey);
|
|
116
123
|
}
|
|
@@ -165,7 +172,9 @@ function buildPage(moduleConfig, page) {
|
|
|
165
172
|
: "Root";
|
|
166
173
|
const wrapperName = `${page.componentExport}${wrapperSuffix}Route`;
|
|
167
174
|
// Vérifier si la route a des paramètres dynamiques (segments avec [])
|
|
168
|
-
|
|
175
|
+
// Inclure les segments du sectionPath (ex: [lang]) ET les segments du module
|
|
176
|
+
const allSegments = [...sectionPath, ...segments];
|
|
177
|
+
const hasDynamicParams = allSegments.some((seg) => seg.startsWith("[") && seg.endsWith("]"));
|
|
169
178
|
// Pour les pages publiques (signin, signup, etc.), utiliser dynamic import sans SSR
|
|
170
179
|
// pour éviter les erreurs d'hydratation avec les IDs HeroUI/React Aria
|
|
171
180
|
const isPublicAuthPage = page.section === "public" &&
|
|
@@ -193,7 +202,7 @@ interface UserPageProps { params: Promise<{ id: string }> }
|
|
|
193
202
|
async function getModuleUserTabs() {
|
|
194
203
|
try {
|
|
195
204
|
// Depuis /app/admin/auth/users/[id]/ vers /apps/test-01/config/user-tabs
|
|
196
|
-
const { moduleUserTabs } = await import("
|
|
205
|
+
const { moduleUserTabs } = await import("../../../../../../config/user-tabs");
|
|
197
206
|
return moduleUserTabs || [];
|
|
198
207
|
} catch (e) {
|
|
199
208
|
console.warn("[user-detail-wrapper] erreur chargement user-tabs", e);
|
|
@@ -240,11 +249,35 @@ export default function ${wrapperName}${hasDynamicParams ? "(props: Record<strin
|
|
|
240
249
|
const importPath = page.entryPoint
|
|
241
250
|
? `${moduleConfig.moduleName}/${page.entryPoint}`
|
|
242
251
|
: moduleConfig.moduleName;
|
|
252
|
+
// Extraire les noms des paramètres dynamiques pour le typage
|
|
253
|
+
// Inclure les segments du sectionPath (ex: [lang]) ET les segments du module
|
|
254
|
+
const allSegments = [...sectionPath, ...segments];
|
|
255
|
+
const dynamicParamNames = allSegments
|
|
256
|
+
.filter((seg) => seg.startsWith("[") && seg.endsWith("]"))
|
|
257
|
+
.map((seg) => seg.slice(1, -1)); // Enlève les []
|
|
258
|
+
// Générer le type des params si nécessaire
|
|
259
|
+
const paramsType = dynamicParamNames.length > 0
|
|
260
|
+
? `{ ${dynamicParamNames.map((name) => `${name}: string`).join("; ")} }`
|
|
261
|
+
: "";
|
|
262
|
+
const propsSignature = hasDynamicParams && paramsType
|
|
263
|
+
? `({\n params,\n}: {\n params: Promise<${paramsType}>;\n})`
|
|
264
|
+
: "()";
|
|
265
|
+
// Détecter si c'est un Server Component (entryPoint: "server")
|
|
266
|
+
const isServerComponent = page.entryPoint === "server";
|
|
267
|
+
// Les Server Components reçoivent params en props
|
|
268
|
+
// Les Client Components utilisent useParams() en interne
|
|
269
|
+
const componentProps = isServerComponent && hasDynamicParams
|
|
270
|
+
? "params={params}"
|
|
271
|
+
: "";
|
|
272
|
+
// Si params existe mais n'est pas passé au composant, on doit le unwrap quand même
|
|
273
|
+
const awaitParams = hasDynamicParams && !isServerComponent
|
|
274
|
+
? "await params; // Unwrap params for Next.js 15\n "
|
|
275
|
+
: "";
|
|
243
276
|
content = `// GENERATED BY LASTBRAIN MODULE BUILD
|
|
244
277
|
import { ${page.componentExport} } from "${importPath}";
|
|
245
278
|
${page.metadataExport ? `\nexport { ${page.metadataExport} as generateMetadata } from "${importPath}";\n` : ""}
|
|
246
|
-
export default
|
|
247
|
-
return <${page.componentExport} ${
|
|
279
|
+
export default ${hasDynamicParams ? "async " : ""}function ${wrapperName}${propsSignature} {
|
|
280
|
+
${awaitParams}return <${page.componentExport} ${componentProps} />;
|
|
248
281
|
}
|
|
249
282
|
`;
|
|
250
283
|
}
|
|
@@ -345,7 +378,7 @@ function generateMenuConfig(moduleConfigs) {
|
|
|
345
378
|
// Fonction pour préparer les menus avec les composants
|
|
346
379
|
const prepareMenusForExport = (menus) => {
|
|
347
380
|
return menus.map((menu) => {
|
|
348
|
-
const { moduleName, __componentRef, ...cleanMenu } = menu;
|
|
381
|
+
const { moduleName: _moduleName, __componentRef, ...cleanMenu } = menu;
|
|
349
382
|
if (__componentRef) {
|
|
350
383
|
// Retourner une référence au composant au lieu de la sérialiser
|
|
351
384
|
return `{ ...${JSON.stringify(cleanMenu)}, component: ${__componentRef} }`;
|
|
@@ -554,10 +587,12 @@ function buildGroupedApi(apis, routePath) {
|
|
|
554
587
|
const exportsBySource = new Map();
|
|
555
588
|
apis.forEach(({ moduleConfig, api }) => {
|
|
556
589
|
const handler = `${moduleConfig.moduleName}/${api.entryPoint}`;
|
|
557
|
-
|
|
558
|
-
|
|
590
|
+
const currentExports = exportsBySource.get(handler);
|
|
591
|
+
if (currentExports) {
|
|
592
|
+
currentExports.push(api.handlerExport);
|
|
593
|
+
return;
|
|
559
594
|
}
|
|
560
|
-
exportsBySource.
|
|
595
|
+
exportsBySource.set(handler, [api.handlerExport]);
|
|
561
596
|
});
|
|
562
597
|
// Générer les exports - un export statement par source
|
|
563
598
|
const exportStatements = [];
|
|
@@ -662,7 +697,7 @@ function cleanGeneratedFiles() {
|
|
|
662
697
|
}
|
|
663
698
|
};
|
|
664
699
|
// Nettoyer les dossiers de sections
|
|
665
|
-
const sectionsToClean = ["(public)", "auth", "admin", "api"];
|
|
700
|
+
const sectionsToClean = ["(public)", "auth", "admin", "api", "[lang]"];
|
|
666
701
|
sectionsToClean.forEach((section) => {
|
|
667
702
|
const sectionPath = path.join(appDirectory, section);
|
|
668
703
|
if (fs.existsSync(sectionPath)) {
|
|
@@ -684,15 +719,55 @@ function cleanGeneratedFiles() {
|
|
|
684
719
|
console.log("🧹 Cleanup completed");
|
|
685
720
|
}
|
|
686
721
|
}
|
|
722
|
+
/**
|
|
723
|
+
* Supprime les anciennes routes sans [lang] et les layouts générés
|
|
724
|
+
*/
|
|
725
|
+
function cleanOldRoutesWithoutLang() {
|
|
726
|
+
const generatedComment = "// GENERATED BY LASTBRAIN MODULE BUILD";
|
|
727
|
+
// Supprimer les anciens dossiers de sections sans [lang]
|
|
728
|
+
const oldSections = ["(public)", "auth", "admin"];
|
|
729
|
+
oldSections.forEach((section) => {
|
|
730
|
+
const sectionPath = path.join(appDirectory, section);
|
|
731
|
+
if (fs.existsSync(sectionPath)) {
|
|
732
|
+
try {
|
|
733
|
+
// Supprimer récursivement tout le dossier
|
|
734
|
+
fs.rmSync(sectionPath, { recursive: true, force: true });
|
|
735
|
+
if (isDebugMode) {
|
|
736
|
+
console.log(`🗑️ Removed old section without [lang]: ${section}`);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
catch (error) {
|
|
740
|
+
console.warn(`⚠️ Could not remove ${section}:`, error);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
// Supprimer les layouts générés dans [lang]
|
|
745
|
+
const langLayoutsToRemove = [
|
|
746
|
+
path.join(appDirectory, "[lang]", "layout.tsx"),
|
|
747
|
+
path.join(appDirectory, "[lang]", "auth", "layout.tsx"),
|
|
748
|
+
path.join(appDirectory, "[lang]", "admin", "layout.tsx"),
|
|
749
|
+
];
|
|
750
|
+
langLayoutsToRemove.forEach((layoutPath) => {
|
|
751
|
+
if (fs.existsSync(layoutPath)) {
|
|
752
|
+
try {
|
|
753
|
+
const content = fs.readFileSync(layoutPath, "utf-8");
|
|
754
|
+
// Supprimer si c'est un fichier généré
|
|
755
|
+
if (content.includes(generatedComment) ||
|
|
756
|
+
content.includes("AppProviders")) {
|
|
757
|
+
fs.unlinkSync(layoutPath);
|
|
758
|
+
if (isDebugMode) {
|
|
759
|
+
console.log(`🗑️ Removed generated layout: ${layoutPath}`);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
console.warn(`⚠️ Could not remove layout ${layoutPath}:`, error);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
}
|
|
687
769
|
function generateAppAside() {
|
|
688
770
|
const targetPath = path.join(appDirectory, "components", "AppAside.tsx");
|
|
689
|
-
// Ne pas écraser si le fichier existe déjà
|
|
690
|
-
if (fs.existsSync(targetPath)) {
|
|
691
|
-
if (isDebugMode) {
|
|
692
|
-
console.log(`⏭️ AppAside already exists, skipping: ${targetPath}`);
|
|
693
|
-
}
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
696
771
|
const templateContent = `"use client";
|
|
697
772
|
|
|
698
773
|
import { AppAside as UIAppAside } from "@lastbrain/app";
|
|
@@ -717,86 +792,236 @@ export function AppAside({ className = "", isVisible = true }: AppAsideProps) {
|
|
|
717
792
|
);
|
|
718
793
|
}`;
|
|
719
794
|
try {
|
|
720
|
-
|
|
721
|
-
fs.writeFileSync(targetPath, templateContent, "utf-8");
|
|
722
|
-
if (isDebugMode) {
|
|
723
|
-
console.log(`✅ Generated AppAside component: ${targetPath}`);
|
|
724
|
-
}
|
|
795
|
+
writeScaffoldFile(targetPath, templateContent, "AppAside component");
|
|
725
796
|
}
|
|
726
797
|
catch (error) {
|
|
727
798
|
console.error(`❌ Error generating AppAside component: ${error}`);
|
|
728
799
|
}
|
|
729
800
|
}
|
|
730
801
|
function generateLayouts() {
|
|
731
|
-
// Générer
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
|
|
802
|
+
// Générer ClientLayout wrapper client
|
|
803
|
+
const clientLayoutPath = path.join(appDirectory, "components", "ClientLayout.tsx");
|
|
804
|
+
const clientLayoutContent = `"use client";
|
|
805
|
+
|
|
806
|
+
import { HeroUIProvider } from "@heroui/system";
|
|
807
|
+
import { ThemeProvider } from "next-themes";
|
|
808
|
+
import { AppProviders } from "./AppProviders";
|
|
809
|
+
import { AppHeader } from "./AppHeader";
|
|
810
|
+
import type { ReactNode } from "react";
|
|
811
|
+
import { useLocalizedRouter, type Language } from "@lastbrain/core";
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
export function ClientLayout({
|
|
815
|
+
children,
|
|
816
|
+
lang = "fr",
|
|
817
|
+
translations = {},
|
|
818
|
+
}: {
|
|
819
|
+
children: ReactNode;
|
|
820
|
+
lang?: Language;
|
|
821
|
+
translations?: Record<string, string>;
|
|
822
|
+
}) {
|
|
823
|
+
const router = useLocalizedRouter();
|
|
824
|
+
|
|
825
|
+
return (
|
|
826
|
+
<HeroUIProvider navigate={router.push}>
|
|
827
|
+
<ThemeProvider
|
|
828
|
+
attribute="class"
|
|
829
|
+
defaultTheme="dark"
|
|
830
|
+
enableSystem={false}
|
|
831
|
+
storageKey="lastbrain-theme"
|
|
832
|
+
>
|
|
833
|
+
<AppProviders lang={lang} translations={translations}>
|
|
834
|
+
<AppHeader />
|
|
835
|
+
<div className="min-h-screen text-foreground bg-background">
|
|
836
|
+
{children}
|
|
837
|
+
</div>
|
|
838
|
+
</AppProviders>
|
|
839
|
+
</ThemeProvider>
|
|
840
|
+
</HeroUIProvider>
|
|
841
|
+
);
|
|
842
|
+
}`;
|
|
843
|
+
try {
|
|
844
|
+
writeScaffoldFile(clientLayoutPath, clientLayoutContent, "ClientLayout component");
|
|
845
|
+
}
|
|
846
|
+
catch (error) {
|
|
847
|
+
console.error(`❌ Error generating ClientLayout component: ${error}`);
|
|
848
|
+
}
|
|
849
|
+
// Générer AppProviders wrapper client
|
|
850
|
+
const appProvidersPath = path.join(appDirectory, "components", "AppProviders.tsx");
|
|
851
|
+
const appProvidersContent = `"use client";
|
|
852
|
+
|
|
853
|
+
import { AppProviders as BaseAppProviders } from "@lastbrain/app";
|
|
854
|
+
import { realtimeConfig } from "../../config/realtime";
|
|
855
|
+
import type { ReactNode } from "react";
|
|
856
|
+
import type { Language } from "@lastbrain/core";
|
|
857
|
+
|
|
858
|
+
export function AppProviders({
|
|
859
|
+
children,
|
|
860
|
+
lang = "fr",
|
|
861
|
+
translations = {},
|
|
862
|
+
}: {
|
|
863
|
+
children: ReactNode;
|
|
864
|
+
lang?: Language;
|
|
865
|
+
translations?: Record<string, string>;
|
|
866
|
+
}) {
|
|
867
|
+
return (
|
|
868
|
+
<BaseAppProviders
|
|
869
|
+
realtimeConfig={realtimeConfig}
|
|
870
|
+
lang={lang}
|
|
871
|
+
translations={translations}
|
|
872
|
+
>
|
|
873
|
+
{children}
|
|
874
|
+
</BaseAppProviders>
|
|
875
|
+
);
|
|
876
|
+
}`;
|
|
877
|
+
try {
|
|
878
|
+
writeScaffoldFile(appProvidersPath, appProvidersContent, "AppProviders component");
|
|
879
|
+
}
|
|
880
|
+
catch (error) {
|
|
881
|
+
console.error(`❌ Error generating AppProviders component: ${error}`);
|
|
882
|
+
}
|
|
883
|
+
// Générer AppHeader component
|
|
884
|
+
const appHeaderPath = path.join(appDirectory, "components", "AppHeader.tsx");
|
|
885
|
+
const appHeaderContent = `"use client";
|
|
886
|
+
|
|
887
|
+
import { Header } from "@lastbrain/ui";
|
|
888
|
+
import { LanguageSwitcher } from "@lastbrain/app";
|
|
735
889
|
import { menuConfig } from "../../config/menu";
|
|
736
890
|
|
|
737
|
-
export
|
|
891
|
+
export function AppHeader() {
|
|
892
|
+
return (
|
|
893
|
+
<Header
|
|
894
|
+
menuConfig={menuConfig}
|
|
895
|
+
languageSwitcher={<LanguageSwitcher variant="minimal" />}
|
|
896
|
+
/>
|
|
897
|
+
);
|
|
898
|
+
}`;
|
|
899
|
+
try {
|
|
900
|
+
writeScaffoldFile(appHeaderPath, appHeaderContent, "AppHeader component");
|
|
901
|
+
}
|
|
902
|
+
catch (error) {
|
|
903
|
+
console.error(`❌ Error generating AppHeader component: ${error}`);
|
|
904
|
+
}
|
|
905
|
+
// Générer layout [lang] pour la gestion i18n
|
|
906
|
+
const langLayoutPath = path.join(appDirectory, "[lang]", "layout.tsx");
|
|
907
|
+
if (!fs.existsSync(langLayoutPath)) {
|
|
908
|
+
const langLayoutContent = `import { loadTranslations } from "@lastbrain/app/i18n/server-lang";
|
|
909
|
+
import { ClientLayout } from "../../components/ClientLayout";
|
|
910
|
+
|
|
911
|
+
export default async function LangLayout({
|
|
738
912
|
children,
|
|
913
|
+
params,
|
|
739
914
|
}: {
|
|
740
915
|
children: React.ReactNode;
|
|
916
|
+
params: Promise<{ lang: string }>;
|
|
741
917
|
}) {
|
|
918
|
+
const { lang } = await params;
|
|
919
|
+
const validLang = lang === "en" || lang === "fr" ? lang : "fr";
|
|
920
|
+
|
|
921
|
+
// Charger les traductions pour cette langue
|
|
922
|
+
const translations = await loadTranslations(validLang);
|
|
923
|
+
|
|
742
924
|
return (
|
|
743
|
-
<
|
|
744
|
-
|
|
745
|
-
|
|
925
|
+
<ClientLayout lang={validLang} translations={translations}>
|
|
926
|
+
<div className="min-h-screen pt-16">
|
|
927
|
+
{children}
|
|
928
|
+
</div>
|
|
929
|
+
</ClientLayout>
|
|
746
930
|
);
|
|
747
931
|
}`;
|
|
748
932
|
try {
|
|
749
|
-
ensureDirectory(path.dirname(
|
|
750
|
-
fs.writeFileSync(
|
|
933
|
+
ensureDirectory(path.dirname(langLayoutPath));
|
|
934
|
+
fs.writeFileSync(langLayoutPath, langLayoutContent, "utf-8");
|
|
751
935
|
if (isDebugMode) {
|
|
752
|
-
console.log(`✅ Generated
|
|
936
|
+
console.log(`✅ Generated [lang] layout: ${langLayoutPath}`);
|
|
753
937
|
}
|
|
754
938
|
}
|
|
755
939
|
catch (error) {
|
|
756
|
-
console.error(`❌ Error generating
|
|
940
|
+
console.error(`❌ Error generating [lang] layout: ${error}`);
|
|
757
941
|
}
|
|
758
942
|
}
|
|
759
943
|
else if (isDebugMode) {
|
|
760
|
-
console.log(`⏭️
|
|
944
|
+
console.log(`⏭️ [lang] layout already exists, skipping: ${langLayoutPath}`);
|
|
945
|
+
}
|
|
946
|
+
// Générer layout auth avec sidebar
|
|
947
|
+
const authLayoutPath = path.join(appDirectory, "[lang]", "auth", "layout.tsx");
|
|
948
|
+
const authLayoutContent = `"use client";
|
|
949
|
+
|
|
950
|
+
import { AuthLayoutWithSidebar, langHref, useLanguage } from "@lastbrain/app";
|
|
951
|
+
import { menuConfig as fullMenuConfig } from "../../../config/menu";
|
|
952
|
+
|
|
953
|
+
export default function SectionLayout({
|
|
954
|
+
children,
|
|
955
|
+
}: {
|
|
956
|
+
children: React.ReactNode;
|
|
957
|
+
}) {
|
|
958
|
+
const { lang } = useLanguage();
|
|
959
|
+
|
|
960
|
+
// Transformer les paths du menu avec la langue
|
|
961
|
+
const menuConfig = {
|
|
962
|
+
...fullMenuConfig,
|
|
963
|
+
auth: (fullMenuConfig.auth || []).map(item => ({
|
|
964
|
+
...item,
|
|
965
|
+
path: item.path?.startsWith("/") && !item.path?.startsWith("/api/")
|
|
966
|
+
? langHref(item.path, lang)
|
|
967
|
+
: item.path
|
|
968
|
+
})),
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
return (
|
|
972
|
+
<AuthLayoutWithSidebar menuConfig={menuConfig}>
|
|
973
|
+
{children}
|
|
974
|
+
</AuthLayoutWithSidebar>
|
|
975
|
+
);
|
|
976
|
+
}`;
|
|
977
|
+
try {
|
|
978
|
+
writeScaffoldFile(authLayoutPath, authLayoutContent, "auth layout with sidebar");
|
|
979
|
+
}
|
|
980
|
+
catch (error) {
|
|
981
|
+
console.error(`❌ Error generating auth layout: ${error}`);
|
|
761
982
|
}
|
|
762
983
|
// Générer layout admin avec sidebar
|
|
763
|
-
const adminLayoutPath = path.join(appDirectory, "admin", "layout.tsx");
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
import {
|
|
984
|
+
const adminLayoutPath = path.join(appDirectory, "[lang]", "admin", "layout.tsx");
|
|
985
|
+
const adminLayoutContent = `"use client";
|
|
986
|
+
|
|
987
|
+
import { AdminLayoutWithSidebar, langHref, useLanguage } from "@lastbrain/app";
|
|
988
|
+
import { menuConfig as fullMenuConfig } from "../../../config/menu";
|
|
767
989
|
|
|
768
990
|
export default function AdminLayout({
|
|
769
991
|
children,
|
|
770
992
|
}: {
|
|
771
993
|
children: React.ReactNode;
|
|
772
994
|
}) {
|
|
995
|
+
const { lang } = useLanguage();
|
|
996
|
+
|
|
997
|
+
// Transformer les paths du menu avec la langue
|
|
998
|
+
const menuConfig = {
|
|
999
|
+
...fullMenuConfig,
|
|
1000
|
+
admin: (fullMenuConfig.admin || []).map(item => ({
|
|
1001
|
+
...item,
|
|
1002
|
+
path: item.path?.startsWith("/") && !item.path?.startsWith("/api/")
|
|
1003
|
+
? langHref(item.path, lang)
|
|
1004
|
+
: item.path
|
|
1005
|
+
})),
|
|
1006
|
+
};
|
|
1007
|
+
|
|
773
1008
|
return (
|
|
774
1009
|
<AdminLayoutWithSidebar menuConfig={menuConfig}>
|
|
775
1010
|
{children}
|
|
776
1011
|
</AdminLayoutWithSidebar>
|
|
777
1012
|
);
|
|
778
1013
|
}`;
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
fs.writeFileSync(adminLayoutPath, adminLayoutContent, "utf-8");
|
|
782
|
-
if (isDebugMode) {
|
|
783
|
-
console.log(`✅ Generated admin layout with sidebar: ${adminLayoutPath}`);
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
catch (error) {
|
|
787
|
-
console.error(`❌ Error generating admin layout: ${error}`);
|
|
788
|
-
}
|
|
1014
|
+
try {
|
|
1015
|
+
writeScaffoldFile(adminLayoutPath, adminLayoutContent, "admin layout with sidebar");
|
|
789
1016
|
}
|
|
790
|
-
|
|
791
|
-
console.
|
|
1017
|
+
catch (error) {
|
|
1018
|
+
console.error(`❌ Error generating admin layout: ${error}`);
|
|
792
1019
|
}
|
|
793
1020
|
}
|
|
794
1021
|
async function generateRealtimeConfig(moduleConfigs) {
|
|
795
1022
|
try {
|
|
796
1023
|
// Extraire les configurations realtime des modules
|
|
797
|
-
const realtimeConfigs = moduleConfigs
|
|
798
|
-
.filter((config) => config.realtime)
|
|
799
|
-
.map((config) => config.realtime);
|
|
1024
|
+
const realtimeConfigs = moduleConfigs.flatMap((config) => (config.realtime ? [config.realtime] : []));
|
|
800
1025
|
if (realtimeConfigs.length === 0) {
|
|
801
1026
|
console.log("⏭️ No realtime configuration found in modules");
|
|
802
1027
|
return;
|
|
@@ -846,8 +1071,7 @@ async function generateUserTabsConfig(moduleConfigs) {
|
|
|
846
1071
|
try {
|
|
847
1072
|
// Extraire les configurations user tabs des modules
|
|
848
1073
|
const userTabsConfigs = moduleConfigs
|
|
849
|
-
.
|
|
850
|
-
.flatMap((config) => config.userTabs.map((tab) => ({
|
|
1074
|
+
.flatMap((config) => (config.userTabs ?? []).map((tab) => ({
|
|
851
1075
|
...tab,
|
|
852
1076
|
moduleName: config.moduleName,
|
|
853
1077
|
})))
|
|
@@ -902,9 +1126,7 @@ async function generateUserTabsConfig(moduleConfigs) {
|
|
|
902
1126
|
async function generateBucketsConfig(moduleConfigs) {
|
|
903
1127
|
try {
|
|
904
1128
|
// Extraire les configurations storage des modules
|
|
905
|
-
const allBuckets = moduleConfigs
|
|
906
|
-
.filter((config) => config.storage?.buckets && config.storage.buckets.length > 0)
|
|
907
|
-
.flatMap((config) => config.storage.buckets);
|
|
1129
|
+
const allBuckets = moduleConfigs.flatMap((config) => config.storage?.buckets ?? []);
|
|
908
1130
|
if (allBuckets.length === 0) {
|
|
909
1131
|
console.log("⏭️ No storage buckets configuration found in modules");
|
|
910
1132
|
return;
|
|
@@ -1027,9 +1249,7 @@ export function isValidFileSize(bucketName: string, fileSize: number): boolean {
|
|
|
1027
1249
|
async function generateStorageProxyApi(moduleConfigs) {
|
|
1028
1250
|
try {
|
|
1029
1251
|
// Extraire les configurations storage des modules
|
|
1030
|
-
const allBuckets = moduleConfigs
|
|
1031
|
-
.filter((config) => config.storage?.buckets && config.storage.buckets.length > 0)
|
|
1032
|
-
.flatMap((config) => config.storage.buckets);
|
|
1252
|
+
const allBuckets = moduleConfigs.flatMap((config) => config.storage?.buckets ?? []);
|
|
1033
1253
|
if (allBuckets.length === 0) {
|
|
1034
1254
|
console.log("⏭️ No storage buckets found, skipping proxy API generation");
|
|
1035
1255
|
return;
|
|
@@ -1159,9 +1379,7 @@ export async function GET(
|
|
|
1159
1379
|
async function generateFooterConfig(moduleConfigs) {
|
|
1160
1380
|
try {
|
|
1161
1381
|
// Extraire tous les liens footer des modules
|
|
1162
|
-
const allFooterLinks = moduleConfigs
|
|
1163
|
-
.filter((config) => config.footer && config.footer.length > 0)
|
|
1164
|
-
.flatMap((config) => config.footer);
|
|
1382
|
+
const allFooterLinks = moduleConfigs.flatMap((config) => config.footer ?? []);
|
|
1165
1383
|
if (allFooterLinks.length === 0) {
|
|
1166
1384
|
console.log("⏭️ No footer links found, skipping footer config generation");
|
|
1167
1385
|
return;
|
|
@@ -1203,6 +1421,85 @@ export const footerConfig: FooterConfig = {
|
|
|
1203
1421
|
console.error("❌ Error generating footer config:", error);
|
|
1204
1422
|
}
|
|
1205
1423
|
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Génère les fichiers i18n en concaténant les traductions des modules
|
|
1426
|
+
*/
|
|
1427
|
+
async function generateI18nFiles() {
|
|
1428
|
+
const appI18nDir = path.join(projectRoot, "i18n");
|
|
1429
|
+
const packagesDir = path.join(_monorepoRoot, "packages");
|
|
1430
|
+
const translations = {
|
|
1431
|
+
fr: {},
|
|
1432
|
+
en: {},
|
|
1433
|
+
};
|
|
1434
|
+
try {
|
|
1435
|
+
// Importer glob dynamiquement
|
|
1436
|
+
const { glob } = await import("glob");
|
|
1437
|
+
// Rechercher tous les fichiers i18n dans packages/**/src/i18n/*.json
|
|
1438
|
+
const pattern = path.join(packagesDir, "**/src/i18n/*.json");
|
|
1439
|
+
const files = await glob(pattern, {
|
|
1440
|
+
ignore: ["**/node_modules/**", "**/dist/**"],
|
|
1441
|
+
});
|
|
1442
|
+
if (isDebugMode) {
|
|
1443
|
+
console.log(`\n🔍 Found ${files.length} i18n files\n`);
|
|
1444
|
+
}
|
|
1445
|
+
for (const file of files) {
|
|
1446
|
+
const fileName = path.basename(file, ".json");
|
|
1447
|
+
const lang = fileName; // fr.json -> fr, en.json -> en
|
|
1448
|
+
if (!translations[lang]) {
|
|
1449
|
+
translations[lang] = {};
|
|
1450
|
+
}
|
|
1451
|
+
try {
|
|
1452
|
+
const content = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
1453
|
+
const moduleName = extractModuleNameFromPath(file);
|
|
1454
|
+
if (isDebugMode) {
|
|
1455
|
+
console.log(` ✓ ${moduleName} (${lang})`);
|
|
1456
|
+
}
|
|
1457
|
+
// Préfixer uniquement si la clé n'est pas déjà namespacée (avec ou sans suffixe -pro)
|
|
1458
|
+
const moduleNamespace = moduleName.replace(/-pro$/, "");
|
|
1459
|
+
for (const [key, value] of Object.entries(content)) {
|
|
1460
|
+
const keyRoot = key.split(".")[0];
|
|
1461
|
+
const isGenericNamespaced = keyRoot.startsWith("module-");
|
|
1462
|
+
const isAlreadyNamespaced = isGenericNamespaced ||
|
|
1463
|
+
key.startsWith(`${moduleName}.`) ||
|
|
1464
|
+
key.startsWith(`${moduleNamespace}.`);
|
|
1465
|
+
const normalizedKey = isAlreadyNamespaced
|
|
1466
|
+
? key
|
|
1467
|
+
: `${moduleNamespace}.${key}`;
|
|
1468
|
+
translations[lang][normalizedKey] = value;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
catch (error) {
|
|
1472
|
+
console.warn(` ⚠️ Error reading ${file}:`, error);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
// Créer le dossier i18n s'il n'existe pas
|
|
1476
|
+
ensureDirectory(appI18nDir);
|
|
1477
|
+
// Écrire les fichiers de traduction
|
|
1478
|
+
for (const [lang, content] of Object.entries(translations)) {
|
|
1479
|
+
const filePath = path.join(appI18nDir, `${lang}.json`);
|
|
1480
|
+
fs.writeFileSync(filePath, JSON.stringify(content, null, 2), "utf-8");
|
|
1481
|
+
const keyCount = Object.keys(content).length;
|
|
1482
|
+
if (isDebugMode) {
|
|
1483
|
+
console.log(`✅ Generated ${filePath} (${keyCount} keys)`);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
catch (error) {
|
|
1488
|
+
console.error("❌ Error generating i18n files:", error);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
/**
|
|
1492
|
+
* Extrait le nom du module depuis le chemin du fichier
|
|
1493
|
+
*/
|
|
1494
|
+
function extractModuleNameFromPath(filePath) {
|
|
1495
|
+
// packages/module-auth/src/i18n/fr.json -> module-auth
|
|
1496
|
+
const parts = filePath.split(path.sep);
|
|
1497
|
+
const packagesIndex = parts.indexOf("packages");
|
|
1498
|
+
if (packagesIndex !== -1 && packagesIndex < parts.length - 1) {
|
|
1499
|
+
return parts[packagesIndex + 1];
|
|
1500
|
+
}
|
|
1501
|
+
return "unknown";
|
|
1502
|
+
}
|
|
1206
1503
|
export async function runModuleBuild() {
|
|
1207
1504
|
ensureDirectory(appDirectory);
|
|
1208
1505
|
// Nettoyer les fichiers générés précédemment
|
|
@@ -1210,6 +1507,11 @@ export async function runModuleBuild() {
|
|
|
1210
1507
|
console.log("🧹 Cleaning previously generated files...");
|
|
1211
1508
|
}
|
|
1212
1509
|
cleanGeneratedFiles();
|
|
1510
|
+
// Supprimer les anciennes routes sans [lang]
|
|
1511
|
+
if (isDebugMode) {
|
|
1512
|
+
console.log("🗑️ Removing old routes without [lang]...");
|
|
1513
|
+
}
|
|
1514
|
+
cleanOldRoutesWithoutLang();
|
|
1213
1515
|
const moduleConfigs = await loadModuleConfigs();
|
|
1214
1516
|
if (isDebugMode) {
|
|
1215
1517
|
console.log(`🔍 Loaded ${moduleConfigs.length} module configurations`);
|
|
@@ -1231,10 +1533,12 @@ export async function runModuleBuild() {
|
|
|
1231
1533
|
const apisByPath = new Map();
|
|
1232
1534
|
moduleConfigs.forEach((moduleConfig) => {
|
|
1233
1535
|
moduleConfig.apis.forEach((api) => {
|
|
1234
|
-
|
|
1235
|
-
|
|
1536
|
+
const apisForPath = apisByPath.get(api.path);
|
|
1537
|
+
if (apisForPath) {
|
|
1538
|
+
apisForPath.push({ moduleConfig, api });
|
|
1539
|
+
return;
|
|
1236
1540
|
}
|
|
1237
|
-
apisByPath.
|
|
1541
|
+
apisByPath.set(api.path, [{ moduleConfig, api }]);
|
|
1238
1542
|
});
|
|
1239
1543
|
});
|
|
1240
1544
|
// Générer les fichiers de route groupés
|
|
@@ -1272,6 +1576,11 @@ export async function runModuleBuild() {
|
|
|
1272
1576
|
console.log("🦶 Generating footer configuration...");
|
|
1273
1577
|
}
|
|
1274
1578
|
await generateFooterConfig(moduleConfigs);
|
|
1579
|
+
// Générer les fichiers i18n
|
|
1580
|
+
if (isDebugMode) {
|
|
1581
|
+
console.log("🌍 Building i18n files...");
|
|
1582
|
+
}
|
|
1583
|
+
await generateI18nFiles();
|
|
1275
1584
|
// Message de succès final
|
|
1276
1585
|
if (!isDebugMode) {
|
|
1277
1586
|
console.log("\n✅ Module build completed successfully!");
|