@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.
Files changed (257) hide show
  1. package/dist/app-shell/not-found.js +2 -2
  2. package/dist/components/LanguageSwitcher.d.ts +7 -0
  3. package/dist/components/LanguageSwitcher.d.ts.map +1 -0
  4. package/dist/components/LanguageSwitcher.js +62 -0
  5. package/dist/config/version.d.ts.map +1 -1
  6. package/dist/config/version.js +19 -21
  7. package/dist/i18n/LanguageProvider.d.ts +4 -0
  8. package/dist/i18n/LanguageProvider.d.ts.map +1 -0
  9. package/dist/i18n/LanguageProvider.js +6 -0
  10. package/dist/i18n/TranslationsScript.d.ts +8 -0
  11. package/dist/i18n/TranslationsScript.d.ts.map +1 -0
  12. package/dist/i18n/TranslationsScript.js +10 -0
  13. package/dist/i18n/cookies.d.ts +6 -0
  14. package/dist/i18n/cookies.d.ts.map +1 -0
  15. package/dist/i18n/cookies.js +24 -0
  16. package/dist/i18n/langHrefHelper.d.ts +36 -0
  17. package/dist/i18n/langHrefHelper.d.ts.map +1 -0
  18. package/dist/i18n/langHrefHelper.js +41 -0
  19. package/dist/i18n/server-helpers.d.ts +33 -0
  20. package/dist/i18n/server-helpers.d.ts.map +1 -0
  21. package/dist/i18n/server-helpers.js +39 -0
  22. package/dist/i18n/server-lang.d.ts +10 -0
  23. package/dist/i18n/server-lang.d.ts.map +1 -0
  24. package/dist/i18n/server-lang.js +42 -0
  25. package/dist/i18n/server.d.ts +10 -0
  26. package/dist/i18n/server.d.ts.map +1 -0
  27. package/dist/i18n/server.js +8 -0
  28. package/dist/i18n/types.d.ts +38 -0
  29. package/dist/i18n/types.d.ts.map +1 -0
  30. package/dist/i18n/types.js +4 -0
  31. package/dist/i18n/useLangRouter.d.ts +12 -0
  32. package/dist/i18n/useLangRouter.d.ts.map +1 -0
  33. package/dist/i18n/useLangRouter.js +18 -0
  34. package/dist/i18n/useLink.d.ts +34 -0
  35. package/dist/i18n/useLink.d.ts.map +1 -0
  36. package/dist/i18n/useLink.js +58 -0
  37. package/dist/i18n/useModuleTranslation.d.ts +11 -0
  38. package/dist/i18n/useModuleTranslation.d.ts.map +1 -0
  39. package/dist/i18n/useModuleTranslation.js +23 -0
  40. package/dist/index.d.ts +7 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +7 -0
  43. package/dist/layouts/AdminLayout.js +1 -1
  44. package/dist/layouts/AppProviders.d.ts +4 -1
  45. package/dist/layouts/AppProviders.d.ts.map +1 -1
  46. package/dist/layouts/AppProviders.js +18 -8
  47. package/dist/layouts/AuthLayout.js +1 -1
  48. package/dist/layouts/AuthLayoutWithSidebar.d.ts.map +1 -1
  49. package/dist/layouts/AuthLayoutWithSidebar.js +5 -3
  50. package/dist/layouts/PublicLayout.d.ts.map +1 -1
  51. package/dist/layouts/PublicLayout.js +1 -1
  52. package/dist/layouts/RootLayout.d.ts +4 -1
  53. package/dist/layouts/RootLayout.d.ts.map +1 -1
  54. package/dist/layouts/RootLayout.js +2 -2
  55. package/dist/scripts/i18n-build.d.ts +7 -0
  56. package/dist/scripts/i18n-build.d.ts.map +1 -0
  57. package/dist/scripts/i18n-build.js +100 -0
  58. package/dist/scripts/init-app.js +12 -12
  59. package/dist/scripts/module-build.d.ts.map +1 -1
  60. package/dist/scripts/module-build.js +390 -83
  61. package/dist/styles.css +1 -1
  62. package/dist/templates/DefaultDoc.d.ts.map +1 -1
  63. package/dist/templates/DefaultDoc.js +90 -2
  64. package/dist/templates/middleware-i18n.example.d.ts +16 -0
  65. package/dist/templates/middleware-i18n.example.d.ts.map +1 -0
  66. package/dist/templates/middleware-i18n.example.js +72 -0
  67. package/package.json +14 -3
  68. package/src/app-shell/not-found.tsx +2 -2
  69. package/src/components/LanguageSwitcher.tsx +156 -0
  70. package/src/config/version.ts +19 -21
  71. package/src/i18n/LanguageProvider.tsx +7 -0
  72. package/src/i18n/README_LANG_HELPERS.md +187 -0
  73. package/src/i18n/TranslationsScript.tsx +17 -0
  74. package/src/i18n/cookies.ts +24 -0
  75. package/src/i18n/langHrefHelper.ts +51 -0
  76. package/src/i18n/server-helpers.ts +48 -0
  77. package/src/i18n/server-lang.ts +51 -0
  78. package/src/i18n/server.ts +13 -0
  79. package/src/i18n/types.ts +39 -0
  80. package/src/i18n/useLangRouter.ts +21 -0
  81. package/src/i18n/useLink.ts +60 -0
  82. package/src/i18n/useModuleTranslation.ts +27 -0
  83. package/src/index.ts +20 -0
  84. package/src/layouts/AdminLayout.tsx +1 -1
  85. package/src/layouts/AppProviders.tsx +35 -16
  86. package/src/layouts/AuthLayout.tsx +1 -1
  87. package/src/layouts/AuthLayoutWithSidebar.tsx +5 -3
  88. package/src/layouts/PublicLayout.tsx +3 -1
  89. package/src/layouts/RootLayout.tsx +13 -6
  90. package/src/scripts/i18n-build.ts +122 -0
  91. package/src/scripts/init-app.ts +12 -12
  92. package/src/scripts/module-build.ts +483 -96
  93. package/src/templates/DefaultDoc.tsx +511 -1
  94. package/src/templates/middleware-i18n.example.ts +92 -0
  95. package/dist/app-shell/(admin)/dashboard/page.d.ts +0 -2
  96. package/dist/app-shell/(admin)/dashboard/page.d.ts.map +0 -1
  97. package/dist/app-shell/(admin)/dashboard/page.js +0 -5
  98. package/dist/app-shell/(admin)/page.d.ts +0 -2
  99. package/dist/app-shell/(admin)/page.d.ts.map +0 -1
  100. package/dist/app-shell/(admin)/page.js +0 -5
  101. package/dist/app-shell/(auth)/dashboard/page.d.ts +0 -2
  102. package/dist/app-shell/(auth)/dashboard/page.d.ts.map +0 -1
  103. package/dist/app-shell/(auth)/dashboard/page.js +0 -5
  104. package/dist/app-shell/(auth)/page.d.ts +0 -2
  105. package/dist/app-shell/(auth)/page.d.ts.map +0 -1
  106. package/dist/app-shell/(auth)/page.js +0 -5
  107. package/dist/components/TableStructure.d.ts +0 -8
  108. package/dist/components/TableStructure.d.ts.map +0 -1
  109. package/dist/components/TableStructure.js +0 -37
  110. package/dist/hooks/useNotificationsSimple.d.ts +0 -20
  111. package/dist/hooks/useNotificationsSimple.d.ts.map +0 -1
  112. package/dist/hooks/useNotificationsSimple.js +0 -37
  113. package/dist/module-build.d.ts +0 -2
  114. package/dist/module-build.d.ts.map +0 -1
  115. package/dist/module-build.js +0 -50
  116. package/dist/modules/index.d.ts +0 -3
  117. package/dist/modules/index.d.ts.map +0 -1
  118. package/dist/modules/index.js +0 -2
  119. package/dist/src/__tests__/module-registry.test.d.ts +0 -2
  120. package/dist/src/__tests__/module-registry.test.d.ts.map +0 -1
  121. package/dist/src/__tests__/module-registry.test.js +0 -53
  122. package/dist/src/app-shell/(admin)/layout.d.ts +0 -4
  123. package/dist/src/app-shell/(admin)/layout.d.ts.map +0 -1
  124. package/dist/src/app-shell/(admin)/layout.js +0 -5
  125. package/dist/src/app-shell/(auth)/layout.d.ts +0 -4
  126. package/dist/src/app-shell/(auth)/layout.d.ts.map +0 -1
  127. package/dist/src/app-shell/(auth)/layout.js +0 -5
  128. package/dist/src/app-shell/(public)/page.d.ts +0 -2
  129. package/dist/src/app-shell/(public)/page.d.ts.map +0 -1
  130. package/dist/src/app-shell/(public)/page.js +0 -5
  131. package/dist/src/app-shell/layout.d.ts +0 -3
  132. package/dist/src/app-shell/layout.d.ts.map +0 -1
  133. package/dist/src/app-shell/layout.js +0 -3
  134. package/dist/src/app-shell/not-found.d.ts +0 -2
  135. package/dist/src/app-shell/not-found.d.ts.map +0 -1
  136. package/dist/src/app-shell/not-found.js +0 -10
  137. package/dist/src/auth/authHelpers.d.ts +0 -7
  138. package/dist/src/auth/authHelpers.d.ts.map +0 -1
  139. package/dist/src/auth/authHelpers.js +0 -19
  140. package/dist/src/auth/useAuthSession.d.ts +0 -7
  141. package/dist/src/auth/useAuthSession.d.ts.map +0 -1
  142. package/dist/src/auth/useAuthSession.js +0 -49
  143. package/dist/src/cli.d.ts +0 -3
  144. package/dist/src/cli.d.ts.map +0 -1
  145. package/dist/src/cli.js +0 -143
  146. package/dist/src/components/NotificationContainer.d.ts +0 -2
  147. package/dist/src/components/NotificationContainer.d.ts.map +0 -1
  148. package/dist/src/components/NotificationContainer.js +0 -8
  149. package/dist/src/hooks/useNotifications.d.ts +0 -30
  150. package/dist/src/hooks/useNotifications.d.ts.map +0 -1
  151. package/dist/src/hooks/useNotifications.js +0 -165
  152. package/dist/src/index.d.ts +0 -22
  153. package/dist/src/index.d.ts.map +0 -1
  154. package/dist/src/index.js +0 -22
  155. package/dist/src/layouts/AdminLayout.d.ts +0 -4
  156. package/dist/src/layouts/AdminLayout.d.ts.map +0 -1
  157. package/dist/src/layouts/AdminLayout.js +0 -4
  158. package/dist/src/layouts/AdminLayoutWithSidebar.d.ts +0 -10
  159. package/dist/src/layouts/AdminLayoutWithSidebar.d.ts.map +0 -1
  160. package/dist/src/layouts/AdminLayoutWithSidebar.js +0 -62
  161. package/dist/src/layouts/AppProviders.d.ts +0 -27
  162. package/dist/src/layouts/AppProviders.d.ts.map +0 -1
  163. package/dist/src/layouts/AppProviders.js +0 -48
  164. package/dist/src/layouts/AuthLayout.d.ts +0 -4
  165. package/dist/src/layouts/AuthLayout.d.ts.map +0 -1
  166. package/dist/src/layouts/AuthLayout.js +0 -4
  167. package/dist/src/layouts/AuthLayoutWithSidebar.d.ts +0 -12
  168. package/dist/src/layouts/AuthLayoutWithSidebar.d.ts.map +0 -1
  169. package/dist/src/layouts/AuthLayoutWithSidebar.js +0 -60
  170. package/dist/src/layouts/PublicLayout.d.ts +0 -8
  171. package/dist/src/layouts/PublicLayout.d.ts.map +0 -1
  172. package/dist/src/layouts/PublicLayout.js +0 -6
  173. package/dist/src/layouts/PublicLayoutWithSidebar.d.ts +0 -9
  174. package/dist/src/layouts/PublicLayoutWithSidebar.d.ts.map +0 -1
  175. package/dist/src/layouts/PublicLayoutWithSidebar.js +0 -60
  176. package/dist/src/layouts/RootLayout.d.ts +0 -6
  177. package/dist/src/layouts/RootLayout.d.ts.map +0 -1
  178. package/dist/src/layouts/RootLayout.js +0 -9
  179. package/dist/src/modules/module-loader.d.ts +0 -5
  180. package/dist/src/modules/module-loader.d.ts.map +0 -1
  181. package/dist/src/modules/module-loader.js +0 -10
  182. package/dist/src/scripts/db-init.d.ts +0 -2
  183. package/dist/src/scripts/db-init.d.ts.map +0 -1
  184. package/dist/src/scripts/db-init.js +0 -300
  185. package/dist/src/scripts/db-migrations-sync.d.ts +0 -2
  186. package/dist/src/scripts/db-migrations-sync.d.ts.map +0 -1
  187. package/dist/src/scripts/db-migrations-sync.js +0 -84
  188. package/dist/src/scripts/dev-sync.d.ts +0 -2
  189. package/dist/src/scripts/dev-sync.d.ts.map +0 -1
  190. package/dist/src/scripts/dev-sync.js +0 -194
  191. package/dist/src/scripts/init-app.d.ts +0 -12
  192. package/dist/src/scripts/init-app.d.ts.map +0 -1
  193. package/dist/src/scripts/init-app.js +0 -2175
  194. package/dist/src/scripts/module-add.d.ts +0 -2
  195. package/dist/src/scripts/module-add.d.ts.map +0 -1
  196. package/dist/src/scripts/module-add.js +0 -232
  197. package/dist/src/scripts/module-build.d.ts +0 -2
  198. package/dist/src/scripts/module-build.d.ts.map +0 -1
  199. package/dist/src/scripts/module-build.js +0 -1280
  200. package/dist/src/scripts/module-create.d.ts +0 -28
  201. package/dist/src/scripts/module-create.d.ts.map +0 -1
  202. package/dist/src/scripts/module-create.js +0 -1429
  203. package/dist/src/scripts/module-delete.d.ts +0 -6
  204. package/dist/src/scripts/module-delete.d.ts.map +0 -1
  205. package/dist/src/scripts/module-delete.js +0 -147
  206. package/dist/src/scripts/module-list.d.ts +0 -2
  207. package/dist/src/scripts/module-list.d.ts.map +0 -1
  208. package/dist/src/scripts/module-list.js +0 -61
  209. package/dist/src/scripts/module-remove.d.ts +0 -2
  210. package/dist/src/scripts/module-remove.d.ts.map +0 -1
  211. package/dist/src/scripts/module-remove.js +0 -311
  212. package/dist/src/scripts/readme-build.d.ts +0 -2
  213. package/dist/src/scripts/readme-build.d.ts.map +0 -1
  214. package/dist/src/scripts/readme-build.js +0 -39
  215. package/dist/src/scripts/script-runner.d.ts +0 -5
  216. package/dist/src/scripts/script-runner.d.ts.map +0 -1
  217. package/dist/src/scripts/script-runner.js +0 -25
  218. package/dist/src/templates/AuthGuidePage.d.ts +0 -2
  219. package/dist/src/templates/AuthGuidePage.d.ts.map +0 -1
  220. package/dist/src/templates/AuthGuidePage.js +0 -9
  221. package/dist/src/templates/DefaultDoc.d.ts +0 -2
  222. package/dist/src/templates/DefaultDoc.d.ts.map +0 -1
  223. package/dist/src/templates/DefaultDoc.js +0 -240
  224. package/dist/src/templates/DocPage.d.ts +0 -17
  225. package/dist/src/templates/DocPage.d.ts.map +0 -1
  226. package/dist/src/templates/DocPage.js +0 -193
  227. package/dist/src/templates/DocsPageWithModules.d.ts +0 -2
  228. package/dist/src/templates/DocsPageWithModules.d.ts.map +0 -1
  229. package/dist/src/templates/DocsPageWithModules.js +0 -8
  230. package/dist/src/templates/MigrationsGuidePage.d.ts +0 -2
  231. package/dist/src/templates/MigrationsGuidePage.d.ts.map +0 -1
  232. package/dist/src/templates/MigrationsGuidePage.js +0 -11
  233. package/dist/src/templates/ModuleGuidePage.d.ts +0 -2
  234. package/dist/src/templates/ModuleGuidePage.d.ts.map +0 -1
  235. package/dist/src/templates/ModuleGuidePage.js +0 -14
  236. package/dist/src/templates/SimpleDocPage.d.ts +0 -2
  237. package/dist/src/templates/SimpleDocPage.d.ts.map +0 -1
  238. package/dist/src/templates/SimpleDocPage.js +0 -28
  239. package/dist/src/templates/SimpleHomePage.d.ts +0 -6
  240. package/dist/src/templates/SimpleHomePage.d.ts.map +0 -1
  241. package/dist/src/templates/SimpleHomePage.js +0 -7
  242. package/dist/src/types/menu.d.ts +0 -23
  243. package/dist/src/types/menu.d.ts.map +0 -1
  244. package/dist/src/types/menu.js +0 -1
  245. package/dist/templates/HomePage.d.ts +0 -6
  246. package/dist/templates/HomePage.d.ts.map +0 -1
  247. package/dist/templates/HomePage.js +0 -6
  248. package/dist/templates/components/AppAside.d.ts +0 -6
  249. package/dist/templates/components/AppAside.d.ts.map +0 -1
  250. package/dist/templates/components/AppAside.js +0 -9
  251. package/dist/templates/layouts/admin-layout.d.ts +0 -4
  252. package/dist/templates/layouts/admin-layout.d.ts.map +0 -1
  253. package/dist/templates/layouts/admin-layout.js +0 -6
  254. package/dist/templates/layouts/auth-layout.d.ts +0 -4
  255. package/dist/templates/layouts/auth-layout.d.ts.map +0 -1
  256. package/dist/templates/layouts/auth-layout.js +0 -6
  257. 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
- if (!fs.existsSync(layoutPath)) {
143
- const layoutContent = `import { ${layoutName} } from "@lastbrain/app";
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
- fs.writeFileSync(layoutPath, layoutContent);
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
- const hasDynamicParams = segments.some(
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("../../../../../config/user-tabs");
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
- content = `// GENERATED BY LASTBRAIN MODULE BUILD
299
- import { ${page.componentExport} } from "${moduleConfig.moduleName}";
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
- export default function ${wrapperName}${hasDynamicParams ? "(props: Record<string, unknown>)" : "()"} {
302
- return <${page.componentExport} ${hasDynamicParams ? "{...props}" : ""} />;
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: (ModuleMenuItemConfig & { moduleName?: string })[] = [];
350
- const authMenus: (ModuleMenuItemConfig & { moduleName?: string })[] = [];
351
- const adminMenus: (ModuleMenuItemConfig & { moduleName?: string })[] = [];
352
- const accountMenus: (ModuleMenuItemConfig & { moduleName?: string })[] = [];
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
- (menu as any).__componentRef = componentName;
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: any[]) => {
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
- if (!exportsBySource.has(handler)) {
697
- exportsBySource.set(handler, []);
741
+ const currentExports = exportsBySource.get(handler);
742
+ if (currentExports) {
743
+ currentExports.push(api.handlerExport);
744
+ return;
698
745
  }
699
- exportsBySource.get(handler)!.push(api.handlerExport);
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
- function generateAppAside() {
846
- const targetPath = path.join(appDirectory, "components", "AppAside.tsx");
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
- // Ne pas écraser si le fichier existe déjà
849
- if (fs.existsSync(targetPath)) {
850
- if (isDebugMode) {
851
- console.log(`⏭️ AppAside already exists, skipping: ${targetPath}`);
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
- return;
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
- ensureDirectory(path.dirname(targetPath));
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 layout auth avec sidebar
893
- const authLayoutPath = path.join(appDirectory, "auth", "layout.tsx");
894
- if (!fs.existsSync(authLayoutPath)) {
895
- const authLayoutContent = `import { AuthLayoutWithSidebar } from "@lastbrain/app";
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 default function SectionLayout({
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
- <AuthLayoutWithSidebar menuConfig={menuConfig}>
905
- {children}
906
- </AuthLayoutWithSidebar>
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(authLayoutPath));
912
- fs.writeFileSync(authLayoutPath, authLayoutContent, "utf-8");
1129
+ ensureDirectory(path.dirname(langLayoutPath));
1130
+ fs.writeFileSync(langLayoutPath, langLayoutContent, "utf-8");
913
1131
  if (isDebugMode) {
914
- console.log(`✅ Generated auth layout with sidebar: ${authLayoutPath}`);
1132
+ console.log(`✅ Generated [lang] layout: ${langLayoutPath}`);
915
1133
  }
916
1134
  } catch (error) {
917
- console.error(`❌ Error generating auth layout: ${error}`);
1135
+ console.error(`❌ Error generating [lang] layout: ${error}`);
918
1136
  }
919
1137
  } else if (isDebugMode) {
920
- console.log(`⏭️ Auth layout already exists, skipping: ${authLayoutPath}`);
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(appDirectory, "admin", "layout.tsx");
925
- if (!fs.existsSync(adminLayoutPath)) {
926
- const adminLayoutContent = `import { AdminLayoutWithSidebar } from "@lastbrain/app";
927
- import { menuConfig } from "../../config/menu";
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
- try {
942
- ensureDirectory(path.dirname(adminLayoutPath));
943
- fs.writeFileSync(adminLayoutPath, adminLayoutContent, "utf-8");
944
- if (isDebugMode) {
945
- console.log(
946
- `✅ Generated admin layout with sidebar: ${adminLayoutPath}`
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
- .filter((config) => config.realtime)
962
- .map((config) => config.realtime!);
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!.map((tab) => ({
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
- .filter(
1100
- (config) => config.storage?.buckets && config.storage.buckets.length > 0
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
- .filter(
1236
- (config) => config.storage?.buckets && config.storage.buckets.length > 0
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
- .filter((config) => config.footer && config.footer.length > 0)
1393
- .flatMap((config) => config.footer!);
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
- if (!apisByPath.has(api.path)) {
1481
- apisByPath.set(api.path, []);
1858
+ const apisForPath = apisByPath.get(api.path);
1859
+ if (apisForPath) {
1860
+ apisForPath.push({ moduleConfig, api });
1861
+ return;
1482
1862
  }
1483
- apisByPath.get(api.path)!.push({ moduleConfig, api });
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!");