@qlover/create-app 1.1.0 → 2.0.2

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 (556) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/index.cjs +9 -9
  3. package/dist/index.js +9 -9
  4. package/package.json +5 -4
  5. package/dist/configs/_common/.editorconfig +0 -23
  6. package/dist/configs/_common/.env.template +0 -13
  7. package/dist/configs/_common/.gitattributes +0 -2
  8. package/dist/configs/_common/.github/workflows/general-check.yml +0 -41
  9. package/dist/configs/_common/.github/workflows/release.yml +0 -81
  10. package/dist/configs/_common/.gitignore.template +0 -64
  11. package/dist/configs/_common/.husky/commit-msg +0 -3
  12. package/dist/configs/_common/.husky/pre-commit +0 -3
  13. package/dist/configs/_common/.prettierignore +0 -17
  14. package/dist/configs/_common/.prettierrc.js +0 -7
  15. package/dist/configs/_common/.vscode/extensions.json +0 -9
  16. package/dist/configs/_common/.vscode/react.code-snippets +0 -19
  17. package/dist/configs/_common/.vscode/settings.json +0 -16
  18. package/dist/configs/_common/commitlint.config.js +0 -10
  19. package/dist/configs/_common/package.json.template +0 -75
  20. package/dist/configs/node-lib/eslint.config.js +0 -50
  21. package/dist/templates/next-app/.env.template +0 -25
  22. package/dist/templates/next-app/.prettierignore +0 -58
  23. package/dist/templates/next-app/README.en.md +0 -130
  24. package/dist/templates/next-app/README.md +0 -130
  25. package/dist/templates/next-app/config/IOCIdentifier.ts +0 -74
  26. package/dist/templates/next-app/config/Identifier/api.ts +0 -41
  27. package/dist/templates/next-app/config/Identifier/common/admint.table.ts +0 -69
  28. package/dist/templates/next-app/config/Identifier/common/common.ts +0 -90
  29. package/dist/templates/next-app/config/Identifier/common/index.ts +0 -3
  30. package/dist/templates/next-app/config/Identifier/common/validators.ts +0 -34
  31. package/dist/templates/next-app/config/Identifier/index.ts +0 -3
  32. package/dist/templates/next-app/config/Identifier/pages/index.ts +0 -7
  33. package/dist/templates/next-app/config/Identifier/pages/page.about.ts +0 -20
  34. package/dist/templates/next-app/config/Identifier/pages/page.admin.home.ts +0 -27
  35. package/dist/templates/next-app/config/Identifier/pages/page.admin.locales.ts +0 -266
  36. package/dist/templates/next-app/config/Identifier/pages/page.admin.user.ts +0 -293
  37. package/dist/templates/next-app/config/Identifier/pages/page.home.ts +0 -56
  38. package/dist/templates/next-app/config/Identifier/pages/page.login.ts +0 -159
  39. package/dist/templates/next-app/config/Identifier/pages/page.register.ts +0 -177
  40. package/dist/templates/next-app/config/adminNavs.ts +0 -19
  41. package/dist/templates/next-app/config/common.ts +0 -43
  42. package/dist/templates/next-app/config/cookies.ts +0 -23
  43. package/dist/templates/next-app/config/i18n/AboutI18n.ts +0 -14
  44. package/dist/templates/next-app/config/i18n/HomeI18n.ts +0 -24
  45. package/dist/templates/next-app/config/i18n/PageI18nInterface.ts +0 -51
  46. package/dist/templates/next-app/config/i18n/admin18n.ts +0 -75
  47. package/dist/templates/next-app/config/i18n/i18nConfig.ts +0 -16
  48. package/dist/templates/next-app/config/i18n/i18nKeyScheam.ts +0 -36
  49. package/dist/templates/next-app/config/i18n/index.ts +0 -7
  50. package/dist/templates/next-app/config/i18n/loginI18n.ts +0 -50
  51. package/dist/templates/next-app/config/i18n/register18n.ts +0 -44
  52. package/dist/templates/next-app/config/route.ts +0 -9
  53. package/dist/templates/next-app/config/theme.ts +0 -28
  54. package/dist/templates/next-app/docs/en/api.md +0 -387
  55. package/dist/templates/next-app/docs/en/component.md +0 -544
  56. package/dist/templates/next-app/docs/en/database.md +0 -496
  57. package/dist/templates/next-app/docs/en/development-guide.md +0 -727
  58. package/dist/templates/next-app/docs/en/env.md +0 -563
  59. package/dist/templates/next-app/docs/en/i18n.md +0 -287
  60. package/dist/templates/next-app/docs/en/index.md +0 -165
  61. package/dist/templates/next-app/docs/en/page.md +0 -457
  62. package/dist/templates/next-app/docs/en/project-structure.md +0 -176
  63. package/dist/templates/next-app/docs/en/router.md +0 -427
  64. package/dist/templates/next-app/docs/en/theme.md +0 -532
  65. package/dist/templates/next-app/docs/en/validator.md +0 -478
  66. package/dist/templates/next-app/docs/zh/api.md +0 -387
  67. package/dist/templates/next-app/docs/zh/component.md +0 -544
  68. package/dist/templates/next-app/docs/zh/database.md +0 -496
  69. package/dist/templates/next-app/docs/zh/development-guide.md +0 -727
  70. package/dist/templates/next-app/docs/zh/env.md +0 -563
  71. package/dist/templates/next-app/docs/zh/i18n.md +0 -287
  72. package/dist/templates/next-app/docs/zh/index.md +0 -165
  73. package/dist/templates/next-app/docs/zh/page.md +0 -457
  74. package/dist/templates/next-app/docs/zh/project-structure.md +0 -176
  75. package/dist/templates/next-app/docs/zh/router.md +0 -427
  76. package/dist/templates/next-app/docs/zh/theme.md +0 -532
  77. package/dist/templates/next-app/docs/zh/validator.md +0 -476
  78. package/dist/templates/next-app/eslint.config.mjs +0 -285
  79. package/dist/templates/next-app/make/generateLocales.ts +0 -32
  80. package/dist/templates/next-app/migrations/schema/LocalesSchema.ts +0 -15
  81. package/dist/templates/next-app/migrations/schema/UserSchema.ts +0 -38
  82. package/dist/templates/next-app/migrations/sql/1694244000000.sql +0 -21
  83. package/dist/templates/next-app/next.config.ts +0 -25
  84. package/dist/templates/next-app/package.json +0 -87
  85. package/dist/templates/next-app/postcss.config.mjs +0 -5
  86. package/dist/templates/next-app/public/favicon.ico +0 -0
  87. package/dist/templates/next-app/public/file.svg +0 -1
  88. package/dist/templates/next-app/public/globe.svg +0 -1
  89. package/dist/templates/next-app/public/locales/en.json +0 -182
  90. package/dist/templates/next-app/public/locales/zh.json +0 -182
  91. package/dist/templates/next-app/public/next.svg +0 -1
  92. package/dist/templates/next-app/public/vercel.svg +0 -1
  93. package/dist/templates/next-app/public/window.svg +0 -1
  94. package/dist/templates/next-app/src/app/[locale]/admin/AdminI18nProvider.tsx +0 -37
  95. package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +0 -42
  96. package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +0 -153
  97. package/dist/templates/next-app/src/app/[locale]/admin/page.tsx +0 -20
  98. package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +0 -67
  99. package/dist/templates/next-app/src/app/[locale]/auth/layout.tsx +0 -18
  100. package/dist/templates/next-app/src/app/[locale]/auth/login/LoginForm.tsx +0 -126
  101. package/dist/templates/next-app/src/app/[locale]/auth/login/page.tsx +0 -90
  102. package/dist/templates/next-app/src/app/[locale]/auth/page.tsx +0 -8
  103. package/dist/templates/next-app/src/app/[locale]/auth/register/RegisterForm.tsx +0 -197
  104. package/dist/templates/next-app/src/app/[locale]/auth/register/page.tsx +0 -90
  105. package/dist/templates/next-app/src/app/[locale]/layout.tsx +0 -63
  106. package/dist/templates/next-app/src/app/[locale]/not-found.tsx +0 -24
  107. package/dist/templates/next-app/src/app/[locale]/page.tsx +0 -98
  108. package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +0 -13
  109. package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +0 -13
  110. package/dist/templates/next-app/src/app/api/admin/locales/route.ts +0 -20
  111. package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +0 -13
  112. package/dist/templates/next-app/src/app/api/admin/users/route.ts +0 -20
  113. package/dist/templates/next-app/src/app/api/ai/completions/route.ts +0 -32
  114. package/dist/templates/next-app/src/app/api/auth/callback/route.ts +0 -11
  115. package/dist/templates/next-app/src/app/api/callback/route.ts +0 -49
  116. package/dist/templates/next-app/src/app/api/locales/json/route.ts +0 -33
  117. package/dist/templates/next-app/src/app/api/user/login/route.ts +0 -10
  118. package/dist/templates/next-app/src/app/api/user/logout/route.ts +0 -8
  119. package/dist/templates/next-app/src/app/api/user/register/route.ts +0 -11
  120. package/dist/templates/next-app/src/app/manifest.ts +0 -16
  121. package/dist/templates/next-app/src/app/robots.txt +0 -2
  122. package/dist/templates/next-app/src/base/cases/AdminPageManager.ts +0 -28
  123. package/dist/templates/next-app/src/base/cases/AppConfig.ts +0 -40
  124. package/dist/templates/next-app/src/base/cases/Datetime.ts +0 -18
  125. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +0 -57
  126. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +0 -116
  127. package/dist/templates/next-app/src/base/cases/InversifyContainer.ts +0 -45
  128. package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +0 -32
  129. package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +0 -77
  130. package/dist/templates/next-app/src/base/cases/ResourceState.ts +0 -17
  131. package/dist/templates/next-app/src/base/cases/RouterService.ts +0 -52
  132. package/dist/templates/next-app/src/base/cases/StringEncryptor.ts +0 -73
  133. package/dist/templates/next-app/src/base/cases/TranslateI18nUtil.ts +0 -53
  134. package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +0 -212
  135. package/dist/templates/next-app/src/base/port/AdminLayoutInterface.ts +0 -26
  136. package/dist/templates/next-app/src/base/port/AppApiInterface.ts +0 -36
  137. package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +0 -27
  138. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +0 -25
  139. package/dist/templates/next-app/src/base/port/IOCInterface.ts +0 -33
  140. package/dist/templates/next-app/src/base/port/RouterInterface.ts +0 -11
  141. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +0 -25
  142. package/dist/templates/next-app/src/base/port/ZodBuilderInterface.ts +0 -8
  143. package/dist/templates/next-app/src/base/services/AdminPageEvent.ts +0 -26
  144. package/dist/templates/next-app/src/base/services/AdminPageScheduler.ts +0 -42
  145. package/dist/templates/next-app/src/base/services/AppApiRequester.ts +0 -67
  146. package/dist/templates/next-app/src/base/services/AppUserGateway.ts +0 -110
  147. package/dist/templates/next-app/src/base/services/I18nService.ts +0 -87
  148. package/dist/templates/next-app/src/base/services/ResourceService.ts +0 -139
  149. package/dist/templates/next-app/src/base/services/UserService.ts +0 -68
  150. package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +0 -106
  151. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +0 -87
  152. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +0 -110
  153. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +0 -52
  154. package/dist/templates/next-app/src/base/types/AppPageRouter.ts +0 -12
  155. package/dist/templates/next-app/src/base/types/PagesRouter.ts +0 -9
  156. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +0 -76
  157. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +0 -125
  158. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -50
  159. package/dist/templates/next-app/src/core/bootstraps/IocIdentifierTest.ts +0 -26
  160. package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +0 -18
  161. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +0 -68
  162. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +0 -100
  163. package/dist/templates/next-app/src/core/globals.ts +0 -28
  164. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +0 -80
  165. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +0 -66
  166. package/dist/templates/next-app/src/i18n/loadMessages.ts +0 -103
  167. package/dist/templates/next-app/src/i18n/request.ts +0 -31
  168. package/dist/templates/next-app/src/i18n/routing.ts +0 -35
  169. package/dist/templates/next-app/src/lib/supabase/client.ts +0 -8
  170. package/dist/templates/next-app/src/lib/supabase/conts.ts +0 -2
  171. package/dist/templates/next-app/src/lib/supabase/proxy.ts +0 -84
  172. package/dist/templates/next-app/src/lib/supabase/server.ts +0 -38
  173. package/dist/templates/next-app/src/pages/[locale]/about.tsx +0 -61
  174. package/dist/templates/next-app/src/pages/_app.tsx +0 -50
  175. package/dist/templates/next-app/src/pages/_document.tsx +0 -13
  176. package/dist/templates/next-app/src/proxy.ts +0 -33
  177. package/dist/templates/next-app/src/server/AppErrorApi.ts +0 -10
  178. package/dist/templates/next-app/src/server/AppPageRouteParams.ts +0 -110
  179. package/dist/templates/next-app/src/server/AppSuccessApi.ts +0 -7
  180. package/dist/templates/next-app/src/server/NextApiServer.ts +0 -61
  181. package/dist/templates/next-app/src/server/PagesRouteParams.ts +0 -145
  182. package/dist/templates/next-app/src/server/PasswordEncrypt.ts +0 -18
  183. package/dist/templates/next-app/src/server/ServerAuth.ts +0 -81
  184. package/dist/templates/next-app/src/server/SupabaseBridge.ts +0 -262
  185. package/dist/templates/next-app/src/server/UserCredentialToken.ts +0 -53
  186. package/dist/templates/next-app/src/server/controllers/AdminLocalesController.ts +0 -86
  187. package/dist/templates/next-app/src/server/controllers/AdminUserController.ts +0 -42
  188. package/dist/templates/next-app/src/server/controllers/LocalesController.ts +0 -36
  189. package/dist/templates/next-app/src/server/controllers/UserController.ts +0 -91
  190. package/dist/templates/next-app/src/server/port/AIControllerInterface.ts +0 -8
  191. package/dist/templates/next-app/src/server/port/AdminLocalesControllerInterface.ts +0 -21
  192. package/dist/templates/next-app/src/server/port/AdminUserControllerInterface.ts +0 -11
  193. package/dist/templates/next-app/src/server/port/CrentialTokenInterface.ts +0 -5
  194. package/dist/templates/next-app/src/server/port/DBBridgeInterface.ts +0 -36
  195. package/dist/templates/next-app/src/server/port/DBTableInterface.ts +0 -12
  196. package/dist/templates/next-app/src/server/port/LocalesControllerInterface.ts +0 -10
  197. package/dist/templates/next-app/src/server/port/LocalesRepositoryInterface.ts +0 -43
  198. package/dist/templates/next-app/src/server/port/PaginationInterface.ts +0 -6
  199. package/dist/templates/next-app/src/server/port/RouteParamsnHandlerInterface.ts +0 -18
  200. package/dist/templates/next-app/src/server/port/ServerAuthInterface.ts +0 -15
  201. package/dist/templates/next-app/src/server/port/ServerInterface.ts +0 -23
  202. package/dist/templates/next-app/src/server/port/UserRepositoryInterface.ts +0 -15
  203. package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +0 -14
  204. package/dist/templates/next-app/src/server/port/ValidatorInterface.ts +0 -23
  205. package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +0 -216
  206. package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +0 -102
  207. package/dist/templates/next-app/src/server/services/AIService.ts +0 -45
  208. package/dist/templates/next-app/src/server/services/AdminAuthPlugin.ts +0 -21
  209. package/dist/templates/next-app/src/server/services/AdminLocalesService.ts +0 -20
  210. package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +0 -137
  211. package/dist/templates/next-app/src/server/services/ApiUserService.ts +0 -29
  212. package/dist/templates/next-app/src/server/services/UserService.ts +0 -134
  213. package/dist/templates/next-app/src/server/validators/ExtendedExecutorError.ts +0 -6
  214. package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +0 -145
  215. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +0 -82
  216. package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +0 -70
  217. package/dist/templates/next-app/src/server/validators/SignupVerifyValidator.ts +0 -68
  218. package/dist/templates/next-app/src/styles/css/antd-themes/_common/_default.css +0 -280
  219. package/dist/templates/next-app/src/styles/css/antd-themes/_common/dark.css +0 -233
  220. package/dist/templates/next-app/src/styles/css/antd-themes/_common/index.css +0 -3
  221. package/dist/templates/next-app/src/styles/css/antd-themes/_common/pink.css +0 -246
  222. package/dist/templates/next-app/src/styles/css/antd-themes/index.css +0 -4
  223. package/dist/templates/next-app/src/styles/css/antd-themes/menu/_default.css +0 -108
  224. package/dist/templates/next-app/src/styles/css/antd-themes/menu/dark.css +0 -67
  225. package/dist/templates/next-app/src/styles/css/antd-themes/menu/index.css +0 -3
  226. package/dist/templates/next-app/src/styles/css/antd-themes/menu/pink.css +0 -67
  227. package/dist/templates/next-app/src/styles/css/antd-themes/no-context.css +0 -34
  228. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/_default.css +0 -34
  229. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/dark.css +0 -31
  230. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/index.css +0 -3
  231. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/pink.css +0 -36
  232. package/dist/templates/next-app/src/styles/css/antd-themes/table/_default.css +0 -44
  233. package/dist/templates/next-app/src/styles/css/antd-themes/table/dark.css +0 -43
  234. package/dist/templates/next-app/src/styles/css/antd-themes/table/index.css +0 -3
  235. package/dist/templates/next-app/src/styles/css/antd-themes/table/pink.css +0 -43
  236. package/dist/templates/next-app/src/styles/css/index.css +0 -6
  237. package/dist/templates/next-app/src/styles/css/page.css +0 -20
  238. package/dist/templates/next-app/src/styles/css/scrollbar.css +0 -34
  239. package/dist/templates/next-app/src/styles/css/tailwind.css +0 -5
  240. package/dist/templates/next-app/src/styles/css/themes/_default.css +0 -30
  241. package/dist/templates/next-app/src/styles/css/themes/dark.css +0 -30
  242. package/dist/templates/next-app/src/styles/css/themes/index.css +0 -3
  243. package/dist/templates/next-app/src/styles/css/themes/pink.css +0 -30
  244. package/dist/templates/next-app/src/styles/css/zIndex.css +0 -9
  245. package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +0 -150
  246. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +0 -39
  247. package/dist/templates/next-app/src/uikit/components/ClientRootProvider.tsx +0 -64
  248. package/dist/templates/next-app/src/uikit/components/ClientSeo.tsx +0 -36
  249. package/dist/templates/next-app/src/uikit/components/ClinetRenderProvider.tsx +0 -42
  250. package/dist/templates/next-app/src/uikit/components/EditableCell.tsx +0 -118
  251. package/dist/templates/next-app/src/uikit/components/FeatureItem.tsx +0 -13
  252. package/dist/templates/next-app/src/uikit/components/IOCProvider.tsx +0 -34
  253. package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +0 -50
  254. package/dist/templates/next-app/src/uikit/components/With.tsx +0 -17
  255. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportButton.tsx +0 -62
  256. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportEvent.ts +0 -28
  257. package/dist/templates/next-app/src/uikit/components/localesImportButton/import.module.css +0 -6
  258. package/dist/templates/next-app/src/uikit/components-app/AdminButton.tsx +0 -29
  259. package/dist/templates/next-app/src/uikit/components-app/AppBridge.tsx +0 -17
  260. package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +0 -105
  261. package/dist/templates/next-app/src/uikit/components-app/AuthButton.tsx +0 -20
  262. package/dist/templates/next-app/src/uikit/components-app/LanguageSwitcher.tsx +0 -75
  263. package/dist/templates/next-app/src/uikit/components-app/LogoutButton.tsx +0 -45
  264. package/dist/templates/next-app/src/uikit/components-app/ThemeSwitcher.tsx +0 -144
  265. package/dist/templates/next-app/src/uikit/components-pages/LanguageSwitcher.tsx +0 -98
  266. package/dist/templates/next-app/src/uikit/components-pages/PagesRoutePage.tsx +0 -93
  267. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +0 -25
  268. package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +0 -22
  269. package/dist/templates/next-app/src/uikit/hook/useIOC.ts +0 -37
  270. package/dist/templates/next-app/src/uikit/hook/useStrictEffect.ts +0 -32
  271. package/dist/templates/next-app/src/uikit/hook/useWarnTranslations.ts +0 -11
  272. package/dist/templates/next-app/src/uikit/utils/getHashParams.ts +0 -8
  273. package/dist/templates/next-app/src/uikit/utils/getHashVerifyEmailParams.ts +0 -42
  274. package/dist/templates/next-app/tailwind.config.ts +0 -8
  275. package/dist/templates/next-app/tsconfig.json +0 -39
  276. package/dist/templates/node-lib/__tests__/readJson.test.ts +0 -26
  277. package/dist/templates/node-lib/bin/test.js +0 -30
  278. package/dist/templates/node-lib/package.json +0 -66
  279. package/dist/templates/node-lib/rollup.config.js +0 -79
  280. package/dist/templates/node-lib/src/index.ts +0 -7
  281. package/dist/templates/node-lib/src/readJson.ts +0 -12
  282. package/dist/templates/node-lib/tsconfig.json +0 -23
  283. package/dist/templates/pack-app/README.md +0 -108
  284. package/dist/templates/pack-app/eslint.config.js +0 -97
  285. package/dist/templates/pack-app/fe-config.json +0 -35
  286. package/dist/templates/pack-app/package.json +0 -86
  287. package/dist/templates/pack-app/pnpm-workspace.yaml +0 -2
  288. package/dist/templates/pack-app/tsconfig.json +0 -9
  289. package/dist/templates/pack-app/tsconfig.test.json +0 -10
  290. package/dist/templates/pack-app/vite.config.ts +0 -14
  291. package/dist/templates/react-app/.env.template +0 -22
  292. package/dist/templates/react-app/.prettierignore +0 -17
  293. package/dist/templates/react-app/README.en.md +0 -274
  294. package/dist/templates/react-app/README.md +0 -273
  295. package/dist/templates/react-app/__tests__/__mocks__/BootstrapTest.ts +0 -16
  296. package/dist/templates/react-app/__tests__/__mocks__/MockAppConfig.ts +0 -48
  297. package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +0 -17
  298. package/dist/templates/react-app/__tests__/__mocks__/MockLogger.ts +0 -14
  299. package/dist/templates/react-app/__tests__/__mocks__/components/TestApp.tsx +0 -38
  300. package/dist/templates/react-app/__tests__/__mocks__/components/TestBootstrapsProvider.tsx +0 -53
  301. package/dist/templates/react-app/__tests__/__mocks__/components/TestRouter.tsx +0 -46
  302. package/dist/templates/react-app/__tests__/__mocks__/components/index.ts +0 -12
  303. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +0 -96
  304. package/dist/templates/react-app/__tests__/__mocks__/i18nextHttpBackend.ts +0 -110
  305. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOC.ts +0 -55
  306. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOCRegister.ts +0 -74
  307. package/dist/templates/react-app/__tests__/setup/index.ts +0 -1
  308. package/dist/templates/react-app/__tests__/setup/setupGlobal.ts +0 -64
  309. package/dist/templates/react-app/__tests__/src/App.structure.test.tsx +0 -115
  310. package/dist/templates/react-app/__tests__/src/base/cases/AppConfig.test.ts +0 -288
  311. package/dist/templates/react-app/__tests__/src/base/cases/DialogHandler.test.ts +0 -226
  312. package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +0 -178
  313. package/dist/templates/react-app/__tests__/src/base/cases/InversifyContainer.test.ts +0 -181
  314. package/dist/templates/react-app/__tests__/src/base/cases/PublicAssetsPath.test.ts +0 -61
  315. package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +0 -177
  316. package/dist/templates/react-app/__tests__/src/base/cases/RequestStatusCatcher.test.ts +0 -191
  317. package/dist/templates/react-app/__tests__/src/base/cases/RouterLoader.test.ts +0 -245
  318. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +0 -240
  319. package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +0 -242
  320. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapClient.test.ts +0 -135
  321. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +0 -74
  322. package/dist/templates/react-app/__tests__/src/main.test.tsx +0 -46
  323. package/dist/templates/react-app/__tests__/src/uikit/components/BaseHeader.test.tsx +0 -97
  324. package/dist/templates/react-app/__tests__/src/uikit/components/chatMessage/ChatRoot.test.tsx +0 -274
  325. package/dist/templates/react-app/config/IOCIdentifier.ts +0 -91
  326. package/dist/templates/react-app/config/Identifier/common/common.error.ts +0 -34
  327. package/dist/templates/react-app/config/Identifier/common/common.ts +0 -62
  328. package/dist/templates/react-app/config/Identifier/common/index.ts +0 -2
  329. package/dist/templates/react-app/config/Identifier/components/component.chatMessage.ts +0 -56
  330. package/dist/templates/react-app/config/Identifier/components/component.messageBaseList.ts +0 -103
  331. package/dist/templates/react-app/config/Identifier/index.ts +0 -2
  332. package/dist/templates/react-app/config/Identifier/pages/index.ts +0 -9
  333. package/dist/templates/react-app/config/Identifier/pages/page.about.ts +0 -189
  334. package/dist/templates/react-app/config/Identifier/pages/page.executor.ts +0 -275
  335. package/dist/templates/react-app/config/Identifier/pages/page.home.ts +0 -71
  336. package/dist/templates/react-app/config/Identifier/pages/page.identifiter.ts +0 -102
  337. package/dist/templates/react-app/config/Identifier/pages/page.jsonStorage.ts +0 -77
  338. package/dist/templates/react-app/config/Identifier/pages/page.login.ts +0 -162
  339. package/dist/templates/react-app/config/Identifier/pages/page.message.ts +0 -20
  340. package/dist/templates/react-app/config/Identifier/pages/page.register.ts +0 -159
  341. package/dist/templates/react-app/config/Identifier/pages/page.request.ts +0 -169
  342. package/dist/templates/react-app/config/app.router.ts +0 -338
  343. package/dist/templates/react-app/config/common.ts +0 -85
  344. package/dist/templates/react-app/config/feapi.mock.json +0 -34
  345. package/dist/templates/react-app/config/i18n/PageI18nInterface.ts +0 -53
  346. package/dist/templates/react-app/config/i18n/aboutI18n.ts +0 -42
  347. package/dist/templates/react-app/config/i18n/chatMessageI18n.ts +0 -17
  348. package/dist/templates/react-app/config/i18n/executorI18n.ts +0 -51
  349. package/dist/templates/react-app/config/i18n/homeI18n.ts +0 -24
  350. package/dist/templates/react-app/config/i18n/i18nConfig.ts +0 -30
  351. package/dist/templates/react-app/config/i18n/identifiter18n.ts +0 -30
  352. package/dist/templates/react-app/config/i18n/jsonStorage18n.ts +0 -27
  353. package/dist/templates/react-app/config/i18n/login18n.ts +0 -42
  354. package/dist/templates/react-app/config/i18n/messageBaseListI18n.ts +0 -22
  355. package/dist/templates/react-app/config/i18n/messageI18n.ts +0 -14
  356. package/dist/templates/react-app/config/i18n/notFoundI18n.ts +0 -34
  357. package/dist/templates/react-app/config/i18n/register18n.ts +0 -40
  358. package/dist/templates/react-app/config/i18n/request18n.ts +0 -41
  359. package/dist/templates/react-app/config/theme.ts +0 -21
  360. package/dist/templates/react-app/docs/en/bootstrap.md +0 -1891
  361. package/dist/templates/react-app/docs/en/components/chat-message-component.md +0 -320
  362. package/dist/templates/react-app/docs/en/components/chat-message-refactor.md +0 -283
  363. package/dist/templates/react-app/docs/en/components/message-base-list-component.md +0 -171
  364. package/dist/templates/react-app/docs/en/development-guide.md +0 -1199
  365. package/dist/templates/react-app/docs/en/env.md +0 -1336
  366. package/dist/templates/react-app/docs/en/global.md +0 -509
  367. package/dist/templates/react-app/docs/en/i18n.md +0 -979
  368. package/dist/templates/react-app/docs/en/index.md +0 -802
  369. package/dist/templates/react-app/docs/en/ioc.md +0 -1365
  370. package/dist/templates/react-app/docs/en/playwright/e2e-tests.md +0 -321
  371. package/dist/templates/react-app/docs/en/playwright/index.md +0 -19
  372. package/dist/templates/react-app/docs/en/playwright/installation-summary.md +0 -332
  373. package/dist/templates/react-app/docs/en/playwright/overview.md +0 -222
  374. package/dist/templates/react-app/docs/en/playwright/quickstart.md +0 -325
  375. package/dist/templates/react-app/docs/en/playwright/reorganization-notes.md +0 -340
  376. package/dist/templates/react-app/docs/en/playwright/setup-complete.md +0 -290
  377. package/dist/templates/react-app/docs/en/playwright/testing-guide.md +0 -565
  378. package/dist/templates/react-app/docs/en/request.md +0 -423
  379. package/dist/templates/react-app/docs/en/router.md +0 -404
  380. package/dist/templates/react-app/docs/en/store.md +0 -1331
  381. package/dist/templates/react-app/docs/en/test-guide.md +0 -976
  382. package/dist/templates/react-app/docs/en/theme.md +0 -424
  383. package/dist/templates/react-app/docs/en/typescript-guide.md +0 -473
  384. package/dist/templates/react-app/docs/en/why-no-globals.md +0 -797
  385. package/dist/templates/react-app/docs/zh/bootstrap.md +0 -1891
  386. package/dist/templates/react-app/docs/zh/components/chat-message-component.md +0 -320
  387. package/dist/templates/react-app/docs/zh/components/chat-message-refactor.md +0 -283
  388. package/dist/templates/react-app/docs/zh/components/message-base-list-component.md +0 -171
  389. package/dist/templates/react-app/docs/zh/development-guide.md +0 -1199
  390. package/dist/templates/react-app/docs/zh/env.md +0 -1336
  391. package/dist/templates/react-app/docs/zh/global.md +0 -511
  392. package/dist/templates/react-app/docs/zh/i18n.md +0 -979
  393. package/dist/templates/react-app/docs/zh/index.md +0 -786
  394. package/dist/templates/react-app/docs/zh/ioc.md +0 -1364
  395. package/dist/templates/react-app/docs/zh/playwright/e2e-tests.md +0 -321
  396. package/dist/templates/react-app/docs/zh/playwright/index.md +0 -19
  397. package/dist/templates/react-app/docs/zh/playwright/installation-summary.md +0 -332
  398. package/dist/templates/react-app/docs/zh/playwright/overview.md +0 -222
  399. package/dist/templates/react-app/docs/zh/playwright/quickstart.md +0 -325
  400. package/dist/templates/react-app/docs/zh/playwright/reorganization-notes.md +0 -340
  401. package/dist/templates/react-app/docs/zh/playwright/setup-complete.md +0 -290
  402. package/dist/templates/react-app/docs/zh/playwright/testing-guide.md +0 -565
  403. package/dist/templates/react-app/docs/zh/request.md +0 -427
  404. package/dist/templates/react-app/docs/zh/router.md +0 -408
  405. package/dist/templates/react-app/docs/zh/store.md +0 -1329
  406. package/dist/templates/react-app/docs/zh/test-guide.md +0 -976
  407. package/dist/templates/react-app/docs/zh/theme.md +0 -424
  408. package/dist/templates/react-app/docs/zh/typescript-guide.md +0 -473
  409. package/dist/templates/react-app/docs/zh/why-no-globals.md +0 -797
  410. package/dist/templates/react-app/e2e/App.spec.ts +0 -319
  411. package/dist/templates/react-app/e2e/fixtures/base.fixture.ts +0 -40
  412. package/dist/templates/react-app/e2e/main.spec.ts +0 -20
  413. package/dist/templates/react-app/e2e/utils/test-helpers.ts +0 -19
  414. package/dist/templates/react-app/eslint.config.mjs +0 -325
  415. package/dist/templates/react-app/index.html +0 -13
  416. package/dist/templates/react-app/makes/generateTs2LocalesOptions.ts +0 -26
  417. package/dist/templates/react-app/package.json +0 -125
  418. package/dist/templates/react-app/playwright.config.ts +0 -79
  419. package/dist/templates/react-app/postcss.config.js +0 -5
  420. package/dist/templates/react-app/public/locales/en/common.json +0 -235
  421. package/dist/templates/react-app/public/locales/zh/common.json +0 -235
  422. package/dist/templates/react-app/public/logo.svg +0 -1
  423. package/dist/templates/react-app/public/router-root/logo.svg +0 -1
  424. package/dist/templates/react-app/src/App.tsx +0 -35
  425. package/dist/templates/react-app/src/assets/react.svg +0 -1
  426. package/dist/templates/react-app/src/base/apis/AiApi.ts +0 -68
  427. package/dist/templates/react-app/src/base/apis/feApi/FeApi.ts +0 -29
  428. package/dist/templates/react-app/src/base/apis/feApi/FeApiAdapter.ts +0 -14
  429. package/dist/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +0 -86
  430. package/dist/templates/react-app/src/base/apis/feApi/FeApiType.ts +0 -21
  431. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +0 -142
  432. package/dist/templates/react-app/src/base/apis/userApi/UserApiAdapter.ts +0 -14
  433. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +0 -86
  434. package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +0 -70
  435. package/dist/templates/react-app/src/base/cases/AppConfig.ts +0 -123
  436. package/dist/templates/react-app/src/base/cases/DialogHandler.ts +0 -115
  437. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +0 -64
  438. package/dist/templates/react-app/src/base/cases/InversifyContainer.ts +0 -45
  439. package/dist/templates/react-app/src/base/cases/PublicAssetsPath.ts +0 -23
  440. package/dist/templates/react-app/src/base/cases/RequestLanguages.ts +0 -55
  441. package/dist/templates/react-app/src/base/cases/RequestLogger.ts +0 -80
  442. package/dist/templates/react-app/src/base/cases/RequestStatusCatcher.ts +0 -40
  443. package/dist/templates/react-app/src/base/cases/ResourceState.ts +0 -23
  444. package/dist/templates/react-app/src/base/cases/RouterLoader.ts +0 -173
  445. package/dist/templates/react-app/src/base/cases/TranslateI18nInterface.ts +0 -26
  446. package/dist/templates/react-app/src/base/port/ExecutorPageBridgeInterface.ts +0 -23
  447. package/dist/templates/react-app/src/base/port/I18nServiceInterface.ts +0 -10
  448. package/dist/templates/react-app/src/base/port/IOCInterface.ts +0 -38
  449. package/dist/templates/react-app/src/base/port/JSONStoragePageBridgeInterface.ts +0 -21
  450. package/dist/templates/react-app/src/base/port/RequestPageBridgeInterface.ts +0 -23
  451. package/dist/templates/react-app/src/base/port/RequestStatusInterface.ts +0 -5
  452. package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +0 -29
  453. package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +0 -20
  454. package/dist/templates/react-app/src/base/services/BaseLayoutService.ts +0 -61
  455. package/dist/templates/react-app/src/base/services/I18nService.ts +0 -146
  456. package/dist/templates/react-app/src/base/services/IdentifierService.ts +0 -162
  457. package/dist/templates/react-app/src/base/services/RouteService.ts +0 -115
  458. package/dist/templates/react-app/src/base/services/UserBootstrap.ts +0 -45
  459. package/dist/templates/react-app/src/base/services/UserService.ts +0 -88
  460. package/dist/templates/react-app/src/base/types/Page.ts +0 -47
  461. package/dist/templates/react-app/src/base/types/deprecated-antd.d.ts +0 -60
  462. package/dist/templates/react-app/src/base/types/global.d.ts +0 -8
  463. package/dist/templates/react-app/src/core/IOC.ts +0 -28
  464. package/dist/templates/react-app/src/core/bootstraps/BootstrapClient.ts +0 -108
  465. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -54
  466. package/dist/templates/react-app/src/core/bootstraps/IocIdentifierTest.ts +0 -26
  467. package/dist/templates/react-app/src/core/bootstraps/PrintBootstrap.ts +0 -14
  468. package/dist/templates/react-app/src/core/bootstraps/SaveAppInfo.ts +0 -28
  469. package/dist/templates/react-app/src/core/clientIoc/ClientIOC.ts +0 -47
  470. package/dist/templates/react-app/src/core/clientIoc/ClientIOCRegister.ts +0 -142
  471. package/dist/templates/react-app/src/core/globals.ts +0 -47
  472. package/dist/templates/react-app/src/main.tsx +0 -19
  473. package/dist/templates/react-app/src/pages/404.tsx +0 -19
  474. package/dist/templates/react-app/src/pages/500.tsx +0 -18
  475. package/dist/templates/react-app/src/pages/NoRouteFound.tsx +0 -5
  476. package/dist/templates/react-app/src/pages/auth/Layout.tsx +0 -27
  477. package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +0 -166
  478. package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +0 -226
  479. package/dist/templates/react-app/src/pages/base/AboutPage.tsx +0 -123
  480. package/dist/templates/react-app/src/pages/base/ExecutorPage.tsx +0 -467
  481. package/dist/templates/react-app/src/pages/base/HomePage.tsx +0 -81
  482. package/dist/templates/react-app/src/pages/base/IdentifierPage.tsx +0 -117
  483. package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +0 -132
  484. package/dist/templates/react-app/src/pages/base/Layout.tsx +0 -20
  485. package/dist/templates/react-app/src/pages/base/MessagePage.tsx +0 -71
  486. package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +0 -18
  487. package/dist/templates/react-app/src/pages/base/RequestPage.tsx +0 -193
  488. package/dist/templates/react-app/src/styles/css/antd-themes/_common/_default.css +0 -280
  489. package/dist/templates/react-app/src/styles/css/antd-themes/_common/dark.css +0 -233
  490. package/dist/templates/react-app/src/styles/css/antd-themes/_common/index.css +0 -3
  491. package/dist/templates/react-app/src/styles/css/antd-themes/_common/pink.css +0 -246
  492. package/dist/templates/react-app/src/styles/css/antd-themes/index.css +0 -4
  493. package/dist/templates/react-app/src/styles/css/antd-themes/menu/_default.css +0 -108
  494. package/dist/templates/react-app/src/styles/css/antd-themes/menu/dark.css +0 -67
  495. package/dist/templates/react-app/src/styles/css/antd-themes/menu/index.css +0 -3
  496. package/dist/templates/react-app/src/styles/css/antd-themes/menu/pink.css +0 -67
  497. package/dist/templates/react-app/src/styles/css/antd-themes/no-context.css +0 -34
  498. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/_default.css +0 -34
  499. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/dark.css +0 -31
  500. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/index.css +0 -3
  501. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/pink.css +0 -36
  502. package/dist/templates/react-app/src/styles/css/antd-themes/table/_default.css +0 -44
  503. package/dist/templates/react-app/src/styles/css/antd-themes/table/dark.css +0 -43
  504. package/dist/templates/react-app/src/styles/css/antd-themes/table/index.css +0 -3
  505. package/dist/templates/react-app/src/styles/css/antd-themes/table/pink.css +0 -43
  506. package/dist/templates/react-app/src/styles/css/index.css +0 -6
  507. package/dist/templates/react-app/src/styles/css/page.css +0 -20
  508. package/dist/templates/react-app/src/styles/css/scrollbar.css +0 -34
  509. package/dist/templates/react-app/src/styles/css/tailwind.css +0 -5
  510. package/dist/templates/react-app/src/styles/css/themes/_default.css +0 -30
  511. package/dist/templates/react-app/src/styles/css/themes/dark.css +0 -30
  512. package/dist/templates/react-app/src/styles/css/themes/index.css +0 -3
  513. package/dist/templates/react-app/src/styles/css/themes/pink.css +0 -30
  514. package/dist/templates/react-app/src/styles/css/zIndex.css +0 -9
  515. package/dist/templates/react-app/src/uikit/bridges/ExecutorPageBridge.ts +0 -72
  516. package/dist/templates/react-app/src/uikit/bridges/JSONStoragePageBridge.ts +0 -41
  517. package/dist/templates/react-app/src/uikit/bridges/NavigateBridge.ts +0 -22
  518. package/dist/templates/react-app/src/uikit/bridges/RequestPageBridge.ts +0 -136
  519. package/dist/templates/react-app/src/uikit/components/AppRouterProvider.tsx +0 -35
  520. package/dist/templates/react-app/src/uikit/components/BaseHeader.tsx +0 -51
  521. package/dist/templates/react-app/src/uikit/components/BaseLayoutProvider.tsx +0 -44
  522. package/dist/templates/react-app/src/uikit/components/BaseRouteProvider.tsx +0 -21
  523. package/dist/templates/react-app/src/uikit/components/BaseRouteSeo.tsx +0 -18
  524. package/dist/templates/react-app/src/uikit/components/BootstrapsProvider.tsx +0 -11
  525. package/dist/templates/react-app/src/uikit/components/ClientSeo.tsx +0 -62
  526. package/dist/templates/react-app/src/uikit/components/ComboProvider.tsx +0 -38
  527. package/dist/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +0 -78
  528. package/dist/templates/react-app/src/uikit/components/Loading.tsx +0 -49
  529. package/dist/templates/react-app/src/uikit/components/LocaleLink.tsx +0 -43
  530. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +0 -58
  531. package/dist/templates/react-app/src/uikit/components/MessageBaseList.tsx +0 -258
  532. package/dist/templates/react-app/src/uikit/components/RouterRenderComponent.tsx +0 -19
  533. package/dist/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +0 -137
  534. package/dist/templates/react-app/src/uikit/components/With.tsx +0 -17
  535. package/dist/templates/react-app/src/uikit/components/chatMessage/ChatMessageBridge.ts +0 -205
  536. package/dist/templates/react-app/src/uikit/components/chatMessage/ChatRoot.tsx +0 -21
  537. package/dist/templates/react-app/src/uikit/components/chatMessage/FocusBar.tsx +0 -108
  538. package/dist/templates/react-app/src/uikit/components/chatMessage/MessageApi.ts +0 -282
  539. package/dist/templates/react-app/src/uikit/components/chatMessage/MessageItem.tsx +0 -102
  540. package/dist/templates/react-app/src/uikit/components/chatMessage/MessagesList.tsx +0 -86
  541. package/dist/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +0 -42
  542. package/dist/templates/react-app/src/uikit/contexts/IOCContext.ts +0 -13
  543. package/dist/templates/react-app/src/uikit/hooks/useAppTranslation.ts +0 -26
  544. package/dist/templates/react-app/src/uikit/hooks/useI18nInterface.ts +0 -25
  545. package/dist/templates/react-app/src/uikit/hooks/useIOC.ts +0 -35
  546. package/dist/templates/react-app/src/uikit/hooks/useNavigateBridge.ts +0 -21
  547. package/dist/templates/react-app/src/uikit/hooks/useRouterI18nGuard.ts +0 -25
  548. package/dist/templates/react-app/src/uikit/hooks/useStrictEffect.ts +0 -31
  549. package/dist/templates/react-app/src/vite-env.d.ts +0 -1
  550. package/dist/templates/react-app/tailwind.config.js +0 -4
  551. package/dist/templates/react-app/tsconfig.app.json +0 -36
  552. package/dist/templates/react-app/tsconfig.e2e.json +0 -24
  553. package/dist/templates/react-app/tsconfig.json +0 -22
  554. package/dist/templates/react-app/tsconfig.node.json +0 -27
  555. package/dist/templates/react-app/tsconfig.test.json +0 -18
  556. package/dist/templates/react-app/vite.config.ts +0 -144
@@ -1,1365 +0,0 @@
1
- # IOC Container (Dependency Injection)
2
-
3
- ## 📋 Table of Contents
4
-
5
- - [Core Philosophy](#-core-philosophy) - UI separation, logic independence
6
- - [What is IOC](#-what-is-ioc) - Inversion of Control
7
- - [Why Need IOC](#-why-need-ioc) - Core problems it solves
8
- - [Two Key Questions](#-two-key-questions) - Why need interfaces? Why separate even simple components?
9
- - [Implementation in the Project](#-implementation-in-the-project) - Bootstrap integration
10
- - [How to Use](#-how-to-use) - Practical guide
11
- - [Testing](#-testing) - Independent testing and combination testing
12
- - [Best Practices](#-best-practices) - 8 core practices
13
- - [FAQ](#-faq) - Common questions
14
-
15
- ---
16
-
17
- ## 🎯 Core Philosophy
18
-
19
- > **🚨 Important Principle: UI is UI, logic is logic, they must be separated!**
20
-
21
- > **⭐ Core Advantage: UI and logic can be tested independently, and also in combination!**
22
-
23
- ### Core Concept
24
-
25
- ```
26
- ┌─────────────────────────────────────────┐
27
- │ Traditional Approach: UI and Logic Mixed│
28
- │ │
29
- │ Component │
30
- │ ├── UI rendering │
31
- │ ├── Business logic │
32
- │ ├── API calls │
33
- │ ├── State management │
34
- │ └── Data processing │
35
- │ │
36
- │ ❌ Problems: │
37
- │ - Hard to test (need to render) │
38
- │ - Logic can't be reused │
39
- │ - Unclear responsibilities │
40
- └─────────────────────────────────────────┘
41
-
42
- ┌─────────────────────────────────────────┐
43
- │ IOC Approach: UI and Logic Completely │
44
- │ Separated │
45
- │ │
46
- │ Component (UI Layer) │
47
- │ └── Only responsible for rendering │
48
- │ ↓ Get through IOC │
49
- │ Service (Logic Layer) │
50
- │ ├── Business logic │
51
- │ ├── API calls │
52
- │ ├── State management │
53
- │ └── Data processing │
54
- │ │
55
- │ ✅ Advantages: │
56
- │ - UI and logic can be tested separately │
57
- │ - Logic can be reused │
58
- │ - Clear responsibilities │
59
- └─────────────────────────────────────────┘
60
- ```
61
-
62
- ---
63
-
64
- ## 🔄 What is IOC
65
-
66
- IOC (Inversion of Control) = **Don't create objects yourself, let the container create and manage them**
67
-
68
- ### Traditional Approach vs IOC
69
-
70
- ```typescript
71
- // ❌ Traditional approach: Create dependencies yourself (tight coupling)
72
- class UserComponent {
73
- private userService = new UserService(); // Create yourself
74
- private storage = new LocalStorage(); // Create yourself
75
- private api = new UserApi(); // Create yourself
76
-
77
- async loadUser() {
78
- return await this.userService.getUser();
79
- }
80
- }
81
-
82
- // Problems:
83
- // 1. UserComponent depends on concrete implementations
84
- // 2. Cannot replace UserService implementation
85
- // 3. Cannot mock UserService in tests
86
- // 4. Need to manually create UserService dependencies
87
-
88
-
89
- // ✅ IOC approach: Container injects dependencies (loose coupling)
90
- function UserComponent() {
91
- // Get service from IOC container
92
- const userService = useIOC('UserServiceInterface'); // Container provides
93
-
94
- async function loadUser() {
95
- return await userService.getUser();
96
- }
97
-
98
- // UI only responsible for rendering
99
- return <div>...</div>;
100
- }
101
-
102
- // Advantages:
103
- // 1. UserComponent depends on interface, not implementation
104
- // 2. Can easily replace UserService implementation
105
- // 3. Can mock UserService in tests
106
- // 4. UserService dependencies managed by container
107
- ```
108
-
109
- ### Analogy
110
-
111
- ```
112
- Traditional Approach = Cook at home
113
- - Need to buy groceries (create dependencies)
114
- - Need to cook (manage lifecycle)
115
- - Need to wash dishes (clean up resources)
116
-
117
- IOC Approach = Go to restaurant
118
- - Order food (tell container what you need)
119
- - Wait for food (container provides service)
120
- - Don't need to worry about kitchen (dependency management handled by container)
121
- ```
122
-
123
- ---
124
-
125
- ## 🤔 Why Need IOC
126
-
127
- ### Core Problem: UI and Logic Mixed Together
128
-
129
- #### ❌ Problem Example: No UI Separation
130
-
131
- ```typescript
132
- // ❌ Traditional component: UI and logic mixed
133
- function UserProfile() {
134
- const [user, setUser] = useState(null);
135
- const [loading, setLoading] = useState(false);
136
- const [error, setError] = useState(null);
137
-
138
- // 😰 Business logic mixed in component
139
- useEffect(() => {
140
- setLoading(true);
141
-
142
- // 😰 API call in component
143
- fetch('/api/user')
144
- .then(res => res.json())
145
- .then(data => {
146
- // 😰 Data processing in component
147
- const processedData = {
148
- ...data,
149
- fullName: `${data.firstName} ${data.lastName}`
150
- };
151
- setUser(processedData);
152
- })
153
- .catch(err => setError(err))
154
- .finally(() => setLoading(false));
155
- }, []);
156
-
157
- // 😰 More business logic
158
- const handleLogout = () => {
159
- localStorage.removeItem('token');
160
- localStorage.removeItem('user');
161
- window.location.href = '/login';
162
- };
163
-
164
- // UI rendering
165
- if (loading) return <div>Loading...</div>;
166
- if (error) return <div>Error: {error.message}</div>;
167
-
168
- return (
169
- <div>
170
- <h1>{user?.fullName}</h1>
171
- <button onClick={handleLogout}>Logout</button>
172
- </div>
173
- );
174
- }
175
-
176
- // 😰😰😰 Problem Summary:
177
- // 1. UI and logic mixed, hard to maintain
178
- // 2. Logic can't be reused (what if another component needs user info?)
179
- // 3. Hard to test (need to render component to test business logic)
180
- // 4. Unclear responsibilities (component does too much)
181
- // 5. Can't test logic separately (must test through UI)
182
- ```
183
-
184
- #### ✅ Solution: IOC + UI Separation
185
-
186
- ```typescript
187
- // ✅ Step 1: Define interface (Port)
188
- export interface UserServiceInterface {
189
- getUser(): Promise<UserInfo>;
190
- logout(): Promise<void>;
191
- isAuthenticated(): boolean;
192
- }
193
-
194
- // ✅ Step 2: Implement service (Logic layer)
195
- @injectable()
196
- export class UserService implements UserServiceInterface {
197
- constructor(
198
- @inject(UserApi) private api: UserApi,
199
- @inject(IOCIdentifier.AppConfig) private config: AppConfig,
200
- @inject(IOCIdentifier.LocalStorageEncrypt) private storage: Storage,
201
- @inject(IOCIdentifier.RouteServiceInterface) private router: RouteService
202
- ) {}
203
-
204
- // Pure logic: Get user info
205
- async getUser(): Promise<UserInfo> {
206
- const data = await this.api.getUserInfo();
207
-
208
- // Data processing
209
- return {
210
- ...data,
211
- fullName: `${data.firstName} ${data.lastName}`
212
- };
213
- }
214
-
215
- // Pure logic: Logout
216
- async logout(): Promise<void> {
217
- this.storage.removeItem(this.config.userTokenStorageKey);
218
- this.storage.removeItem(this.config.userInfoStorageKey);
219
- await this.router.push('/login');
220
- }
221
-
222
- isAuthenticated(): boolean {
223
- return !!this.storage.getItem(this.config.userTokenStorageKey);
224
- }
225
- }
226
-
227
- // ✅ Step 3: UI component (UI layer)
228
- function UserProfile() {
229
- // Get service from IOC container
230
- const userService = useIOC('UserServiceInterface');
231
- const [user, setUser] = useState(null);
232
- const [loading, setLoading] = useState(false);
233
-
234
- useEffect(() => {
235
- setLoading(true);
236
- // ✅ UI only calls service, contains no business logic
237
- userService.getUser()
238
- .then(setUser)
239
- .finally(() => setLoading(false));
240
- }, []);
241
-
242
- // ✅ UI only responsible for rendering and event binding
243
- if (loading) return <div>Loading...</div>;
244
-
245
- return (
246
- <div>
247
- <h1>{user?.fullName}</h1>
248
- <button onClick={() => userService.logout()}>Logout</button>
249
- </div>
250
- );
251
- }
252
-
253
- // ✅✅✅ Advantages Summary:
254
- // 1. UI and logic completely separated, clear responsibilities
255
- // 2. Logic can be reused (other components can use UserService)
256
- // 3. Easy to test (can test UserService independently, no need to render UI)
257
- // 4. Easy to maintain (changing logic doesn't affect UI, changing UI doesn't affect logic)
258
- // 5. Can test logic separately (no dependency on UI)
259
- ```
260
-
261
- ### Comparison Summary
262
-
263
- | Feature | No UI Separation | IOC + UI Separation |
264
- | ------------------------------- | ---------------------------------- | --------------------------------------------- |
265
- | **Clarity of Responsibilities** | ❌ UI and logic mixed | ✅ UI only renders, logic independent |
266
- | **Testability** | ❌ Must render component to test | ✅ Logic can be tested independently |
267
- | **Reusability** | ❌ Logic can't be reused | ✅ Logic can be reused in multiple components |
268
- | **Maintainability** | ❌ Changing logic affects UI | ✅ UI and logic modified independently |
269
- | **Test Speed** | ❌ Slow (need to render UI) | ✅ Fast (pure logic tests) |
270
- | **Test Complexity** | ❌ High (need to mock many things) | ✅ Low (only need to mock interfaces) |
271
-
272
- ---
273
-
274
- ## ❓ Two Key Questions
275
-
276
- ### Question 1: Why does an implementation class also need an interface?
277
-
278
- Many developers ask: "If `UserService` only has one implementation class, why define a `UserServiceInterface` interface?"
279
-
280
- #### Answer: For testability and flexibility
281
-
282
- ```typescript
283
- // ❌ No interface: Hard to test
284
- class UserComponent {
285
- constructor(
286
- @inject(UserService) private userService: UserService // Depend on concrete implementation
287
- ) {}
288
- }
289
-
290
- // In tests:
291
- describe('UserComponent', () => {
292
- it('should load user', () => {
293
- // ❌ Problem: Cannot mock UserService
294
- // UserService has many dependencies (API, Storage, Router, etc.)
295
- // Need to create all these dependencies to create UserService
296
-
297
- const userApi = new UserApi(); // Need to create
298
- const storage = new Storage(); // Need to create
299
- const router = new Router(); // Need to create
300
- const config = new AppConfig(); // Need to create
301
-
302
- const userService = new UserService(userApi, config, storage, router);
303
- const component = new UserComponent(userService);
304
-
305
- // 😰 Too complex!
306
- });
307
- });
308
-
309
- // ✅ With interface: Easy to test
310
- class UserComponent {
311
- constructor(
312
- @inject('UserServiceInterface') // Depend on interface
313
- private userService: UserServiceInterface
314
- ) {}
315
- }
316
-
317
- // In tests:
318
- describe('UserComponent', () => {
319
- it('should load user', () => {
320
- // ✅ Only need to mock interface
321
- const mockUserService: UserServiceInterface = {
322
- getUser: jest.fn().mockResolvedValue({ name: 'John' }),
323
- logout: jest.fn(),
324
- isAuthenticated: jest.fn().mockReturnValue(true)
325
- };
326
-
327
- const component = new UserComponent(mockUserService);
328
-
329
- // ✅ Simple and clear!
330
- });
331
- });
332
- ```
333
-
334
- **Key Advantages:**
335
-
336
- 1. **Simple testing** - Only need to mock interface methods, no need to create real dependencies
337
- 2. **Isolation** - When testing UserComponent, don't need to care about UserService implementation details
338
- 3. **Flexibility** - Can easily replace implementation in future (like adding MockUserService, CacheUserService, etc.)
339
- 4. **Decoupling** - Component only depends on interface, not concrete implementation
340
-
341
- **Even with only one implementation class, interface is necessary because:**
342
-
343
- - ✅ Need to mock in tests
344
- - ✅ May have new implementations in future
345
- - ✅ Component shouldn't depend on concrete implementation
346
- - ✅ Interface is contract, implementation is detail
347
-
348
- ### Question 2: Why does a simple UI component also need UI separation?
349
-
350
- Many developers ask: "My component is simple, just displays a username, why separate?"
351
-
352
- #### Answer: For testability and future extensibility
353
-
354
- ```typescript
355
- // ❌ Simple component, not separated
356
- function UserName() {
357
- const [name, setName] = useState('');
358
-
359
- useEffect(() => {
360
- // 😰 Even if simple, logic is mixed in UI
361
- fetch('/api/user')
362
- .then(res => res.json())
363
- .then(data => setName(data.name));
364
- }, []);
365
-
366
- return <span>{name}</span>;
367
- }
368
-
369
- // Problems:
370
- // 1. Cannot test logic (must render component)
371
- // 2. What if logic becomes complex? (add cache, error handling, etc.)
372
- // 3. What if other components need username? (copy-paste?)
373
-
374
-
375
- // ✅ Simple component, but separated
376
- // 1. Service (Logic layer)
377
- @injectable()
378
- export class UserService implements UserServiceInterface {
379
- constructor(@inject(UserApi) private api: UserApi) {}
380
-
381
- async getUserName(): Promise<string> {
382
- const user = await this.api.getUserInfo();
383
- return user.name;
384
- }
385
- }
386
-
387
- // 2. UI component (UI layer)
388
- function UserName() {
389
- const userService = useIOC('UserServiceInterface');
390
- const [name, setName] = useState('');
391
-
392
- useEffect(() => {
393
- userService.getUserName().then(setName);
394
- }, []);
395
-
396
- return <span>{name}</span>;
397
- }
398
-
399
- // Advantages:
400
- // 1. ✅ Can test getUserName logic independently
401
- // 2. ✅ When logic becomes complex in future, only need to modify UserService
402
- // 3. ✅ Other components can reuse UserService
403
- // 4. ✅ UI component stays simple, only responsible for rendering
404
- ```
405
-
406
- **Key Scenario: Logic Gradually Becomes Complex**
407
-
408
- ```typescript
409
- // ❌ Not separated: Component becomes bloated when logic gets complex
410
- function UserName() {
411
- const [name, setName] = useState('');
412
- const [loading, setLoading] = useState(false);
413
- const [error, setError] = useState(null);
414
-
415
- useEffect(() => {
416
- setLoading(true);
417
-
418
- // 😰 Add cache
419
- const cached = localStorage.getItem('userName');
420
- if (cached) {
421
- setName(cached);
422
- setLoading(false);
423
- return;
424
- }
425
-
426
- // 😰 Add error handling
427
- fetch('/api/user')
428
- .then(res => {
429
- if (!res.ok) throw new Error('Failed');
430
- return res.json();
431
- })
432
- .then(data => {
433
- setName(data.name);
434
- localStorage.setItem('userName', data.name);
435
- })
436
- .catch(err => setError(err))
437
- .finally(() => setLoading(false));
438
- }, []);
439
-
440
- // 😰 Component becomes complex
441
- if (loading) return <span>Loading...</span>;
442
- if (error) return <span>Error</span>;
443
- return <span>{name}</span>;
444
- }
445
-
446
-
447
- // ✅ With separation: When logic becomes complex, only modify service
448
- @injectable()
449
- export class UserService implements UserServiceInterface {
450
- constructor(
451
- @inject(UserApi) private api: UserApi,
452
- @inject(IOCIdentifier.LocalStorageEncrypt) private storage: Storage
453
- ) {}
454
-
455
- // ✅ Logic in service, clear and straightforward
456
- async getUserName(): Promise<string> {
457
- // Cache logic
458
- const cached = this.storage.getItem('userName');
459
- if (cached) return cached;
460
-
461
- // API call
462
- const user = await this.api.getUserInfo();
463
-
464
- // Cache
465
- this.storage.setItem('userName', user.name);
466
-
467
- return user.name;
468
- }
469
- }
470
-
471
- // ✅ UI component stays simple
472
- function UserName() {
473
- const userService = useIOC('UserServiceInterface');
474
- const [name, setName] = useState('');
475
- const [loading, setLoading] = useState(false);
476
-
477
- useEffect(() => {
478
- setLoading(true);
479
- userService.getUserName()
480
- .then(setName)
481
- .finally(() => setLoading(false));
482
- }, []);
483
-
484
- if (loading) return <span>Loading...</span>;
485
- return <span>{name}</span>;
486
- }
487
- ```
488
-
489
- **Summary: Even if component is simple, still separate because:**
490
-
491
- - ✅ **Simple now doesn't mean simple later** - Requirements change
492
- - ✅ **Logic can be reused** - Other components may need it
493
- - ✅ **Easy to test** - Logic can be tested independently
494
- - ✅ **Clear responsibilities** - UI only renders, logic independent
495
- - ✅ **Easy to maintain** - Changing logic doesn't affect UI
496
-
497
- ---
498
-
499
- ## 🛠️ Implementation in the Project
500
-
501
- ### 1. File Structure
502
-
503
- ```
504
- src/
505
- ├── base/
506
- │ ├── port/ # Interface definition layer
507
- │ │ ├── UserServiceInterface.ts
508
- │ │ ├── I18nServiceInterface.ts
509
- │ │ └── RouteServiceInterface.ts
510
- │ └── services/ # Service implementation layer
511
- │ ├── UserService.ts
512
- │ ├── I18nService.ts
513
- │ └── RouteService.ts
514
- ├── core/
515
- │ ├── clientIoc/
516
- │ │ ├── ClientIOC.ts # IOC container
517
- │ │ └── ClientIOCRegister.ts # Registrar
518
- │ └── globals.ts # Global instances
519
- ├── uikit/
520
- │ ├── hooks/
521
- │ │ └── useIOC.ts # React Hook
522
- │ └── contexts/
523
- │ └── IOCContext.tsx # React Context
524
- └── config/
525
- └── IOCIdentifier.ts # Identifier definitions
526
-
527
- ```
528
-
529
- ### 2. IOC Identifier Definition
530
-
531
- ```typescript
532
- // config/IOCIdentifier.ts
533
- export interface IOCIdentifierMap {
534
- AppConfig: AppConfig;
535
- Logger: LoggerInterface;
536
- LocalStorageEncrypt: SyncStorageInterface<string, string>;
537
- UserServiceInterface: UserServiceInterface;
538
- I18nServiceInterface: I18nServiceInterface;
539
- RouteServiceInterface: RouteServiceInterface;
540
- }
541
-
542
- export const IOCIdentifier = {
543
- AppConfig: 'AppConfig',
544
- Logger: 'Logger',
545
- LocalStorageEncrypt: 'LocalStorageEncrypt',
546
- UserServiceInterface: 'UserServiceInterface',
547
- I18nServiceInterface: 'I18nServiceInterface',
548
- RouteServiceInterface: 'RouteServiceInterface'
549
- } as const;
550
- ```
551
-
552
- ### 3. Service Registration
553
-
554
- ```typescript
555
- // src/core/clientIoc/ClientIOCRegister.ts
556
- export class ClientIOCRegister implements IOCRegisterInterface {
557
- constructor(protected options: IocRegisterOptions) {}
558
-
559
- /**
560
- * Register global services
561
- */
562
- protected registerGlobals(ioc: IOCContainerInterface): void {
563
- const { appConfig } = this.options;
564
- const { dialogHandler, localStorageEncrypt, JSON, logger } = globals;
565
-
566
- // ✅ Register global instances
567
- ioc.bind(IOCIdentifier.JSONSerializer, JSON);
568
- ioc.bind(IOCIdentifier.Logger, logger);
569
- ioc.bind(IOCIdentifier.AppConfig, appConfig);
570
- ioc.bind(IOCIdentifier.LocalStorageEncrypt, localStorageEncrypt);
571
- }
572
-
573
- /**
574
- * Register business services
575
- */
576
- protected registerImplement(ioc: IOCContainerInterface): void {
577
- // ✅ Register service implementations
578
- ioc.bind(
579
- IOCIdentifier.I18nServiceInterface,
580
- new I18nService(this.options.pathname)
581
- );
582
-
583
- ioc.bind(IOCIdentifier.RouteServiceInterface, new RouteService(/* ... */));
584
-
585
- // ✅ Service can depend on other services
586
- ioc.bind(IOCIdentifier.UserServiceInterface, ioc.get(UserService));
587
- }
588
-
589
- /**
590
- * Registration entry point
591
- */
592
- register(ioc: IOCContainerInterface): void {
593
- this.registerGlobals(ioc);
594
- this.registerImplement(ioc);
595
- }
596
- }
597
- ```
598
-
599
- ### 4. Create IOC Container
600
-
601
- ```typescript
602
- // src/core/clientIoc/ClientIOC.ts
603
- import { createIOCFunction } from '@qlover/corekit-bridge';
604
- import { InversifyContainer } from '@/base/cases/InversifyContainer';
605
- import { ClientIOCRegister } from './ClientIOCRegister';
606
-
607
- export const clientIOC = {
608
- create(options: IocRegisterOptions) {
609
- // Create container
610
- const container = new InversifyContainer();
611
-
612
- // Create IOC function
613
- const IOC = createIOCFunction(container);
614
-
615
- // Register services
616
- const register = new ClientIOCRegister(options);
617
- register.register(container, IOC);
618
-
619
- return IOC;
620
- }
621
- };
622
- ```
623
-
624
- ### 5. Initialize in Bootstrap
625
-
626
- ```typescript
627
- // src/core/bootstraps/BootstrapClient.ts
628
- export class BootstrapClient {
629
- static async main(args: BootstrapClientArgs) {
630
- const { root, bootHref, ioc } = args;
631
-
632
- // ✅ Create IOC container
633
- const IOC = ioc.create({
634
- pathname: bootHref,
635
- appConfig: appConfig
636
- });
637
-
638
- // Use IOC in Bootstrap
639
- const bootstrap = new Bootstrap({
640
- root,
641
- logger,
642
- ioc: {
643
- manager: IOC,
644
- register: iocRegister
645
- }
646
- });
647
-
648
- await bootstrap.initialize();
649
- await bootstrap.start();
650
- }
651
- }
652
- ```
653
-
654
- ---
655
-
656
- ## 📝 How to Use
657
-
658
- ### 1. Define Interface (Port)
659
-
660
- ```typescript
661
- // src/base/port/UserServiceInterface.ts
662
- export interface UserServiceInterface {
663
- getUser(): Promise<UserInfo>;
664
- login(username: string, password: string): Promise<void>;
665
- logout(): Promise<void>;
666
- isAuthenticated(): boolean;
667
- }
668
- ```
669
-
670
- ### 2. Implement Service
671
-
672
- ```typescript
673
- // src/base/services/UserService.ts
674
- import { injectable, inject } from 'inversify';
675
-
676
- @injectable()
677
- export class UserService implements UserServiceInterface {
678
- constructor(
679
- @inject(UserApi) private api: UserApi,
680
- @inject(IOCIdentifier.AppConfig) private config: AppConfig,
681
- @inject(IOCIdentifier.LocalStorageEncrypt) private storage: Storage,
682
- @inject(IOCIdentifier.RouteServiceInterface) private router: RouteService
683
- ) {}
684
-
685
- async getUser(): Promise<UserInfo> {
686
- const token = this.storage.getItem(this.config.userTokenStorageKey);
687
- if (!token) throw new Error('No token');
688
-
689
- return await this.api.getUserInfo(token);
690
- }
691
-
692
- async login(username: string, password: string): Promise<void> {
693
- const response = await this.api.login({ username, password });
694
- this.storage.setItem(this.config.userTokenStorageKey, response.token);
695
- }
696
-
697
- async logout(): Promise<void> {
698
- this.storage.removeItem(this.config.userTokenStorageKey);
699
- await this.router.push('/login');
700
- }
701
-
702
- isAuthenticated(): boolean {
703
- return !!this.storage.getItem(this.config.userTokenStorageKey);
704
- }
705
- }
706
- ```
707
-
708
- ### 3. Use in UI Components
709
-
710
- ```typescript
711
- // src/pages/UserProfile.tsx
712
- import { useIOC } from '@/uikit/hooks/useIOC';
713
-
714
- function UserProfile() {
715
- // ✅ Get service from IOC container
716
- const userService = useIOC('UserServiceInterface');
717
- const [user, setUser] = useState<UserInfo | null>(null);
718
-
719
- useEffect(() => {
720
- userService.getUser().then(setUser);
721
- }, []);
722
-
723
- const handleLogout = () => {
724
- userService.logout();
725
- };
726
-
727
- // ✅ UI only responsible for rendering
728
- return (
729
- <div>
730
- <h1>{user?.name}</h1>
731
- <button onClick={handleLogout}>Logout</button>
732
- </div>
733
- );
734
- }
735
- ```
736
-
737
- ### 4. Use Other Services in Services
738
-
739
- ```typescript
740
- // src/base/services/ProfileService.ts
741
- @injectable()
742
- export class ProfileService {
743
- constructor(
744
- // ✅ Service can depend on other services
745
- @inject(IOCIdentifier.UserServiceInterface)
746
- private userService: UserServiceInterface,
747
- @inject(IOCIdentifier.I18nServiceInterface)
748
- private i18n: I18nServiceInterface
749
- ) {}
750
-
751
- async getUserProfile(): Promise<string> {
752
- const user = await this.userService.getUser();
753
- return this.i18n.t('profile.welcome', { name: user.name });
754
- }
755
- }
756
- ```
757
-
758
- ---
759
-
760
- ## 🧪 Testing
761
-
762
- ### Core Advantage: UI and logic can be tested independently, and also in combination
763
-
764
- #### 1. Test Logic Independently (No UI needed)
765
-
766
- ```typescript
767
- // __tests__/src/base/services/UserService.test.ts
768
- import { describe, it, expect, vi, beforeEach } from 'vitest';
769
- import { UserService } from '@/base/services/UserService';
770
-
771
- describe('UserService (Logic Test)', () => {
772
- let userService: UserService;
773
- let mockApi: any;
774
- let mockStorage: any;
775
- let mockRouter: any;
776
- let mockConfig: any;
777
-
778
- beforeEach(() => {
779
- // ✅ Only need to mock interfaces
780
- mockApi = {
781
- getUserInfo: vi.fn(),
782
- login: vi.fn()
783
- };
784
-
785
- mockStorage = {
786
- getItem: vi.fn(),
787
- setItem: vi.fn(),
788
- removeItem: vi.fn()
789
- };
790
-
791
- mockRouter = {
792
- push: vi.fn()
793
- };
794
-
795
- mockConfig = {
796
- userTokenStorageKey: '__test_token__'
797
- };
798
-
799
- // ✅ Create service
800
- userService = new UserService(mockApi, mockConfig, mockStorage, mockRouter);
801
- });
802
-
803
- it('should get user when token exists', async () => {
804
- // ✅ Set mock return value
805
- mockStorage.getItem.mockReturnValue('test-token');
806
- mockApi.getUserInfo.mockResolvedValue({ name: 'John' });
807
-
808
- // ✅ Test logic
809
- const user = await userService.getUser();
810
-
811
- // ✅ Verify result
812
- expect(user.name).toBe('John');
813
- expect(mockStorage.getItem).toHaveBeenCalledWith('__test_token__');
814
- expect(mockApi.getUserInfo).toHaveBeenCalledWith('test-token');
815
- });
816
-
817
- it('should throw error when no token', async () => {
818
- // ✅ Test error scenario
819
- mockStorage.getItem.mockReturnValue(null);
820
-
821
- await expect(userService.getUser()).rejects.toThrow('No token');
822
- });
823
-
824
- it('should login and save token', async () => {
825
- // ✅ Test login logic
826
- mockApi.login.mockResolvedValue({ token: 'new-token' });
827
-
828
- await userService.login('user', 'pass');
829
-
830
- expect(mockApi.login).toHaveBeenCalledWith({
831
- username: 'user',
832
- password: 'pass'
833
- });
834
- expect(mockStorage.setItem).toHaveBeenCalledWith(
835
- '__test_token__',
836
- 'new-token'
837
- );
838
- });
839
-
840
- it('should logout and clear token', async () => {
841
- // ✅ Test logout logic
842
- await userService.logout();
843
-
844
- expect(mockStorage.removeItem).toHaveBeenCalledWith('__test_token__');
845
- expect(mockRouter.push).toHaveBeenCalledWith('/login');
846
- });
847
- });
848
-
849
- // ✅✅✅ Advantages:
850
- // 1. Don't need to render UI
851
- // 2. Tests run fast (pure logic)
852
- // 3. Easy to mock (only need to mock interfaces)
853
- // 4. Can test all edge cases
854
- ```
855
-
856
- #### 2. Test UI Independently (No real logic needed)
857
-
858
- ```typescript
859
- // __tests__/src/pages/UserProfile.test.tsx
860
- import { describe, it, expect, vi } from 'vitest';
861
- import { render, screen, waitFor, fireEvent } from '@testing-library/react';
862
- import { UserProfile } from '@/pages/UserProfile';
863
- import { IOCProvider } from '@/uikit/contexts/IOCContext';
864
-
865
- describe('UserProfile (UI Test)', () => {
866
- it('should display user name', async () => {
867
- // ✅ Mock service
868
- const mockUserService = {
869
- getUser: vi.fn().mockResolvedValue({ name: 'John Doe' }),
870
- logout: vi.fn(),
871
- isAuthenticated: vi.fn().mockReturnValue(true)
872
- };
873
-
874
- const mockIOC = (identifier: string) => {
875
- if (identifier === 'UserServiceInterface') return mockUserService;
876
- };
877
-
878
- // ✅ Render component
879
- render(
880
- <IOCProvider value={mockIOC}>
881
- <UserProfile />
882
- </IOCProvider>
883
- );
884
-
885
- // ✅ Verify UI
886
- await waitFor(() => {
887
- expect(screen.getByText('John Doe')).toBeInTheDocument();
888
- });
889
- });
890
-
891
- it('should call logout when button clicked', async () => {
892
- const mockUserService = {
893
- getUser: vi.fn().mockResolvedValue({ name: 'John' }),
894
- logout: vi.fn(),
895
- isAuthenticated: vi.fn().mockReturnValue(true)
896
- };
897
-
898
- const mockIOC = (identifier: string) => {
899
- if (identifier === 'UserServiceInterface') return mockUserService;
900
- };
901
-
902
- render(
903
- <IOCProvider value={mockIOC}>
904
- <UserProfile />
905
- </IOCProvider>
906
- );
907
-
908
- // ✅ Simulate user action
909
- const logoutButton = screen.getByText('Logout');
910
- fireEvent.click(logoutButton);
911
-
912
- // ✅ Verify service call
913
- expect(mockUserService.logout).toHaveBeenCalled();
914
- });
915
- });
916
-
917
- // ✅✅✅ Advantages:
918
- // 1. Don't need real service implementation
919
- // 2. Can easily simulate various scenarios
920
- // 3. UI tests focus on UI logic
921
- ```
922
-
923
- #### 3. Combination Testing (UI + Logic)
924
-
925
- ```typescript
926
- // __tests__/src/integration/UserFlow.test.tsx
927
- import { describe, it, expect } from 'vitest';
928
- import { render, screen, waitFor, fireEvent } from '@testing-library/react';
929
- import { UserProfile } from '@/pages/UserProfile';
930
- import { UserService } from '@/base/services/UserService';
931
- import { IOCProvider } from '@/uikit/contexts/IOCContext';
932
-
933
- describe('User Flow (Integration Test)', () => {
934
- it('should complete user login flow', async () => {
935
- // ✅ Use real service implementation
936
- const mockApi = {
937
- getUserInfo: vi.fn().mockResolvedValue({ name: 'John' }),
938
- login: vi.fn().mockResolvedValue({ token: 'test-token' })
939
- };
940
-
941
- const mockStorage = {
942
- getItem: vi.fn(),
943
- setItem: vi.fn(),
944
- removeItem: vi.fn()
945
- };
946
-
947
- const mockRouter = { push: vi.fn() };
948
- const mockConfig = { userTokenStorageKey: '__token__' };
949
-
950
- // ✅ Create real service
951
- const userService = new UserService(
952
- mockApi,
953
- mockConfig,
954
- mockStorage,
955
- mockRouter
956
- );
957
-
958
- const mockIOC = (identifier: string) => {
959
- if (identifier === 'UserServiceInterface') return userService;
960
- };
961
-
962
- // ✅ Render real UI
963
- render(
964
- <IOCProvider value={mockIOC}>
965
- <UserProfile />
966
- </IOCProvider>
967
- );
968
-
969
- // ✅ Test complete flow
970
- await waitFor(() => {
971
- expect(screen.getByText('John')).toBeInTheDocument();
972
- });
973
-
974
- // ✅ Click logout
975
- fireEvent.click(screen.getByText('Logout'));
976
-
977
- // ✅ Verify entire flow
978
- expect(mockStorage.removeItem).toHaveBeenCalledWith('__token__');
979
- expect(mockRouter.push).toHaveBeenCalledWith('/login');
980
- });
981
- });
982
-
983
- // ✅✅✅ Advantages:
984
- // 1. Test real user flow
985
- // 2. Can discover UI and logic integration issues
986
- // 3. Closer to real usage scenarios
987
- ```
988
-
989
- ### Testing Strategy Summary
990
-
991
- ```
992
- ┌─────────────────────────────────────────┐
993
- │ Testing Pyramid │
994
- │ │
995
- │ △ UI Tests (Few) │
996
- │ ╱ ╲ │
997
- │ ╱ ╲ │
998
- │ ╱ ╲ │
999
- │ ╱───────╲ Integration Tests (Some) │
1000
- │ ╱ ╲ │
1001
- │╱═══════════╲ Logic Tests (Many) │
1002
- │ │
1003
- │ Logic Tests: Fast, stable, comprehensive│
1004
- │ Integration Tests: Verify integration │
1005
- │ UI Tests: Verify user interactions │
1006
- └─────────────────────────────────────────┘
1007
- ```
1008
-
1009
- **Recommended Test Ratio:**
1010
-
1011
- - 70% Logic tests (UserService.test.ts)
1012
- - 20% Integration tests (UserFlow.test.tsx)
1013
- - 10% UI tests (UserProfile.test.tsx)
1014
-
1015
- ---
1016
-
1017
- ## 💎 Best Practices
1018
-
1019
- ### 1. ✅ Always Define Interface
1020
-
1021
- ```typescript
1022
- // ✅ Good practice: Define interface first
1023
- export interface UserServiceInterface {
1024
- getUser(): Promise<UserInfo>;
1025
- logout(): Promise<void>;
1026
- }
1027
-
1028
- // Then implement
1029
- @injectable()
1030
- export class UserService implements UserServiceInterface {
1031
- // ...
1032
- }
1033
-
1034
- // ❌ Bad practice: Write implementation directly
1035
- @injectable()
1036
- export class UserService {
1037
- // No interface, hard to test
1038
- }
1039
- ```
1040
-
1041
- ### 2. ✅ Complete UI and Logic Separation
1042
-
1043
- ```typescript
1044
- // ✅ Good practice: UI only responsible for rendering
1045
- function UserProfile() {
1046
- const userService = useIOC('UserServiceInterface');
1047
- const [user, setUser] = useState(null);
1048
-
1049
- useEffect(() => {
1050
- userService.getUser().then(setUser);
1051
- }, []);
1052
-
1053
- return <div>{user?.name}</div>;
1054
- }
1055
-
1056
- // ❌ Bad practice: Logic mixed in UI
1057
- function UserProfile() {
1058
- const [user, setUser] = useState(null);
1059
-
1060
- useEffect(() => {
1061
- fetch('/api/user')
1062
- .then(res => res.json())
1063
- .then(setUser);
1064
- }, []);
1065
-
1066
- return <div>{user?.name}</div>;
1067
- }
1068
- ```
1069
-
1070
- ### 3. ✅ Use Dependency Injection
1071
-
1072
- ```typescript
1073
- // ✅ Good practice: Inject through constructor
1074
- @injectable()
1075
- export class UserService {
1076
- constructor(
1077
- @inject(UserApi) private api: UserApi,
1078
- @inject(IOCIdentifier.AppConfig) private config: AppConfig
1079
- ) {}
1080
- }
1081
-
1082
- // ❌ Bad practice: Create dependencies directly
1083
- export class UserService {
1084
- private api = new UserApi();
1085
- private config = new AppConfig();
1086
- }
1087
- ```
1088
-
1089
- ### 4. ✅ Single Responsibility for Services
1090
-
1091
- ```typescript
1092
- // ✅ Good practice: Each service responsible for one thing
1093
- @injectable()
1094
- export class UserService {
1095
- // Only responsible for user-related logic
1096
- async getUser() {
1097
- /* ... */
1098
- }
1099
- async logout() {
1100
- /* ... */
1101
- }
1102
- }
1103
-
1104
- @injectable()
1105
- export class ThemeService {
1106
- // Only responsible for theme-related logic
1107
- setTheme() {
1108
- /* ... */
1109
- }
1110
- getTheme() {
1111
- /* ... */
1112
- }
1113
- }
1114
-
1115
- // ❌ Bad practice: One service does multiple things
1116
- @injectable()
1117
- export class ApplicationService {
1118
- async getUser() {
1119
- /* ... */
1120
- }
1121
- setTheme() {
1122
- /* ... */
1123
- }
1124
- changeLanguage() {
1125
- /* ... */
1126
- }
1127
- // Too many responsibilities!
1128
- }
1129
- ```
1130
-
1131
- ### 5. ✅ Depend on Interfaces, Not Implementations
1132
-
1133
- ```typescript
1134
- // ✅ Good practice
1135
- @injectable()
1136
- export class UserService {
1137
- constructor(
1138
- @inject('UserApiInterface') private api: UserApiInterface // Interface
1139
- ) {}
1140
- }
1141
-
1142
- // ❌ Bad practice
1143
- @injectable()
1144
- export class UserService {
1145
- constructor(
1146
- @inject(UserApi) private api: UserApi // Concrete implementation
1147
- ) {}
1148
- }
1149
- ```
1150
-
1151
- ### 6. ✅ Separate Even If Simple
1152
-
1153
- ```typescript
1154
- // ✅ Good practice: Separate even if simple
1155
- @injectable()
1156
- export class CounterService {
1157
- private count = 0;
1158
-
1159
- increment() {
1160
- this.count++;
1161
- return this.count;
1162
- }
1163
- }
1164
-
1165
- function Counter() {
1166
- const counterService = useIOC('CounterService');
1167
- const [count, setCount] = useState(0);
1168
-
1169
- const handleClick = () => {
1170
- setCount(counterService.increment());
1171
- };
1172
-
1173
- return <button onClick={handleClick}>{count}</button>;
1174
- }
1175
-
1176
- // ❌ Bad practice: Simple logic also mixed in UI
1177
- function Counter() {
1178
- const [count, setCount] = useState(0);
1179
-
1180
- return (
1181
- <button onClick={() => setCount(count + 1)}>
1182
- {count}
1183
- </button>
1184
- );
1185
- }
1186
- ```
1187
-
1188
- ### 7. ✅ Write Comprehensive Tests
1189
-
1190
- ```typescript
1191
- // ✅ Good practice: Logic tests + UI tests + Integration tests
1192
- describe('UserService (Logic)', () => {
1193
- it('should get user', async () => {
1194
- /* ... */
1195
- });
1196
- it('should handle error', async () => {
1197
- /* ... */
1198
- });
1199
- });
1200
-
1201
- describe('UserProfile (UI)', () => {
1202
- it('should display user', async () => {
1203
- /* ... */
1204
- });
1205
- });
1206
-
1207
- describe('User Flow (Integration)', () => {
1208
- it('should complete flow', async () => {
1209
- /* ... */
1210
- });
1211
- });
1212
-
1213
- // ❌ Bad practice: Only UI tests
1214
- describe('UserProfile', () => {
1215
- it('should work', async () => {
1216
- // Only test UI, logic not tested
1217
- });
1218
- });
1219
- ```
1220
-
1221
- ### 8. ✅ Use Type-safe Identifiers
1222
-
1223
- ```typescript
1224
- // ✅ Good practice: Type-safe identifiers
1225
- const userService = useIOC('UserServiceInterface');
1226
- // TypeScript knows userService type
1227
-
1228
- // ❌ Bad practice: String literals
1229
- const userService = useIOC('UserService');
1230
- // Easy to misspell, no type checking
1231
- ```
1232
-
1233
- ---
1234
-
1235
- ## ❓ FAQ
1236
-
1237
- ### Q1: Does IOC increase complexity?
1238
-
1239
- **A:** Short-term maybe, but long-term it greatly reduces complexity:
1240
-
1241
- **Short-term (small projects):**
1242
-
1243
- - Need to define interfaces
1244
- - Need to register services
1245
- - Need to learn IOC concepts
1246
-
1247
- **Long-term (project grows):**
1248
-
1249
- - ✅ Easy to test (save lots of testing time)
1250
- - ✅ Easy to maintain (clear dependency relationships)
1251
- - ✅ Easy to extend (adding new features is simple)
1252
- - ✅ Team collaboration (clear responsibilities)
1253
-
1254
- ### Q2: Do all components need IOC?
1255
-
1256
- **A:** Not necessarily, but recommended:
1257
-
1258
- **Scenarios that need IOC:**
1259
-
1260
- - ✅ Components with business logic
1261
- - ✅ Components that call APIs
1262
- - ✅ Components that access Storage
1263
- - ✅ Components that need testing
1264
-
1265
- **Scenarios that can skip IOC:**
1266
-
1267
- - Pure presentational components (only receive props)
1268
- - Very simple UI components (like Button, Icon)
1269
-
1270
- ### Q3: Why not directly import service?
1271
-
1272
- **A:**
1273
-
1274
- ```typescript
1275
- // ❌ Direct import
1276
- import { userService } from '@/services/UserService';
1277
-
1278
- function UserProfile() {
1279
- // Problems:
1280
- // 1. userService is singleton, can't replace in tests
1281
- // 2. userService dependencies created at module load time
1282
- // 3. Hard to mock
1283
- }
1284
-
1285
- // ✅ Use IOC
1286
- function UserProfile() {
1287
- const userService = useIOC('UserServiceInterface');
1288
-
1289
- // Advantages:
1290
- // 1. Can provide mock implementation in tests
1291
- // 2. Dependencies managed by container, created on demand
1292
- // 3. Easy to mock
1293
- }
1294
- ```
1295
-
1296
- ### Q4: How to test components using IOC?
1297
-
1298
- **A:** Provide mock IOC:
1299
-
1300
- ```typescript
1301
- const mockIOC = (identifier: string) => {
1302
- if (identifier === 'UserServiceInterface') {
1303
- return mockUserService;
1304
- }
1305
- // ... other services
1306
- };
1307
-
1308
- render(
1309
- <IOCProvider value={mockIOC}>
1310
- <UserProfile />
1311
- </IOCProvider>
1312
- );
1313
- ```
1314
-
1315
- ### Q5: What's the difference between IOC and Context?
1316
-
1317
- **A:**
1318
-
1319
- | Feature | React Context | IOC Container |
1320
- | ------------------------- | ------------------------- | --------------------- |
1321
- | **Scope** | React component tree | Global |
1322
- | **Dependency Management** | ❌ None | ✅ Yes |
1323
- | **Lifecycle** | Component lifecycle | Application lifecycle |
1324
- | **Testing** | ⚠️ Need Provider | ✅ Easy to mock |
1325
- | **Type Safety** | ⚠️ Need manual definition | ✅ Auto-inference |
1326
-
1327
- **Recommendation:**
1328
-
1329
- - Use IOC to manage services (logic)
1330
- - Use Context to manage UI state
1331
-
1332
- ---
1333
-
1334
- ## 📚 Related Documentation
1335
-
1336
- - [Project Architecture Design](./index.md) - Understand overall architecture
1337
- - [Bootstrap Initializer](./bootstrap.md) - IOC in Bootstrap application
1338
- - [Environment Variable Management](./env.md) - AppConfig injection
1339
- - [Store State Management](./store.md) - How application layer notifies UI layer (IOC + Store)
1340
- - [Testing Guide](./test-guide.md) - Detailed testing strategies
1341
-
1342
- ---
1343
-
1344
- ## 🎉 Summary
1345
-
1346
- Core value of IOC container:
1347
-
1348
- 1. **UI Separation** 🎨 - UI is UI, logic is logic
1349
- 2. **Testability** 🧪 - Logic can be tested independently, UI can be tested independently, and also in combination
1350
- 3. **Interfaces Required** 🔌 - Even with only one implementation, still need interface (for testing)
1351
- 4. **Complete Separation** 🏗️ - Even simple components, still separate (for future)
1352
- 5. **Dependency Management** 📦 - Container uniformly manages all dependencies
1353
- 6. **Decoupling** 🔗 - Components don't depend on concrete implementations
1354
- 7. **Easy to Maintain** 🛠️ - Clear dependency relationships
1355
- 8. **Easy to Extend** 🚀 - Easy to add new features
1356
-
1357
- **Remember two core principles:**
1358
-
1359
- 1. **UI is UI, logic is logic, they must be separated!**
1360
- 2. **Even with only one implementation, still need interface; even if component is simple, still separate!**
1361
-
1362
- ---
1363
-
1364
- **Feedback:**
1365
- If you have any questions or suggestions about the IOC container, please discuss in the team channel or submit an Issue.