@qlover/create-app 0.7.14 → 0.8.0

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 (361) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/configs/_common/.gitignore.template +6 -0
  3. package/dist/configs/_common/.prettierignore +17 -5
  4. package/dist/configs/_common/.vscode/settings.json +6 -1
  5. package/dist/index.cjs +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/templates/next-app/.env.template +1 -1
  8. package/dist/templates/next-app/README.en.md +130 -0
  9. package/dist/templates/next-app/README.md +114 -20
  10. package/dist/templates/next-app/config/Identifier/api.ts +5 -5
  11. package/dist/templates/next-app/config/Identifier/common/admint.table.ts +69 -0
  12. package/dist/templates/next-app/config/Identifier/common/common.ts +76 -0
  13. package/dist/templates/next-app/config/Identifier/common/index.ts +3 -0
  14. package/dist/templates/next-app/config/Identifier/{validator.ts → common/validators.ts} +5 -5
  15. package/dist/templates/next-app/config/Identifier/index.ts +2 -12
  16. package/dist/templates/next-app/config/Identifier/pages/index.ts +6 -0
  17. package/dist/templates/next-app/config/Identifier/pages/page.admin.home.ts +27 -0
  18. package/dist/templates/next-app/config/Identifier/pages/page.admin.locales.ts +266 -0
  19. package/dist/templates/next-app/config/Identifier/pages/page.admin.user.ts +293 -0
  20. package/dist/templates/{react-app/config/Identifier → next-app/config/Identifier/pages}/page.home.ts +15 -22
  21. package/dist/templates/next-app/config/Identifier/{page.login.ts → pages/page.login.ts} +28 -34
  22. package/dist/templates/next-app/config/Identifier/{page.register.ts → pages/page.register.ts} +30 -29
  23. package/dist/templates/next-app/config/adminNavs.ts +19 -0
  24. package/dist/templates/next-app/config/common.ts +22 -13
  25. package/dist/templates/next-app/config/i18n/HomeI18n.ts +5 -5
  26. package/dist/templates/next-app/config/i18n/admin18n.ts +61 -19
  27. package/dist/templates/next-app/config/i18n/i18nConfig.ts +2 -0
  28. package/dist/templates/next-app/config/i18n/i18nKeyScheam.ts +36 -0
  29. package/dist/templates/next-app/config/i18n/loginI18n.ts +22 -22
  30. package/dist/templates/next-app/config/i18n/register18n.ts +23 -24
  31. package/dist/templates/next-app/docs/en/api.md +387 -0
  32. package/dist/templates/next-app/docs/en/component.md +544 -0
  33. package/dist/templates/next-app/docs/en/database.md +496 -0
  34. package/dist/templates/next-app/docs/en/development-guide.md +727 -0
  35. package/dist/templates/next-app/docs/en/env.md +563 -0
  36. package/dist/templates/next-app/docs/en/i18n.md +287 -0
  37. package/dist/templates/next-app/docs/en/index.md +165 -0
  38. package/dist/templates/next-app/docs/en/page.md +457 -0
  39. package/dist/templates/next-app/docs/en/project-structure.md +176 -0
  40. package/dist/templates/next-app/docs/en/router.md +427 -0
  41. package/dist/templates/next-app/docs/en/theme.md +532 -0
  42. package/dist/templates/next-app/docs/en/validator.md +478 -0
  43. package/dist/templates/next-app/docs/zh/api.md +387 -0
  44. package/dist/templates/next-app/docs/zh/component.md +544 -0
  45. package/dist/templates/next-app/docs/zh/database.md +496 -0
  46. package/dist/templates/next-app/docs/zh/development-guide.md +727 -0
  47. package/dist/templates/next-app/docs/zh/env.md +563 -0
  48. package/dist/templates/next-app/docs/zh/i18n.md +287 -0
  49. package/dist/templates/next-app/docs/zh/index.md +165 -0
  50. package/dist/templates/next-app/docs/zh/page.md +457 -0
  51. package/dist/templates/next-app/docs/zh/project-structure.md +176 -0
  52. package/dist/templates/next-app/docs/zh/router.md +427 -0
  53. package/dist/templates/next-app/docs/zh/theme.md +532 -0
  54. package/dist/templates/next-app/docs/zh/validator.md +476 -0
  55. package/dist/templates/next-app/make/generateLocales.ts +19 -12
  56. package/dist/templates/next-app/migrations/schema/LocalesSchema.ts +15 -0
  57. package/dist/templates/next-app/migrations/sql/1694244000000.sql +11 -0
  58. package/dist/templates/next-app/package.json +7 -3
  59. package/dist/templates/next-app/public/locales/en.json +172 -207
  60. package/dist/templates/next-app/public/locales/zh.json +172 -207
  61. package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +153 -0
  62. package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +48 -50
  63. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +2 -2
  64. package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +34 -0
  65. package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +40 -0
  66. package/dist/templates/next-app/src/app/api/admin/locales/route.ts +42 -0
  67. package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +32 -0
  68. package/dist/templates/next-app/src/app/api/locales/json/route.ts +44 -0
  69. package/dist/templates/next-app/src/base/cases/AdminPageManager.ts +1 -13
  70. package/dist/templates/next-app/src/base/cases/Datetime.ts +18 -0
  71. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +12 -6
  72. package/dist/templates/next-app/src/base/cases/ResourceState.ts +17 -0
  73. package/dist/templates/next-app/src/base/cases/TranslateI18nInterface.ts +25 -0
  74. package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +200 -0
  75. package/dist/templates/next-app/src/base/port/ZodBuilderInterface.ts +8 -0
  76. package/dist/templates/next-app/src/base/services/AdminLocalesService.ts +20 -0
  77. package/dist/templates/next-app/src/base/services/AdminPageEvent.ts +26 -0
  78. package/dist/templates/next-app/src/base/services/AdminPageScheduler.ts +42 -0
  79. package/dist/templates/next-app/src/base/services/ResourceService.ts +122 -0
  80. package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +104 -0
  81. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +38 -5
  82. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +1 -1
  83. package/dist/templates/next-app/src/i18n/request.ts +30 -1
  84. package/dist/templates/next-app/src/server/PageParams.ts +2 -10
  85. package/dist/templates/next-app/src/server/port/DBBridgeInterface.ts +5 -0
  86. package/dist/templates/next-app/src/server/port/DBTableInterface.ts +2 -0
  87. package/dist/templates/next-app/src/server/port/LocalesRepositoryInterface.ts +43 -0
  88. package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +197 -0
  89. package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +122 -0
  90. package/dist/templates/next-app/src/server/sqlBridges/SupabaseBridge.ts +60 -11
  91. package/dist/templates/next-app/src/server/validators/ExtendedExecutorError.ts +6 -0
  92. package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +131 -0
  93. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +2 -5
  94. package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +32 -16
  95. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/_default.css +2 -1
  96. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/dark.css +28 -29
  97. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/pink.css +2 -1
  98. package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +17 -3
  99. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +5 -4
  100. package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +5 -4
  101. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +3 -2
  102. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +1 -1
  103. package/dist/templates/next-app/src/uikit/components/EditableCell.tsx +118 -0
  104. package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +5 -6
  105. package/dist/templates/next-app/src/uikit/components/ThemeSwitcher.tsx +1 -1
  106. package/dist/templates/next-app/src/uikit/components/With.tsx +2 -2
  107. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportButton.tsx +62 -0
  108. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportEvent.ts +28 -0
  109. package/dist/templates/next-app/src/uikit/components/localesImportButton/import.module.css +6 -0
  110. package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +8 -14
  111. package/dist/templates/next-app/src/uikit/hook/useWarnTranslations.ts +25 -0
  112. package/dist/templates/react-app/.prettierignore +17 -0
  113. package/dist/templates/react-app/README.en.md +71 -54
  114. package/dist/templates/react-app/README.md +35 -18
  115. package/dist/templates/react-app/__tests__/__mocks__/BootstrapTest.ts +14 -0
  116. package/dist/templates/react-app/__tests__/__mocks__/MockAppConfit.ts +1 -1
  117. package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +2 -2
  118. package/dist/templates/react-app/__tests__/__mocks__/MockLogger.ts +1 -1
  119. package/dist/templates/react-app/__tests__/__mocks__/components/TestApp.tsx +45 -0
  120. package/dist/templates/react-app/__tests__/__mocks__/components/TestBootstrapsProvider.tsx +34 -0
  121. package/dist/templates/react-app/__tests__/__mocks__/components/TestRouter.tsx +46 -0
  122. package/dist/templates/react-app/__tests__/__mocks__/components/index.ts +12 -0
  123. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +1 -2
  124. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOC.ts +51 -0
  125. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOCRegister.ts +69 -0
  126. package/dist/templates/react-app/__tests__/setup/index.ts +1 -51
  127. package/dist/templates/react-app/__tests__/setup/setupGlobal.ts +51 -0
  128. package/dist/templates/react-app/__tests__/src/App.structure.test.tsx +115 -0
  129. package/dist/templates/react-app/__tests__/src/base/cases/AppConfig.test.ts +2 -2
  130. package/dist/templates/react-app/__tests__/src/base/cases/AppError.test.ts +1 -1
  131. package/dist/templates/react-app/__tests__/src/base/cases/DialogHandler.test.ts +3 -5
  132. package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +13 -2
  133. package/dist/templates/react-app/__tests__/src/base/cases/InversifyContainer.test.ts +1 -1
  134. package/dist/templates/react-app/__tests__/src/base/cases/PublicAssetsPath.test.ts +1 -1
  135. package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +5 -5
  136. package/dist/templates/react-app/__tests__/src/base/cases/RequestStatusCatcher.test.ts +1 -2
  137. package/dist/templates/react-app/__tests__/src/base/cases/RouterLoader.test.ts +25 -15
  138. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +29 -15
  139. package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +19 -9
  140. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapClient.test.ts +153 -0
  141. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +9 -7
  142. package/dist/templates/react-app/__tests__/src/main.integration.test.tsx +4 -5
  143. package/dist/templates/react-app/__tests__/src/main.test.tsx +4 -4
  144. package/dist/templates/react-app/__tests__/src/uikit/components/BaseHeader.test.tsx +68 -59
  145. package/dist/templates/react-app/config/IOCIdentifier.ts +8 -8
  146. package/dist/templates/react-app/config/Identifier/{common.error.ts → common/common.error.ts} +5 -5
  147. package/dist/templates/react-app/config/Identifier/{common.ts → common/common.ts} +9 -9
  148. package/dist/templates/react-app/config/Identifier/common/index.ts +2 -0
  149. package/dist/templates/react-app/config/Identifier/index.ts +1 -9
  150. package/dist/templates/react-app/config/Identifier/pages/index.ts +8 -0
  151. package/dist/templates/react-app/config/Identifier/{page.about.ts → pages/page.about.ts} +34 -26
  152. package/dist/templates/react-app/config/Identifier/{page.executor.ts → pages/page.executor.ts} +47 -39
  153. package/dist/templates/{next-app/config/Identifier → react-app/config/Identifier/pages}/page.home.ts +24 -23
  154. package/dist/templates/react-app/config/Identifier/pages/page.identifiter.ts +102 -0
  155. package/dist/templates/react-app/config/Identifier/{page.jsonStorage.ts → pages/page.jsonStorage.ts} +18 -11
  156. package/dist/templates/react-app/config/Identifier/{page.login.ts → pages/page.login.ts} +37 -27
  157. package/dist/templates/react-app/config/Identifier/{page.register.ts → pages/page.register.ts} +37 -25
  158. package/dist/templates/react-app/config/Identifier/{page.request.ts → pages/page.request.ts} +34 -44
  159. package/dist/templates/react-app/config/app.router.ts +66 -69
  160. package/dist/templates/react-app/config/i18n/PageI18nInterface.ts +51 -0
  161. package/dist/templates/react-app/config/i18n/aboutI18n.ts +42 -0
  162. package/dist/templates/react-app/config/i18n/executorI18n.ts +51 -0
  163. package/dist/templates/react-app/config/i18n/homeI18n.ts +24 -0
  164. package/dist/templates/react-app/config/i18n/i18nConfig.ts +30 -0
  165. package/dist/templates/react-app/config/i18n/identifiter18n.ts +30 -0
  166. package/dist/templates/react-app/config/i18n/jsonStorage18n.ts +27 -0
  167. package/dist/templates/react-app/config/i18n/login18n.ts +42 -0
  168. package/dist/templates/react-app/config/i18n/notFoundI18n.ts +34 -0
  169. package/dist/templates/react-app/config/i18n/register18n.ts +40 -0
  170. package/dist/templates/react-app/config/i18n/request18n.ts +41 -0
  171. package/dist/templates/react-app/config/theme.ts +14 -4
  172. package/dist/templates/react-app/docs/en/bootstrap.md +1670 -341
  173. package/dist/templates/react-app/docs/en/development-guide.md +1021 -345
  174. package/dist/templates/react-app/docs/en/env.md +1132 -278
  175. package/dist/templates/react-app/docs/en/i18n.md +858 -147
  176. package/dist/templates/react-app/docs/en/index.md +733 -104
  177. package/dist/templates/react-app/docs/en/ioc.md +1228 -287
  178. package/dist/templates/react-app/docs/en/playwright/e2e-tests.md +321 -0
  179. package/dist/templates/react-app/docs/en/playwright/index.md +19 -0
  180. package/dist/templates/react-app/docs/en/playwright/installation-summary.md +332 -0
  181. package/dist/templates/react-app/docs/en/playwright/overview.md +222 -0
  182. package/dist/templates/react-app/docs/en/playwright/quickstart.md +325 -0
  183. package/dist/templates/react-app/docs/en/playwright/reorganization-notes.md +340 -0
  184. package/dist/templates/react-app/docs/en/playwright/setup-complete.md +290 -0
  185. package/dist/templates/react-app/docs/en/playwright/testing-guide.md +565 -0
  186. package/dist/templates/react-app/docs/en/store.md +1194 -184
  187. package/dist/templates/react-app/docs/en/why-no-globals.md +797 -0
  188. package/dist/templates/react-app/docs/zh/bootstrap.md +1670 -341
  189. package/dist/templates/react-app/docs/zh/development-guide.md +1021 -345
  190. package/dist/templates/react-app/docs/zh/env.md +1132 -275
  191. package/dist/templates/react-app/docs/zh/i18n.md +858 -147
  192. package/dist/templates/react-app/docs/zh/index.md +717 -104
  193. package/dist/templates/react-app/docs/zh/ioc.md +1229 -287
  194. package/dist/templates/react-app/docs/zh/playwright/e2e-tests.md +321 -0
  195. package/dist/templates/react-app/docs/zh/playwright/index.md +19 -0
  196. package/dist/templates/react-app/docs/zh/playwright/installation-summary.md +332 -0
  197. package/dist/templates/react-app/docs/zh/playwright/overview.md +222 -0
  198. package/dist/templates/react-app/docs/zh/playwright/quickstart.md +325 -0
  199. package/dist/templates/react-app/docs/zh/playwright/reorganization-notes.md +340 -0
  200. package/dist/templates/react-app/docs/zh/playwright/setup-complete.md +290 -0
  201. package/dist/templates/react-app/docs/zh/playwright/testing-guide.md +565 -0
  202. package/dist/templates/react-app/docs/zh/store.md +1192 -184
  203. package/dist/templates/react-app/docs/zh/why-no-globals.md +797 -0
  204. package/dist/templates/react-app/e2e/App.spec.ts +319 -0
  205. package/dist/templates/react-app/e2e/fixtures/base.fixture.ts +40 -0
  206. package/dist/templates/react-app/e2e/main.spec.ts +20 -0
  207. package/dist/templates/react-app/e2e/utils/test-helpers.ts +19 -0
  208. package/dist/templates/react-app/eslint.config.mjs +247 -0
  209. package/dist/templates/react-app/makes/eslint-utils.mjs +195 -0
  210. package/dist/templates/react-app/makes/generateTs2LocalesOptions.ts +26 -0
  211. package/dist/templates/react-app/package.json +31 -3
  212. package/dist/templates/react-app/playwright.config.ts +79 -0
  213. package/dist/templates/react-app/public/locales/en/common.json +190 -179
  214. package/dist/templates/react-app/public/locales/zh/common.json +190 -179
  215. package/dist/templates/react-app/src/App.tsx +15 -42
  216. package/dist/templates/react-app/src/base/apis/AiApi.ts +5 -5
  217. package/dist/templates/react-app/src/base/apis/feApi/FeApi.ts +1 -1
  218. package/dist/templates/react-app/src/base/apis/feApi/FeApiAdapter.ts +1 -1
  219. package/dist/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +8 -8
  220. package/dist/templates/react-app/src/base/apis/feApi/FeApiType.ts +1 -1
  221. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +6 -6
  222. package/dist/templates/react-app/src/base/apis/userApi/UserApiAdapter.ts +1 -1
  223. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +12 -14
  224. package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +1 -1
  225. package/dist/templates/react-app/src/base/cases/DialogHandler.ts +5 -2
  226. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +3 -3
  227. package/dist/templates/react-app/src/base/cases/InversifyContainer.ts +3 -3
  228. package/dist/templates/react-app/src/base/cases/RequestLanguages.ts +2 -2
  229. package/dist/templates/react-app/src/base/cases/RequestLogger.ts +4 -4
  230. package/dist/templates/react-app/src/base/cases/RequestStatusCatcher.ts +1 -1
  231. package/dist/templates/react-app/src/base/cases/ResourceState.ts +23 -0
  232. package/dist/templates/react-app/src/base/cases/RouterLoader.ts +4 -4
  233. package/dist/templates/react-app/src/base/cases/TranslateI18nInterface.ts +26 -0
  234. package/dist/templates/react-app/src/base/port/ExecutorPageBridgeInterface.ts +2 -3
  235. package/dist/templates/react-app/src/base/port/I18nServiceInterface.ts +1 -1
  236. package/dist/templates/react-app/src/base/port/IOCInterface.ts +36 -0
  237. package/dist/templates/react-app/src/base/port/JSONStoragePageBridgeInterface.ts +2 -1
  238. package/dist/templates/react-app/src/base/port/ProcesserExecutorInterface.ts +1 -1
  239. package/dist/templates/react-app/src/base/port/RequestPageBridgeInterface.ts +2 -2
  240. package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +9 -5
  241. package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +1 -1
  242. package/dist/templates/react-app/src/base/services/I18nService.ts +29 -29
  243. package/dist/templates/react-app/src/base/services/IdentifierService.ts +143 -0
  244. package/dist/templates/react-app/src/base/services/ProcesserExecutor.ts +3 -3
  245. package/dist/templates/react-app/src/base/services/RouteService.ts +27 -8
  246. package/dist/templates/react-app/src/base/services/UserService.ts +8 -8
  247. package/dist/templates/react-app/src/base/types/Page.ts +14 -2
  248. package/dist/templates/react-app/src/base/types/global.d.ts +1 -1
  249. package/dist/templates/react-app/src/core/IOC.ts +5 -46
  250. package/dist/templates/react-app/src/core/bootstraps/{BootstrapApp.ts → BootstrapClient.ts} +44 -17
  251. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +14 -7
  252. package/dist/templates/react-app/src/core/bootstraps/IocIdentifierTest.ts +1 -1
  253. package/dist/templates/react-app/src/core/bootstraps/PrintBootstrap.ts +1 -1
  254. package/dist/templates/react-app/src/core/clientIoc/ClientIOC.ts +40 -0
  255. package/dist/templates/react-app/src/core/{IocRegisterImpl.ts → clientIoc/ClientIOCRegister.ts} +35 -24
  256. package/dist/templates/react-app/src/core/globals.ts +9 -9
  257. package/dist/templates/react-app/src/main.tsx +4 -4
  258. package/dist/templates/react-app/src/pages/404.tsx +6 -3
  259. package/dist/templates/react-app/src/pages/500.tsx +5 -2
  260. package/dist/templates/react-app/src/pages/NoRouteFound.tsx +5 -0
  261. package/dist/templates/react-app/src/pages/auth/Layout.tsx +9 -6
  262. package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +46 -56
  263. package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +46 -58
  264. package/dist/templates/react-app/src/pages/base/AboutPage.tsx +35 -40
  265. package/dist/templates/react-app/src/pages/base/ExecutorPage.tsx +51 -51
  266. package/dist/templates/react-app/src/pages/base/HomePage.tsx +14 -15
  267. package/dist/templates/react-app/src/pages/base/IdentifierPage.tsx +70 -11
  268. package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +24 -25
  269. package/dist/templates/react-app/src/pages/base/Layout.tsx +2 -2
  270. package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +3 -2
  271. package/dist/templates/react-app/src/pages/base/RequestPage.tsx +41 -59
  272. package/dist/templates/react-app/src/styles/css/antd-themes/{_default.css → _common/_default.css} +85 -0
  273. package/dist/templates/react-app/src/styles/css/antd-themes/{dark.css → _common/dark.css} +99 -0
  274. package/dist/templates/react-app/src/styles/css/antd-themes/_common/index.css +3 -0
  275. package/dist/templates/react-app/src/styles/css/antd-themes/{pink.css → _common/pink.css} +86 -0
  276. package/dist/templates/react-app/src/styles/css/antd-themes/index.css +4 -3
  277. package/dist/templates/react-app/src/styles/css/antd-themes/menu/_default.css +108 -0
  278. package/dist/templates/react-app/src/styles/css/antd-themes/menu/dark.css +67 -0
  279. package/dist/templates/react-app/src/styles/css/antd-themes/menu/index.css +3 -0
  280. package/dist/templates/react-app/src/styles/css/antd-themes/menu/pink.css +67 -0
  281. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/_default.css +34 -0
  282. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/dark.css +31 -0
  283. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/index.css +3 -0
  284. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/pink.css +36 -0
  285. package/dist/templates/react-app/src/styles/css/antd-themes/table/_default.css +44 -0
  286. package/dist/templates/react-app/src/styles/css/antd-themes/table/dark.css +43 -0
  287. package/dist/templates/react-app/src/styles/css/antd-themes/table/index.css +3 -0
  288. package/dist/templates/react-app/src/styles/css/antd-themes/table/pink.css +43 -0
  289. package/dist/templates/react-app/src/styles/css/page.css +4 -3
  290. package/dist/templates/react-app/src/styles/css/themes/_default.css +1 -0
  291. package/dist/templates/react-app/src/styles/css/themes/dark.css +1 -0
  292. package/dist/templates/react-app/src/styles/css/themes/pink.css +1 -0
  293. package/dist/templates/react-app/src/styles/css/zIndex.css +1 -1
  294. package/dist/templates/react-app/src/uikit/bridges/ExecutorPageBridge.ts +3 -3
  295. package/dist/templates/react-app/src/uikit/bridges/JSONStoragePageBridge.ts +2 -2
  296. package/dist/templates/react-app/src/uikit/bridges/NavigateBridge.ts +1 -1
  297. package/dist/templates/react-app/src/uikit/bridges/RequestPageBridge.ts +3 -3
  298. package/dist/templates/react-app/src/uikit/components/AppRouterProvider.tsx +35 -0
  299. package/dist/templates/react-app/src/uikit/components/BaseHeader.tsx +15 -11
  300. package/dist/templates/react-app/src/uikit/components/BaseRouteProvider.tsx +14 -11
  301. package/dist/templates/react-app/src/uikit/components/BaseRouteSeo.tsx +18 -0
  302. package/dist/templates/react-app/src/uikit/components/BootstrapsProvider.tsx +13 -0
  303. package/dist/templates/react-app/src/uikit/components/ClientSeo.tsx +62 -0
  304. package/dist/templates/react-app/src/uikit/components/ComboProvider.tsx +38 -0
  305. package/dist/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +48 -27
  306. package/dist/templates/react-app/src/uikit/components/Loading.tsx +4 -2
  307. package/dist/templates/react-app/src/uikit/components/LocaleLink.tsx +4 -5
  308. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +34 -11
  309. package/dist/templates/react-app/src/uikit/components/ProcessExecutorProvider.tsx +9 -5
  310. package/dist/templates/react-app/src/uikit/components/RouterRenderComponent.tsx +6 -3
  311. package/dist/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +97 -40
  312. package/dist/templates/react-app/src/uikit/components/UserAuthProvider.tsx +5 -5
  313. package/dist/templates/react-app/src/uikit/components/With.tsx +17 -0
  314. package/dist/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +17 -11
  315. package/dist/templates/react-app/src/uikit/contexts/IOCContext.ts +13 -0
  316. package/dist/templates/react-app/src/uikit/hooks/useAppTranslation.ts +26 -0
  317. package/dist/templates/react-app/src/uikit/hooks/useI18nGuard.ts +8 -11
  318. package/dist/templates/react-app/src/uikit/hooks/useI18nInterface.ts +25 -0
  319. package/dist/templates/react-app/src/uikit/hooks/useIOC.ts +35 -0
  320. package/dist/templates/react-app/src/uikit/hooks/useNavigateBridge.ts +3 -3
  321. package/dist/templates/react-app/src/uikit/hooks/useStrictEffect.ts +0 -1
  322. package/dist/templates/react-app/tsconfig.e2e.json +21 -0
  323. package/dist/templates/react-app/tsconfig.json +8 -1
  324. package/dist/templates/react-app/tsconfig.node.json +1 -1
  325. package/dist/templates/react-app/tsconfig.test.json +3 -1
  326. package/dist/templates/react-app/vite.config.ts +50 -34
  327. package/package.json +2 -1
  328. package/dist/configs/react-app/eslint.config.js +0 -94
  329. package/dist/templates/next-app/config/Identifier/common.error.ts +0 -41
  330. package/dist/templates/next-app/config/Identifier/common.ts +0 -69
  331. package/dist/templates/next-app/config/Identifier/page.about.ts +0 -181
  332. package/dist/templates/next-app/config/Identifier/page.admin.ts +0 -48
  333. package/dist/templates/next-app/config/Identifier/page.executor.ts +0 -272
  334. package/dist/templates/next-app/config/Identifier/page.identifiter.ts +0 -39
  335. package/dist/templates/next-app/config/Identifier/page.jsonStorage.ts +0 -72
  336. package/dist/templates/next-app/config/Identifier/page.request.ts +0 -182
  337. package/dist/templates/next-app/docs/env.md +0 -94
  338. package/dist/templates/next-app/src/base/cases/ChatAction.ts +0 -21
  339. package/dist/templates/next-app/src/base/cases/FocusBarAction.ts +0 -36
  340. package/dist/templates/next-app/src/base/cases/RequestState.ts +0 -20
  341. package/dist/templates/next-app/src/base/port/AdminPageInterface.ts +0 -85
  342. package/dist/templates/next-app/src/base/port/AsyncStateInterface.ts +0 -7
  343. package/dist/templates/next-app/src/base/services/AdminUserService.ts +0 -45
  344. package/dist/templates/next-app/src/uikit/components/ChatRoot.tsx +0 -17
  345. package/dist/templates/next-app/src/uikit/components/chat/ChatActionInterface.ts +0 -30
  346. package/dist/templates/next-app/src/uikit/components/chat/ChatFocusBar.tsx +0 -65
  347. package/dist/templates/next-app/src/uikit/components/chat/ChatMessages.tsx +0 -59
  348. package/dist/templates/next-app/src/uikit/components/chat/ChatWrap.tsx +0 -28
  349. package/dist/templates/next-app/src/uikit/components/chat/FocusBarActionInterface.ts +0 -19
  350. package/dist/templates/next-app/src/uikit/hook/useMountedClient.ts +0 -17
  351. package/dist/templates/next-app/src/uikit/hook/useStore.ts +0 -15
  352. package/dist/templates/react-app/__tests__/__mocks__/I18nService.ts +0 -13
  353. package/dist/templates/react-app/__tests__/src/App.test.tsx +0 -139
  354. package/dist/templates/react-app/config/Identifier/page.identifiter.ts +0 -39
  355. package/dist/templates/react-app/config/i18n.ts +0 -15
  356. package/dist/templates/react-app/docs/en/project-structure.md +0 -434
  357. package/dist/templates/react-app/docs/zh/project-structure.md +0 -434
  358. package/dist/templates/react-app/src/base/cases/RequestState.ts +0 -20
  359. package/dist/templates/react-app/src/base/port/AsyncStateInterface.ts +0 -7
  360. package/dist/templates/react-app/src/uikit/hooks/useDocumentTitle.ts +0 -15
  361. package/dist/templates/react-app/src/uikit/hooks/useStore.ts +0 -15
@@ -0,0 +1,797 @@
1
+ # Why Prohibit Direct Use of Browser Global Variables?
2
+
3
+ ## 📋 Table of Contents
4
+
5
+ - [Core Philosophy](#-core-philosophy)
6
+ - [Prohibited Global Variables](#-prohibited-global-variables)
7
+ - [Allowed Locations](#-allowed-locations)
8
+ - [Why Do This](#-why-do-this)
9
+ - [Practical Application Scenarios](#-practical-application-scenarios)
10
+ - [Best Practices](#-best-practices)
11
+ - [FAQ](#-faq)
12
+
13
+ ---
14
+
15
+ ## 🎯 Core Philosophy
16
+
17
+ In our project, we prohibit direct use of browser global variables (like `window`, `document`, `localStorage`, etc.) in business code. Instead, they must be **injected through the application entry point or encapsulation layer**.
18
+
19
+ ### Simply Put:
20
+
21
+ ```typescript
22
+ // ❌ Not allowed: Direct use in business components
23
+ function MyComponent() {
24
+ const width = window.innerWidth; // ESLint error!
25
+ return <div>Width: {width}</div>;
26
+ }
27
+
28
+ // ✅ Recommended: Import from encapsulation layer
29
+ import { localStorage } from '@/core/globals';
30
+
31
+ function MyComponent() {
32
+ const token = localStorage.getItem('token'); // Correct!
33
+ return <div>Token: {token}</div>;
34
+ }
35
+ ```
36
+
37
+ ---
38
+
39
+ ## 🚫 Prohibited Global Variables
40
+
41
+ The following global variables are prohibited from direct use in `src/**/*.{ts,tsx,js,jsx}`:
42
+
43
+ - `window` - Browser window object
44
+ - `document` - DOM document object
45
+ - `localStorage` - Local storage
46
+ - `sessionStorage` - Session storage
47
+ - `navigator` - Browser information
48
+ - `location` - URL information
49
+ - `history` - Browser history
50
+
51
+ ---
52
+
53
+ ## ✅ Allowed Locations
54
+
55
+ ### 1. **Application Entry** (`src/main.tsx`)
56
+
57
+ This is the only place allowed to directly access the browser environment, as it's the application's starting point:
58
+
59
+ ```typescript
60
+ // !only this file use `window`, `document` ...global variables
61
+ import 'reflect-metadata';
62
+ import { StrictMode } from 'react';
63
+ import { createRoot } from 'react-dom/client';
64
+ import App from './App.tsx';
65
+ import { BootstrapClient } from './core/bootstraps/BootstrapClient';
66
+ import { clientIOC } from './core/clientIoc/ClientIOC.ts';
67
+
68
+ BootstrapClient.main({
69
+ root: window, // ✅ Direct use of window
70
+ bootHref: window.location.href, // ✅ Direct use of location
71
+ ioc: clientIOC
72
+ });
73
+
74
+ createRoot(document.getElementById('root')!).render( // ✅ Direct use of document
75
+ <StrictMode>
76
+ <App />
77
+ </StrictMode>
78
+ );
79
+ ```
80
+
81
+ **Why?** Because `main.tsx` is responsible for injecting the browser environment into the application - it's the starting point of "dependency injection".
82
+
83
+ ### 2. **Global Variable Encapsulation Layer** (`src/core/globals.ts`)
84
+
85
+ This is where global variables are uniformly encapsulated and managed:
86
+
87
+ ```typescript
88
+ /**
89
+ * Override localStorage to use the global local storage
90
+ */
91
+ export const localStorage = new SyncStorage(new ObjectStorage(), [
92
+ JSON,
93
+ new Base64Serializer(),
94
+ window.localStorage as unknown as SyncStorageInterface<string> // ✅ Encapsulate localStorage
95
+ ]);
96
+
97
+ export const localStorageEncrypt = localStorage;
98
+
99
+ export const cookieStorage = new CookieStorage();
100
+ ```
101
+
102
+ **Why?** This is the encapsulation layer, responsible for wrapping raw browser APIs into unified, type-safe interfaces.
103
+
104
+ ### 3. **Special Infrastructure Layer**
105
+
106
+ Some infrastructure code (like IOC container initialization) may need to access global variables, but should:
107
+
108
+ #### ⚠️ Case A: Not recommended but acceptable
109
+
110
+ Direct use in `ClientIOC.ts`:
111
+
112
+ ```typescript
113
+ const register = new ClientIOCRegister({
114
+ pathname: window.location.pathname, // ⚠️ Special case, acceptable
115
+ appConfig: appConfig
116
+ });
117
+ ```
118
+
119
+ **Note:** IOC container initialization needs `pathname`, this is acceptable but not best practice.
120
+
121
+ #### ✅ Case B: Better approach (recommended)
122
+
123
+ Pass through `main.tsx`:
124
+
125
+ ```typescript
126
+ // main.tsx
127
+ BootstrapClient.main({
128
+ root: window,
129
+ bootHref: window.location.href, // Get at entry
130
+ pathname: window.location.pathname, // Pass via parameter
131
+ ioc: clientIOC
132
+ });
133
+
134
+ // ClientIOC.ts
135
+ create(pathname: string) { // Receive parameter instead of direct access
136
+ const register = new ClientIOCRegister({
137
+ pathname: pathname, // ✅ Use passed parameter
138
+ appConfig: appConfig
139
+ });
140
+ }
141
+ ```
142
+
143
+ ---
144
+
145
+ ## 🤔 Why Do This?
146
+
147
+ ### 1. **Test-Friendly** 🧪
148
+
149
+ Direct use of global variables makes testing **extremely difficult or impossible**.
150
+
151
+ #### ❌ Problem Example: Hard-to-test Component
152
+
153
+ ```typescript
154
+ // UserProfile.tsx - Direct use of global variables
155
+ function UserProfile() {
156
+ const [user, setUser] = useState(null);
157
+
158
+ useEffect(() => {
159
+ // Direct use of fetch
160
+ fetch('/api/user')
161
+ .then(res => res.json())
162
+ .then(data => {
163
+ // Direct use of localStorage
164
+ localStorage.setItem('lastUser', data.id);
165
+ setUser(data);
166
+ });
167
+ }, []);
168
+
169
+ return <div>{user?.name}</div>;
170
+ }
171
+
172
+ // ❌ Test code - Almost impossible to test
173
+ describe('UserProfile', () => {
174
+ it('should load and display user', async () => {
175
+ // Problem 1: How to mock fetch? Need polyfill or global mock
176
+ global.fetch = jest.fn();
177
+
178
+ // Problem 2: How to mock localStorage? Need manual implementation
179
+ const mockLocalStorage = {
180
+ setItem: jest.fn()
181
+ };
182
+ global.localStorage = mockLocalStorage as any;
183
+
184
+ // Problem 3: Need to clean up global state, otherwise affects other tests
185
+ // Problem 4: Tests may interfere with each other
186
+
187
+ render(<UserProfile />);
188
+ // Hard to verify behavior...
189
+ });
190
+ });
191
+ ```
192
+
193
+ **Problems:**
194
+
195
+ - 😰 Need to mock global variables (fetch, localStorage)
196
+ - 😰 Tests may interfere with each other
197
+ - 😰 Hard to test error scenarios
198
+ - 😰 Test code full of tricks and hacks
199
+ - 😰 May not run at all in Node.js environment
200
+
201
+ #### ✅ Solution 1: Import from encapsulation layer
202
+
203
+ ```typescript
204
+ // UserProfile.tsx - Import from encapsulation layer
205
+ import { localStorage } from '@/core/globals';
206
+
207
+ function getUser() {
208
+ return fetch('/api/user').then((res) => res.json());
209
+ }
210
+
211
+ // ✅ Test code - Easier to test
212
+ jest.mock('@/core/globals', () => ({
213
+ localStorage: {
214
+ setItem: jest.fn()
215
+ }
216
+ }));
217
+
218
+ describe('UserProfile', () => {
219
+ it('should save user to localStorage', () => {
220
+ // Relatively easy to mock, but still need to handle fetch
221
+ });
222
+ });
223
+ ```
224
+
225
+ #### ⭐ Solution 2: Use IOC Container (Best)
226
+
227
+ ```typescript
228
+ // UserProfile.tsx - Use IOC container
229
+ import { useIoc } from '@/uikit/hooks/useIoc';
230
+
231
+ function UserProfile() {
232
+ const userService = useIoc('UserService');
233
+ const [user, setUser] = useState(null);
234
+
235
+ useEffect(() => {
236
+ userService.getCurrentUser().then(setUser);
237
+ }, []);
238
+
239
+ return <div>{user?.name}</div>;
240
+ }
241
+
242
+ // ✅✅ Test code - Very easy!
243
+ import { render, screen, waitFor } from '@testing-library/react';
244
+ import { IocProvider } from '@/contexts/IocContext';
245
+
246
+ describe('UserProfile', () => {
247
+ it('should load and display user', async () => {
248
+ // ✅ Only need to mock service, no need to mock global variables
249
+ const mockUserService = {
250
+ getCurrentUser: jest.fn().mockResolvedValue({
251
+ id: '1',
252
+ name: 'John Doe'
253
+ })
254
+ };
255
+
256
+ const mockIoc = (serviceName: string) => {
257
+ if (serviceName === 'UserService') return mockUserService;
258
+ };
259
+
260
+ render(
261
+ <IocProvider value={mockIoc}>
262
+ <UserProfile />
263
+ </IocProvider>
264
+ );
265
+
266
+ // ✅ Clear assertions
267
+ await waitFor(() => {
268
+ expect(screen.getByText('John Doe')).toBeInTheDocument();
269
+ });
270
+
271
+ // ✅ Verify service call
272
+ expect(mockUserService.getCurrentUser).toHaveBeenCalledTimes(1);
273
+ });
274
+
275
+ it('should handle error', async () => {
276
+ // ✅ Easy to test error scenarios
277
+ const mockUserService = {
278
+ getCurrentUser: jest.fn().mockRejectedValue(new Error('Network error'))
279
+ };
280
+
281
+ // Test error handling...
282
+ });
283
+
284
+ it('should handle loading state', () => {
285
+ // ✅ Easy to test loading state
286
+ const mockUserService = {
287
+ getCurrentUser: jest.fn().mockReturnValue(new Promise(() => {})) // Never resolves
288
+ };
289
+
290
+ // Test loading state...
291
+ });
292
+ });
293
+ ```
294
+
295
+ #### Comparison Summary
296
+
297
+ | Test Scenario | Direct Global Variables | Encapsulation Layer | IOC Container |
298
+ | --------------------- | ----------------------- | ------------------- | ------------- |
299
+ | Mock Complexity | 😰😰😰 Very hard | 😐 Medium | 😊😊😊 Simple |
300
+ | Test Isolation | ❌ Poor | ⚠️ Fair | ✅ Good |
301
+ | Test Error Scenarios | ❌ Difficult | ⚠️ Possible | ✅ Easy |
302
+ | Test Code Readability | ❌ Poor | ⚠️ Fair | ✅ Good |
303
+ | Run in Node.js | ❌ Difficult | ✅ Yes | ✅ Yes |
304
+
305
+ **Key Advantages:**
306
+
307
+ - ✅ **Simple mocking**: Only need to mock one service object, no need to mock global environment
308
+ - ✅ **Test isolation**: Each test has independent mocks, no interference
309
+ - ✅ **Easy error testing**: Easily simulate various error scenarios (network errors, timeout, permission errors, etc.)
310
+ - ✅ **Fast execution**: Don't need real browser environment, tests run faster
311
+ - ✅ **Clear code**: Test code is simple and intuitive, easy to maintain
312
+
313
+ ### 2. **SSR/Multi-environment Compatibility** 🌐
314
+
315
+ If your application needs to support server-side rendering (like Next.js), direct use of global variables will cause errors:
316
+
317
+ ```typescript
318
+ // ❌ Will error in SSR
319
+ function MyComponent() {
320
+ const width = window.innerWidth; // ReferenceError: window is not defined
321
+ return <div>{width}</div>;
322
+ }
323
+
324
+ // ✅ Safe approach
325
+ import { getWindow } from '@/core/globals';
326
+
327
+ function MyComponent() {
328
+ const win = getWindow(); // Encapsulation layer can handle SSR cases
329
+ const width = win ? win.innerWidth : 0;
330
+ return <div>{width}</div>;
331
+ }
332
+ ```
333
+
334
+ ### 3. **Type Safety and Error Handling** 🛡️
335
+
336
+ Encapsulation layer can provide better types and error handling:
337
+
338
+ ```typescript
339
+ // src/core/globals.ts
340
+ export const localStorage = new SyncStorage(/* ... */); // Has complete type definitions
341
+
342
+ // Business code
343
+ import { localStorage } from '@/core/globals';
344
+
345
+ // ✅ Has complete type hints and error handling
346
+ localStorage.setItem('key', value); // TypeScript will check types
347
+ ```
348
+
349
+ ### 4. **Code Traceability** 🔍
350
+
351
+ With ESLint rules, we can:
352
+
353
+ - **See at a glance** which code depends on browser environment
354
+ - **Easily find** all places using browser APIs (search `from '@/core/globals'`)
355
+ - **Convenient refactoring** - uniformly modify all browser API calls
356
+
357
+ ```typescript
358
+ // Want to know where localStorage is used?
359
+ // Search: import { localStorage } from '@/core/globals'
360
+ // Instead of searching "localStorage" in the entire project (will have many false positives)
361
+ ```
362
+
363
+ ### 5. **Unified Degradation and Polyfill** 🔄
364
+
365
+ Can handle compatibility and degradation uniformly in encapsulation layer:
366
+
367
+ ```typescript
368
+ // src/core/globals.ts
369
+ export const localStorage = (() => {
370
+ try {
371
+ const storage = window.localStorage;
372
+ // Test if available
373
+ storage.setItem('__test__', '1');
374
+ storage.removeItem('__test__');
375
+ return storage;
376
+ } catch {
377
+ // Degrade to memory storage (like privacy mode)
378
+ console.warn('localStorage unavailable, using memory storage');
379
+ return new MemoryStorage();
380
+ }
381
+ })();
382
+ ```
383
+
384
+ ### 6. **Prevent Accidental Coupling** 🚫
385
+
386
+ Forces developers to think:
387
+
388
+ - Does this code really need to depend on browser environment?
389
+ - Can it be written as a pure function?
390
+ - Can it be passed through parameters instead of direct access?
391
+
392
+ ```typescript
393
+ // ❌ Tightly coupled to browser environment
394
+ function isDesktop() {
395
+ return window.innerWidth > 768;
396
+ }
397
+
398
+ // ✅ Decoupled: Pass through parameters
399
+ function isDesktop(width: number) {
400
+ return width > 768;
401
+ }
402
+
403
+ // Pass at call site
404
+ const desktop = isDesktop(window.innerWidth);
405
+ ```
406
+
407
+ ---
408
+
409
+ ## 💡 Practical Application Scenarios
410
+
411
+ ### Scenario 1: Need to operate localStorage
412
+
413
+ ```typescript
414
+ // ❌ Wrong approach: Direct use of browser API
415
+ function saveToken(token: string) {
416
+ localStorage.setItem('token', token); // ESLint error!
417
+ }
418
+
419
+ // ✅ Correct approach 1: Import encapsulated storage from globals
420
+ import { localStorage } from '@/core/globals';
421
+
422
+ function saveToken(token: string) {
423
+ localStorage.setItem('token', token); // Use encapsulated localStorage
424
+ }
425
+
426
+ // ✅ Correct approach 2: Get service through IOC container (recommended)
427
+ import { useIoc } from '@/uikit/hooks/useIoc';
428
+
429
+ function useAuth() {
430
+ const authService = useIoc('AuthService'); // Get service from IOC container
431
+
432
+ const saveToken = (token: string) => {
433
+ authService.setToken(token); // Service internally encapsulates storage operations
434
+ };
435
+
436
+ return { saveToken };
437
+ }
438
+
439
+ // Use in component
440
+ function LoginComponent() {
441
+ const { saveToken } = useAuth();
442
+
443
+ const handleLogin = async () => {
444
+ const token = await login();
445
+ saveToken(token); // Don't need to care if underlying uses localStorage or other storage
446
+ };
447
+ }
448
+ ```
449
+
450
+ **Why is IOC approach better?**
451
+
452
+ - Service layer already encapsulates all storage logic
453
+ - Business code doesn't need to care about storage implementation details
454
+ - Easy to switch storage methods (localStorage → IndexedDB → server)
455
+ - Service can contain more business logic (encryption, validation, expiration handling, etc.)
456
+
457
+ ### Scenario 2: Need to get current path
458
+
459
+ ```typescript
460
+ // ❌ Wrong approach: Direct access in component
461
+ function MyComponent() {
462
+ const path = window.location.pathname; // ESLint error!
463
+ // ...
464
+ }
465
+
466
+ // ✅ Correct approach 1: Use React Router
467
+ import { useLocation } from 'react-router-dom';
468
+
469
+ function MyComponent() {
470
+ const location = useLocation();
471
+ const path = location.pathname; // Through Router-provided hook
472
+ // ...
473
+ }
474
+
475
+ // ✅ Correct approach 2: Get router service through IOC container
476
+ import { useIoc } from '@/uikit/hooks/useIoc';
477
+
478
+ function MyComponent() {
479
+ const routerService = useIoc('RouterService'); // Get router service from IOC
480
+ const path = routerService.getCurrentPath(); // Get path through service
481
+
482
+ // Router service can also provide more features
483
+ const navigate = (path: string) => {
484
+ routerService.navigate(path); // Unified routing navigation
485
+ };
486
+ }
487
+ ```
488
+
489
+ ### Scenario 3: Need to make HTTP request
490
+
491
+ ```typescript
492
+ // ❌ Wrong approach: Direct use of fetch
493
+ async function getUserInfo(id: string) {
494
+ const response = await fetch(`/api/users/${id}`); // Direct use of global fetch
495
+ return response.json();
496
+ }
497
+
498
+ // ✅ Correct approach: Get HTTP service through IOC container
499
+ import { useIoc } from '@/uikit/hooks/useIoc';
500
+
501
+ function useUserService() {
502
+ const httpService = useIoc('HttpService'); // Get HTTP service from IOC
503
+
504
+ const getUserInfo = async (id: string) => {
505
+ // HTTP service already encapsulates:
506
+ // - Unified error handling
507
+ // - Request interceptors (add token)
508
+ // - Response interceptors (handle error codes)
509
+ // - Request cancellation
510
+ // - Timeout control
511
+ return httpService.get(`/users/${id}`);
512
+ };
513
+
514
+ return { getUserInfo };
515
+ }
516
+
517
+ // Use in component
518
+ function UserProfile({ userId }: { userId: string }) {
519
+ const { getUserInfo } = useUserService();
520
+ const [user, setUser] = useState(null);
521
+
522
+ useEffect(() => {
523
+ getUserInfo(userId).then(setUser);
524
+ }, [userId]);
525
+
526
+ return <div>{user?.name}</div>;
527
+ }
528
+ ```
529
+
530
+ ### Scenario 4: Need internationalization translation
531
+
532
+ ```typescript
533
+ // ❌ Wrong approach: Direct dependency on global i18n instance
534
+ import i18n from 'i18next';
535
+
536
+ function MyComponent() {
537
+ const text = i18n.t('common.welcome'); // Direct dependency on global instance
538
+ return <div>{text}</div>;
539
+ }
540
+
541
+ // ✅ Correct approach: Get I18n service through IOC container
542
+ import { useIoc } from '@/uikit/hooks/useIoc';
543
+
544
+ function MyComponent() {
545
+ const i18nService = useIoc('I18nService'); // Get service from IOC
546
+ const text = i18nService.t('common.welcome'); // Translate through service
547
+
548
+ // I18n service provides more features
549
+ const changeLanguage = (lang: string) => {
550
+ i18nService.changeLanguage(lang);
551
+ };
552
+
553
+ return (
554
+ <div>
555
+ {text}
556
+ <button onClick={() => changeLanguage('en')}>English</button>
557
+ </div>
558
+ );
559
+ }
560
+ ```
561
+
562
+ ### Scenario 5: Need to get window width
563
+
564
+ ```typescript
565
+ // ❌ Wrong approach
566
+ function useWindowSize() {
567
+ const [size, setSize] = useState(window.innerWidth); // ESLint error!
568
+ // ...
569
+ }
570
+
571
+ // ✅ Correct approach 1: Import from globals
572
+ import { window } from '@/core/globals';
573
+
574
+ function useWindowSize() {
575
+ const [size, setSize] = useState(window?.innerWidth || 0);
576
+ // ...
577
+ }
578
+
579
+ // ✅ Correct approach 2: Get Window service through IOC container (best)
580
+ import { useIoc } from '@/uikit/hooks/useIoc';
581
+
582
+ function useWindowSize() {
583
+ const windowService = useIoc('WindowService');
584
+ const [size, setSize] = useState(windowService.getWidth());
585
+
586
+ useEffect(() => {
587
+ const unsubscribe = windowService.onResize((newSize) => {
588
+ setSize(newSize.width);
589
+ });
590
+
591
+ return unsubscribe; // Service internally manages event listeners
592
+ }, []);
593
+
594
+ return size;
595
+ }
596
+ ```
597
+
598
+ ---
599
+
600
+ ## 📖 Best Practices
601
+
602
+ ### 1. **Prioritize Using IOC Container to Get Services (Recommended)** ⭐
603
+
604
+ ```typescript
605
+ // ✅ Best practice: Get services through IOC container
606
+ import { useIoc } from '@/uikit/hooks/useIoc';
607
+
608
+ function MyComponent() {
609
+ const authService = useIoc('AuthService');
610
+ const i18nService = useIoc('I18nService');
611
+ const httpService = useIoc('HttpService');
612
+
613
+ // Business logic...
614
+ }
615
+ ```
616
+
617
+ **Why?**
618
+
619
+ - Services already encapsulate all underlying dependencies (including global variables)
620
+ - Easy to test (can mock entire service)
621
+ - Business code doesn't need to care about implementation details
622
+ - Unified dependency management
623
+
624
+ ### 2. **Inject Dependencies at Application Entry**
625
+
626
+ ```typescript
627
+ // main.tsx
628
+ BootstrapClient.main({
629
+ root: window,
630
+ bootHref: window.location.href,
631
+ ioc: clientIOC,
632
+ // Other needed browser information
633
+ initialWindowSize: {
634
+ width: window.innerWidth,
635
+ height: window.innerHeight
636
+ }
637
+ });
638
+ ```
639
+
640
+ ### 3. **Prioritize Using React Ecosystem Solutions**
641
+
642
+ - Use `react-router-dom` instead of directly accessing `location`
643
+ - Use CSS media queries or `useMediaQuery` instead of reading `window.innerWidth`
644
+ - Use React's event system instead of `document.addEventListener`
645
+
646
+ ### 4. **Second Choice: Access Through Encapsulation Layer**
647
+
648
+ If there's no corresponding service, can import from `@/core/globals`:
649
+
650
+ ```typescript
651
+ // src/core/globals.ts
652
+ export const getDocument = () => {
653
+ if (typeof document === 'undefined') {
654
+ throw new Error('document is not available in SSR');
655
+ }
656
+ return document;
657
+ };
658
+
659
+ // Business code
660
+ import { getDocument } from '@/core/globals';
661
+
662
+ const doc = getDocument();
663
+ const element = doc.getElementById('root');
664
+ ```
665
+
666
+ ### 5. **Document Special Cases**
667
+
668
+ If an infrastructure layer must directly access global variables, add comments explaining why:
669
+
670
+ ```typescript
671
+ // ClientIOC.ts
672
+ create() {
673
+ // Note: Direct use of window.location.pathname here
674
+ // Reason: Needed for IOC container initialization, and executes after main.tsx, browser environment guaranteed
675
+ // TODO: Consider passing through BootstrapClient to avoid direct access
676
+ const pathname = window.location.pathname;
677
+ // ...
678
+ }
679
+ ```
680
+
681
+ ### 6. **Recommended Solution Priority**
682
+
683
+ ```
684
+ 1️⃣ Use IOC container services (useIoc('XxxService')) ⭐ Best
685
+ 2️⃣ Use React ecosystem solutions (useLocation, useMediaQuery) 👍 Recommended
686
+ 3️⃣ Import from globals (import { xxx } from '@/core/globals') ✅ Acceptable
687
+ 4️⃣ Direct access to global vars (window.xxx) ❌ Prohibited
688
+ ```
689
+
690
+ ---
691
+
692
+ ## ❓ FAQ
693
+
694
+ ### Q1: My code is simple, why so much trouble?
695
+
696
+ **A:** Architectural standards aren't for "now", but for:
697
+
698
+ - Possible future SSR requirements
699
+ - Easier to write unit tests
700
+ - Consistency in team collaboration
701
+ - Code maintainability and traceability
702
+
703
+ ### Q2: What if I really need to directly use global variables in a file?
704
+
705
+ **A:** Add exception in `eslint.config.mjs`:
706
+
707
+ ```javascript
708
+ {
709
+ files: [
710
+ 'src/main.tsx',
711
+ 'src/core/globals.ts',
712
+ 'src/utils/dom-helper.ts' // Add your file
713
+ ],
714
+ rules: {
715
+ 'no-restricted-globals': 'off'
716
+ }
717
+ }
718
+ ```
719
+
720
+ But consider carefully and add comments explaining why.
721
+
722
+ ### Q3: What's the difference between `@/core/globals` and direct `window.xxx`?
723
+
724
+ **A:** Main differences:
725
+
726
+ 1. **Type safety**: Encapsulation layer provides complete TypeScript types
727
+ 2. **Error handling**: Encapsulation layer can handle SSR, privacy mode, etc.
728
+ 3. **Unified management**: All browser API access in one place, easy to track and modify
729
+ 4. **Testability**: Can easily mock entire `@/core/globals` module
730
+
731
+ ### Q4: Why can `ClientIOC` directly use `window.location.pathname`?
732
+
733
+ **A:** This is a **tradeoff**:
734
+
735
+ - **Acceptable**: Because `ClientIOC` is infrastructure layer and executes after `main.tsx`, browser environment is guaranteed
736
+ - **Better approach**: Pass `pathname` parameter through `BootstrapClient.main()`
737
+ - **Future improvement**: Plan to refactor to dependency injection approach
738
+
739
+ ---
740
+
741
+ ## 🎯 Summary
742
+
743
+ ### Locations Allowed to Use Global Variables
744
+
745
+ | Location | Allowed | Description |
746
+ | --------------------------------- | --------------- | ----------------------------------------------------------------- |
747
+ | `src/main.tsx` | ✅ Yes | Application entry, responsible for injecting dependencies |
748
+ | `src/core/globals.ts` | ✅ Yes | Encapsulation layer, uniformly manages global variables |
749
+ | `src/core/clientIoc/ClientIOC.ts` | ⚠️ Special case | Infrastructure layer, recommended to change to injection approach |
750
+ | Other business code | ❌ Prohibited | Must access through encapsulation layer or dependency injection |
751
+
752
+ ### How Business Code Accesses Browser APIs
753
+
754
+ ```typescript
755
+ // Priority from high to low
756
+
757
+ // 🥇 Method 1: Get service through IOC container (most recommended)
758
+ const authService = useIoc('AuthService');
759
+ authService.setToken(token); // Service internally handles storage
760
+
761
+ // 🥈 Method 2: Use React ecosystem solutions
762
+ const location = useLocation(); // react-router-dom
763
+ const path = location.pathname;
764
+
765
+ // 🥉 Method 3: Import encapsulation from globals
766
+ import { localStorage } from '@/core/globals';
767
+ localStorage.setItem('key', value);
768
+
769
+ // ❌ Method 4: Direct access (prohibited!)
770
+ window.localStorage.setItem('key', value); // ESLint error
771
+ ```
772
+
773
+ ### Remember Three Principles:
774
+
775
+ 1. **Inject at entry** - `main.tsx` is the only place to directly access browser environment
776
+ 2. **Encapsulate in encapsulation layer** - `core/globals.ts` or service layer provides unified interface
777
+ 3. **Use in business layer** - Prioritize getting services through IOC container, second choice import from encapsulation layer
778
+
779
+ ### Why Do This?
780
+
781
+ ✅ **Easy to test** - Can easily mock services or encapsulation layer
782
+ ✅ **SSR compatible** - Encapsulation layer can handle server-side rendering scenarios
783
+ ✅ **Type safety** - Complete TypeScript type support
784
+ ✅ **Easy to trace** - Unified dependency management, easy to find and refactor
785
+ ✅ **Degradation handling** - Uniformly handle browser compatibility and degradation strategies
786
+ ✅ **Decouple business** - Business code doesn't depend on specific implementations
787
+
788
+ ---
789
+
790
+ **Related Documentation:**
791
+
792
+ - [ESLint Configuration](../../eslint.config.mjs)
793
+ - [Dependency Injection Pattern](./dependency-injection.md)
794
+ - [Project Architecture Design](./index.md)
795
+
796
+ **Need Help?**
797
+ If you're unsure how to handle a certain scenario, please ask in the team channel or submit an Issue.