@qlover/create-app 0.7.5 → 0.7.7

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 (166) hide show
  1. package/CHANGELOG.md +257 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/.env.template +22 -0
  5. package/dist/templates/next-app/.prettierignore +58 -0
  6. package/dist/templates/next-app/README.md +36 -0
  7. package/dist/templates/next-app/build/generateLocales.ts +25 -0
  8. package/dist/templates/next-app/config/IOCIdentifier.ts +45 -0
  9. package/dist/templates/next-app/config/Identifier/common.error.ts +34 -0
  10. package/dist/templates/next-app/config/Identifier/common.ts +62 -0
  11. package/dist/templates/next-app/config/Identifier/index.ts +10 -0
  12. package/dist/templates/next-app/config/Identifier/page.about.ts +181 -0
  13. package/dist/templates/next-app/config/Identifier/page.executor.ts +272 -0
  14. package/dist/templates/next-app/config/Identifier/page.home.ts +63 -0
  15. package/dist/templates/next-app/config/Identifier/page.identifiter.ts +39 -0
  16. package/dist/templates/next-app/config/Identifier/page.jsonStorage.ts +72 -0
  17. package/dist/templates/next-app/config/Identifier/page.login.ts +165 -0
  18. package/dist/templates/next-app/config/Identifier/page.register.ts +147 -0
  19. package/dist/templates/next-app/config/Identifier/page.request.ts +182 -0
  20. package/dist/templates/next-app/config/common.ts +34 -0
  21. package/dist/templates/next-app/config/i18n/PageI18nInterface.ts +51 -0
  22. package/dist/templates/next-app/config/i18n/i18nConfig.ts +12 -0
  23. package/dist/templates/next-app/config/i18n/index.ts +3 -0
  24. package/dist/templates/next-app/config/i18n/loginI18n.ts +42 -0
  25. package/dist/templates/next-app/config/theme.ts +23 -0
  26. package/dist/templates/next-app/docs/env.md +94 -0
  27. package/dist/templates/next-app/eslint.config.mjs +181 -0
  28. package/dist/templates/next-app/next.config.ts +21 -0
  29. package/dist/templates/next-app/package.json +58 -0
  30. package/dist/templates/next-app/plugins/eslint-plugin-testid.mjs +94 -0
  31. package/dist/templates/next-app/plugins/generateLocalesPlugin.ts +33 -0
  32. package/dist/templates/next-app/postcss.config.mjs +5 -0
  33. package/dist/templates/next-app/public/file.svg +1 -0
  34. package/dist/templates/next-app/public/globe.svg +1 -0
  35. package/dist/templates/next-app/public/locales/en/common.json +183 -0
  36. package/dist/templates/next-app/public/locales/zh/common.json +183 -0
  37. package/dist/templates/next-app/public/next.svg +1 -0
  38. package/dist/templates/next-app/public/vercel.svg +1 -0
  39. package/dist/templates/next-app/public/window.svg +1 -0
  40. package/dist/templates/next-app/src/app/[locale]/favicon.ico +0 -0
  41. package/dist/templates/next-app/src/app/[locale]/layout.tsx +44 -0
  42. package/dist/templates/next-app/src/app/[locale]/login/FeatureItem.tsx +13 -0
  43. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +115 -0
  44. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +73 -0
  45. package/dist/templates/next-app/src/app/[locale]/not-found.tsx +24 -0
  46. package/dist/templates/next-app/src/app/[locale]/page.tsx +106 -0
  47. package/dist/templates/next-app/src/base/cases/AppConfig.ts +15 -0
  48. package/dist/templates/next-app/src/base/cases/InversifyContainer.ts +33 -0
  49. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +25 -0
  50. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +11 -0
  51. package/dist/templates/next-app/src/base/services/I18nService.ts +115 -0
  52. package/dist/templates/next-app/src/base/services/UserService.ts +23 -0
  53. package/dist/templates/next-app/src/core/IOC.ts +58 -0
  54. package/dist/templates/next-app/src/core/IocRegisterImpl.ts +100 -0
  55. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +98 -0
  56. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +47 -0
  57. package/dist/templates/next-app/src/core/bootstraps/IocIdentifierTest.ts +26 -0
  58. package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +18 -0
  59. package/dist/templates/next-app/src/core/globals.ts +21 -0
  60. package/dist/templates/next-app/src/i18n/request.ts +22 -0
  61. package/dist/templates/next-app/src/i18n/routing.ts +30 -0
  62. package/dist/templates/next-app/src/middleware.ts +22 -0
  63. package/dist/templates/next-app/src/server/getServerI18n.ts +26 -0
  64. package/dist/templates/next-app/src/styles/css/antd-themes/_default.css +239 -0
  65. package/dist/templates/next-app/src/styles/css/antd-themes/dark.css +178 -0
  66. package/dist/templates/next-app/src/styles/css/antd-themes/index.css +3 -0
  67. package/dist/templates/next-app/src/styles/css/antd-themes/no-context.css +34 -0
  68. package/dist/templates/next-app/src/styles/css/antd-themes/pink.css +204 -0
  69. package/dist/templates/next-app/src/styles/css/index.css +6 -0
  70. package/dist/templates/next-app/src/styles/css/page.css +19 -0
  71. package/dist/templates/next-app/src/styles/css/tailwind.css +5 -0
  72. package/dist/templates/next-app/src/styles/css/themes/_default.css +29 -0
  73. package/dist/templates/next-app/src/styles/css/themes/dark.css +29 -0
  74. package/dist/templates/next-app/src/styles/css/themes/index.css +3 -0
  75. package/dist/templates/next-app/src/styles/css/themes/pink.css +29 -0
  76. package/dist/templates/next-app/src/styles/css/zIndex.css +9 -0
  77. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +42 -0
  78. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +25 -0
  79. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +45 -0
  80. package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +52 -0
  81. package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +51 -0
  82. package/dist/templates/next-app/src/uikit/components/NextIntlProvider.tsx +21 -0
  83. package/dist/templates/next-app/src/uikit/components/ThemeSwitcher.tsx +86 -0
  84. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +6 -0
  85. package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +28 -0
  86. package/dist/templates/next-app/src/uikit/hook/useIOC.ts +37 -0
  87. package/dist/templates/next-app/src/uikit/hook/useMountedClient.ts +11 -0
  88. package/dist/templates/next-app/src/uikit/hook/useStore.ts +15 -0
  89. package/dist/templates/next-app/tailwind.config.ts +8 -0
  90. package/dist/templates/next-app/tsconfig.json +36 -0
  91. package/dist/templates/react-app/.env.template +0 -2
  92. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +1 -1
  93. package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +6 -31
  94. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +1 -1
  95. package/dist/templates/react-app/config/IOCIdentifier.ts +77 -5
  96. package/dist/templates/react-app/config/app.router.ts +2 -2
  97. package/dist/templates/react-app/package.json +4 -7
  98. package/dist/templates/react-app/public/locales/en/common.json +1 -1
  99. package/dist/templates/react-app/public/locales/zh/common.json +1 -1
  100. package/dist/templates/react-app/src/App.tsx +9 -4
  101. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +1 -1
  102. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +4 -0
  103. package/dist/templates/react-app/src/base/cases/DialogHandler.ts +16 -13
  104. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +4 -3
  105. package/dist/templates/react-app/src/base/cases/InversifyContainer.ts +2 -2
  106. package/dist/templates/react-app/src/base/cases/RequestLanguages.ts +39 -0
  107. package/dist/templates/react-app/src/base/cases/RequestState.ts +20 -0
  108. package/dist/templates/react-app/src/base/cases/RequestStatusCatcher.ts +2 -2
  109. package/dist/templates/react-app/src/base/cases/RouterLoader.ts +8 -2
  110. package/dist/templates/react-app/src/base/port/AsyncStateInterface.ts +7 -0
  111. package/dist/templates/react-app/src/base/port/ExecutorPageBridgeInterface.ts +24 -0
  112. package/dist/templates/react-app/src/base/port/I18nServiceInterface.ts +10 -0
  113. package/dist/templates/react-app/src/base/port/JSONStoragePageBridgeInterface.ts +20 -0
  114. package/dist/templates/react-app/src/base/port/ProcesserExecutorInterface.ts +20 -0
  115. package/dist/templates/react-app/src/base/port/RequestPageBridgeInterface.ts +23 -0
  116. package/dist/templates/react-app/src/base/port/RequestStatusInterface.ts +5 -0
  117. package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +27 -0
  118. package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +12 -0
  119. package/dist/templates/react-app/src/base/services/I18nService.ts +10 -6
  120. package/dist/templates/react-app/src/base/services/ProcesserExecutor.ts +23 -5
  121. package/dist/templates/react-app/src/base/services/RouteService.ts +25 -54
  122. package/dist/templates/react-app/src/base/services/UserService.ts +10 -20
  123. package/dist/templates/react-app/src/core/IOC.ts +1 -26
  124. package/dist/templates/react-app/src/core/IocRegisterImpl.ts +125 -0
  125. package/dist/templates/react-app/src/core/bootstraps/BootstrapApp.ts +4 -6
  126. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +8 -6
  127. package/dist/templates/react-app/src/core/bootstraps/IocIdentifierTest.ts +26 -0
  128. package/dist/templates/react-app/src/pages/auth/Layout.tsx +2 -2
  129. package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +5 -6
  130. package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +8 -7
  131. package/dist/templates/react-app/src/pages/base/ExecutorPage.tsx +8 -19
  132. package/dist/templates/react-app/src/pages/base/{ErrorIdentifierPage.tsx → IdentifierPage.tsx} +1 -1
  133. package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +11 -15
  134. package/dist/templates/react-app/src/pages/base/Layout.tsx +1 -1
  135. package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +2 -2
  136. package/dist/templates/react-app/src/pages/base/RequestPage.tsx +34 -46
  137. package/dist/templates/react-app/src/styles/css/antd-themes/_default.css +2 -2
  138. package/dist/templates/react-app/src/styles/css/antd-themes/dark.css +2 -2
  139. package/dist/templates/react-app/src/styles/css/antd-themes/pink.css +2 -2
  140. package/dist/templates/react-app/src/styles/css/index.css +1 -0
  141. package/dist/templates/react-app/src/styles/css/page.css +8 -0
  142. package/dist/templates/react-app/src/styles/css/zIndex.css +9 -0
  143. package/dist/templates/react-app/src/uikit/{controllers/ExecutorController.ts → bridges/ExecutorPageBridge.ts} +13 -36
  144. package/dist/templates/react-app/src/uikit/bridges/JSONStoragePageBridge.ts +41 -0
  145. package/dist/templates/react-app/src/uikit/bridges/NavigateBridge.ts +16 -0
  146. package/dist/templates/react-app/src/uikit/bridges/RequestPageBridge.ts +136 -0
  147. package/dist/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +3 -2
  148. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +2 -2
  149. package/dist/templates/react-app/src/uikit/{providers → components}/ProcessExecutorProvider.tsx +4 -4
  150. package/dist/templates/react-app/src/uikit/components/RouterRenderComponent.tsx +1 -1
  151. package/dist/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +3 -5
  152. package/dist/templates/react-app/src/uikit/{providers → components}/UserAuthProvider.tsx +3 -3
  153. package/dist/templates/react-app/src/uikit/hooks/useI18nGuard.ts +5 -2
  154. package/dist/templates/react-app/src/uikit/hooks/{userRouterService.ts → useNavigateBridge.ts} +3 -3
  155. package/dist/templates/react-app/src/uikit/hooks/useStore.ts +5 -2
  156. package/dist/templates/react-app/tsconfig.json +1 -4
  157. package/package.json +1 -1
  158. package/dist/templates/react-app/src/base/port/InteractionHubInterface.ts +0 -94
  159. package/dist/templates/react-app/src/base/port/UIDependenciesInterface.ts +0 -37
  160. package/dist/templates/react-app/src/core/registers/IocRegisterImpl.ts +0 -25
  161. package/dist/templates/react-app/src/core/registers/RegisterCommon.ts +0 -74
  162. package/dist/templates/react-app/src/core/registers/RegisterControllers.ts +0 -26
  163. package/dist/templates/react-app/src/core/registers/RegisterGlobals.ts +0 -30
  164. package/dist/templates/react-app/src/uikit/controllers/JSONStorageController.ts +0 -49
  165. package/dist/templates/react-app/src/uikit/controllers/RequestController.ts +0 -158
  166. /package/dist/templates/react-app/src/uikit/{providers → components}/BaseRouteProvider.tsx +0 -0
@@ -0,0 +1,106 @@
1
+ import Image from 'next/image';
2
+
3
+ export default async function Home() {
4
+ return (
5
+ <div
6
+ data-testid="Home"
7
+ className="font-sans grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20"
8
+ >
9
+ <main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
10
+ <Image
11
+ className="dark:invert"
12
+ src="/next.svg"
13
+ alt="Next.js logo"
14
+ width={180}
15
+ height={38}
16
+ priority
17
+ />
18
+ <ol className="font-mono list-inside list-decimal text-sm/6 text-center sm:text-left">
19
+ <li className="mb-2 tracking-[-.01em]">
20
+ Get started by editing{' '}
21
+ <code className="bg-black/[.05] dark:bg-white/[.06] font-mono font-semibold px-1 py-0.5 rounded">
22
+ src/app/page.tsx
23
+ </code>
24
+ .
25
+ </li>
26
+ <li className="tracking-[-.01em]">
27
+ Save and see your changes instantly.
28
+ </li>
29
+ </ol>
30
+
31
+ <div className="flex gap-4 items-center flex-col sm:flex-row">
32
+ <a
33
+ className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
34
+ href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
35
+ target="_blank"
36
+ rel="noopener noreferrer"
37
+ >
38
+ <Image
39
+ className="dark:invert"
40
+ src="/vercel.svg"
41
+ alt="Vercel logomark"
42
+ width={20}
43
+ height={20}
44
+ />
45
+ Deploy now
46
+ </a>
47
+ <a
48
+ className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
49
+ href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
50
+ target="_blank"
51
+ rel="noopener noreferrer"
52
+ >
53
+ Read our docs
54
+ </a>
55
+ </div>
56
+ </main>
57
+ <footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
58
+ <a
59
+ className="flex items-center gap-2 hover:underline hover:underline-offset-4"
60
+ href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
61
+ target="_blank"
62
+ rel="noopener noreferrer"
63
+ >
64
+ <Image
65
+ aria-hidden
66
+ src="/file.svg"
67
+ alt="File icon"
68
+ width={16}
69
+ height={16}
70
+ />
71
+ Learn
72
+ </a>
73
+ <a
74
+ className="flex items-center gap-2 hover:underline hover:underline-offset-4"
75
+ href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
76
+ target="_blank"
77
+ rel="noopener noreferrer"
78
+ >
79
+ <Image
80
+ aria-hidden
81
+ src="/window.svg"
82
+ alt="Window icon"
83
+ width={16}
84
+ height={16}
85
+ />
86
+ Examples
87
+ </a>
88
+ <a
89
+ className="flex items-center gap-2 hover:underline hover:underline-offset-4"
90
+ href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
91
+ target="_blank"
92
+ rel="noopener noreferrer"
93
+ >
94
+ <Image
95
+ aria-hidden
96
+ src="/globe.svg"
97
+ alt="Globe icon"
98
+ width={16}
99
+ height={16}
100
+ />
101
+ Go to nextjs.org →
102
+ </a>
103
+ </footer>
104
+ </div>
105
+ );
106
+ }
@@ -0,0 +1,15 @@
1
+ import { name, version } from '../../../package.json';
2
+ import type { EnvConfigInterface } from '@qlover/corekit-bridge';
3
+
4
+ export class AppConfig implements EnvConfigInterface {
5
+ /**
6
+ * Current environment mode for Vite
7
+ * @description Represents the running environment (development, production, etc.)
8
+ * Automatically set based on the current .env file being used
9
+ */
10
+ readonly env: string = process.env.APP_ENV!;
11
+ readonly appName: string = name;
12
+ readonly appVersion: string = version;
13
+
14
+ readonly userTokenKey: string = '_user_token';
15
+ }
@@ -0,0 +1,33 @@
1
+ import { Container } from 'inversify';
2
+ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
3
+ import type {
4
+ IOCContainerInterface,
5
+ ServiceIdentifier
6
+ } from '@qlover/corekit-bridge';
7
+
8
+ export class InversifyContainer implements IOCContainerInterface {
9
+ protected container: Container;
10
+
11
+ constructor() {
12
+ this.container = new Container({
13
+ // allow `@injectable` decorator, auto bind injectable classes
14
+ autobind: true,
15
+ // use singleton scope
16
+ defaultScope: 'Singleton'
17
+ });
18
+ }
19
+
20
+ bind<T>(key: ServiceIdentifier<T>, value: T): void {
21
+ this.container.bind<T>(key).toConstantValue(value);
22
+ }
23
+
24
+ get<K extends keyof IOCIdentifierMap>(
25
+ serviceIdentifier: K
26
+ ): IOCIdentifierMap[K];
27
+ get<T>(serviceIdentifier: ServiceIdentifier<T>): T;
28
+ get<T, K extends keyof IOCIdentifierMap>(
29
+ serviceIdentifier: ServiceIdentifier<T> | K
30
+ ): T | IOCIdentifierMap[K] {
31
+ return this.container.get<T>(serviceIdentifier);
32
+ }
33
+ }
@@ -0,0 +1,25 @@
1
+ import {
2
+ StoreInterface,
3
+ type StoreStateInterface
4
+ } from '@qlover/corekit-bridge';
5
+ import type { i18nConfig } from '@config/i18n';
6
+
7
+ export type SupportedLocale = (typeof i18nConfig.supportedLngs)[number];
8
+ export type SupportedNamespace = typeof i18nConfig.fallbackLng;
9
+ export type I18nServiceLocale = SupportedLocale;
10
+
11
+ export class I18nServiceState implements StoreStateInterface {
12
+ loading: boolean = false;
13
+ constructor(public language: I18nServiceLocale) {}
14
+ }
15
+ export abstract class I18nServiceInterface
16
+ extends StoreInterface<I18nServiceState>
17
+ implements I18nServiceInterface
18
+ {
19
+ abstract t(key: string, params?: Record<string, unknown>): Promise<string>;
20
+ abstract changeLanguage(language: I18nServiceLocale): Promise<void>;
21
+ abstract changeLoading(loading: boolean): void;
22
+ abstract getCurrentLanguage(): Promise<I18nServiceLocale>;
23
+ abstract isValidLanguage(language: string): language is I18nServiceLocale;
24
+ abstract getSupportedLanguages(): I18nServiceLocale[];
25
+ }
@@ -0,0 +1,11 @@
1
+ import { ImagicaAuthService } from '@brain-toolkit/bridge';
2
+ import type { ExecutorPlugin } from '@qlover/fe-corekit';
3
+
4
+ export abstract class UserServiceInterface
5
+ extends ImagicaAuthService
6
+ implements ExecutorPlugin
7
+ {
8
+ readonly pluginName = 'UserService';
9
+
10
+ abstract getToken(): string | null;
11
+ }
@@ -0,0 +1,115 @@
1
+ import { useLocaleRoutes } from '@config/common';
2
+ import { i18nConfig } from '@config/i18n';
3
+ import {
4
+ I18nServiceInterface,
5
+ I18nServiceState
6
+ } from '../port/I18nServiceInterface';
7
+ import type { I18nServiceLocale } from '../port/I18nServiceInterface';
8
+ import type { useTranslations } from 'next-intl';
9
+
10
+ type TranslationFunction = ReturnType<typeof useTranslations>;
11
+
12
+ export class I18nService extends I18nServiceInterface {
13
+ readonly pluginName = 'I18nService';
14
+ private initialized: boolean = false;
15
+ private pathname: string = '';
16
+ private translator: TranslationFunction | null = null;
17
+
18
+ constructor() {
19
+ super(() => new I18nServiceState(i18nConfig.fallbackLng));
20
+ }
21
+
22
+ private async ensureInitialized(): Promise<void> {
23
+ if (!this.initialized) {
24
+ throw new Error('I18nService not initialized');
25
+ }
26
+ }
27
+
28
+ setPathname(pathname: string): void {
29
+ this.pathname = pathname;
30
+ }
31
+
32
+ setTranslator(translator: TranslationFunction): void {
33
+ this.translator = translator;
34
+ }
35
+
36
+ /**
37
+ * @override
38
+ */
39
+ async onBefore(): Promise<void> {
40
+ if (this.initialized) {
41
+ return;
42
+ }
43
+
44
+ this.initialized = true;
45
+
46
+ // 初始化语言状态
47
+ const currentLang = this.getCurrentLanguageFromPath();
48
+ if (this.isValidLanguage(currentLang)) {
49
+ this.emit({ ...this.state, language: currentLang });
50
+ }
51
+ }
52
+
53
+ private getCurrentLanguageFromPath(): string {
54
+ const paths = this.pathname.split('/');
55
+
56
+ for (const path of paths) {
57
+ if (this.isValidLanguage(path)) {
58
+ return path;
59
+ }
60
+ }
61
+
62
+ return i18nConfig.fallbackLng;
63
+ }
64
+
65
+ override async changeLanguage(language: I18nServiceLocale): Promise<void> {
66
+ try {
67
+ this.changeLoading(true);
68
+ await this.ensureInitialized();
69
+
70
+ // 在这里我们只需要更新状态,因为实际的语言切换会通过路由处理
71
+ this.emit({ ...this.state, language });
72
+
73
+ // 如果不使用本地化路由,则保存语言设置到本地存储
74
+ if (!useLocaleRoutes && typeof window !== 'undefined') {
75
+ window.localStorage.setItem('i18nextLng', language);
76
+ }
77
+ } finally {
78
+ this.changeLoading(false);
79
+ }
80
+ }
81
+
82
+ override changeLoading(loading: boolean): void {
83
+ this.emit({ ...this.state, loading });
84
+ }
85
+
86
+ override async getCurrentLanguage(): Promise<I18nServiceLocale> {
87
+ await this.ensureInitialized();
88
+ return this.getCurrentLanguageFromPath() as I18nServiceLocale;
89
+ }
90
+
91
+ override isValidLanguage(language: string): language is I18nServiceLocale {
92
+ return i18nConfig.supportedLngs.includes(language as I18nServiceLocale);
93
+ }
94
+
95
+ override getSupportedLanguages(): I18nServiceLocale[] {
96
+ return [...i18nConfig.supportedLngs];
97
+ }
98
+
99
+ override async t(
100
+ key: string,
101
+ params?: Record<string, string | number | Date>
102
+ ): Promise<string> {
103
+ await this.ensureInitialized();
104
+
105
+ if (!this.translator) {
106
+ return key;
107
+ }
108
+
109
+ try {
110
+ return this.translator(key, params);
111
+ } catch {
112
+ return key;
113
+ }
114
+ }
115
+ }
@@ -0,0 +1,23 @@
1
+ import { CookieStorage } from '@qlover/corekit-bridge';
2
+ import { injectable, inject } from 'inversify';
3
+ import { AppConfig } from '../cases/AppConfig';
4
+ import { UserServiceInterface } from '../port/UserServiceInterface';
5
+
6
+ @injectable()
7
+ export class UserService extends UserServiceInterface {
8
+ constructor(@inject(AppConfig) protected appConfig: AppConfig) {
9
+ super({
10
+ credentialStorage: {
11
+ key: appConfig.userTokenKey,
12
+ storage: new CookieStorage()
13
+ },
14
+ requestConfig: {
15
+ env: appConfig.env !== 'production' ? 'development' : 'production'
16
+ }
17
+ });
18
+ }
19
+
20
+ getToken(): string | null {
21
+ return this.store.getCredential();
22
+ }
23
+ }
@@ -0,0 +1,58 @@
1
+ // ! dont't import tsx, only ts file
2
+ import type { InversifyContainer } from '@/base/cases/InversifyContainer';
3
+ import { BootstrapClient } from './bootstraps/BootstrapClient';
4
+ import type { IOCRegisterInterface } from '@qlover/corekit-bridge';
5
+
6
+ /**
7
+ * IOC register options
8
+ */
9
+ export type IocRegisterOptions = {
10
+ /**
11
+ * The app config
12
+ */
13
+ appConfig: Record<string, unknown>;
14
+ };
15
+
16
+ /**
17
+ * IOC container
18
+ *
19
+ * This is a alias of IOCContainerInterface, use it without care about the implementation.
20
+ *
21
+ * Need to achieve the effect: when the implementation class on the right side of the equal sign changes, the IOCContainer will change automatically
22
+ */
23
+ export type IOCContainer = InversifyContainer;
24
+
25
+ /**
26
+ * IOC register interface.
27
+ *
28
+ * This is shortcut interface, implement this interface, you can use any IOC container.
29
+ *
30
+ * Need to achieve the effect: when the implementation class on the right side of the equal sign changes, the IOCContainer will change automatically
31
+ */
32
+ export type IOCRegister = IOCRegisterInterface<
33
+ IOCContainer,
34
+ IocRegisterOptions
35
+ >;
36
+
37
+ /**
38
+ * IOC function
39
+ *
40
+ * This is the only and main exported content of the file
41
+ *
42
+ * @example use A class
43
+ * ```ts
44
+ * const userService = IOC(UserService);
45
+ * ```
46
+ *
47
+ * @example use A string identifier
48
+ *
49
+ * string identifier is shortcut for `IOCIdentifierMap` type, string key of `IOCIdentifier`
50
+ *
51
+ * ```ts
52
+ * const logger = IOC('Logger'); // Logger instance
53
+ *
54
+ * // or
55
+ * const logger = IOC(IOCIdentifier.Logger);
56
+ * ```
57
+ */
58
+ export const IOC = BootstrapClient.createSingletonIOC();
@@ -0,0 +1,100 @@
1
+ import { IOCIdentifier as I } from '@config/IOCIdentifier';
2
+ import { I18nService } from '@/base/services/I18nService';
3
+ import { logger, JSON } from './globals';
4
+ import type { IOCContainer, IOCRegister, IocRegisterOptions } from './IOC';
5
+ import type { IOCManagerInterface } from '@qlover/corekit-bridge';
6
+
7
+ export class IocRegisterImpl implements IOCRegister {
8
+ constructor(protected options: IocRegisterOptions) {}
9
+
10
+ /**
11
+ * Register globals
12
+ *
13
+ * 一般用于注册全局
14
+ *
15
+ * @param ioc - IOC container
16
+ */
17
+ protected registerGlobals(ioc: IOCContainer): void {
18
+ const { appConfig } = this.options;
19
+ ioc.bind(I.JSONSerializer, JSON);
20
+ ioc.bind(I.Logger, logger);
21
+ ioc.bind(I.AppConfig, appConfig);
22
+ // ioc.bind(I.EnvConfigInterface, appConfig);
23
+ // ioc.bind(I.DialogHandler, dialogHandler);
24
+ // ioc.bind(I.UIDialogInterface, dialogHandler);
25
+ // ioc.bind(I.AntdStaticApiInterface, dialogHandler);
26
+ // ioc.bind(I.LocalStorage, globals.localStorage);
27
+ // ioc.bind(I.LocalStorageEncrypt, localStorageEncrypt);
28
+ // ioc.bind(I.CookieStorage, globals.cookieStorage);
29
+ }
30
+
31
+ /**
32
+ * 一般用于注册没有其他复杂依赖的项目实现类
33
+ *
34
+ * 比如:
35
+ * - 路由服务
36
+ * - 国际化服务
37
+ * - 主题服务
38
+ *
39
+ * @param ioc
40
+ */
41
+ protected registerImplement(ioc: IOCContainer): void {
42
+ ioc.bind(I.I18nServiceInterface, new I18nService());
43
+ // ioc.bind(
44
+ // I.RouteServiceInterface,
45
+ // new RouteService(
46
+ // ioc.get(NavigateBridge),
47
+ // ioc.get(I.I18nServiceInterface),
48
+ // {
49
+ // routes: useLocaleRoutes ? baseRoutes : baseNoLocaleRoutes,
50
+ // logger: ioc.get(I.Logger),
51
+ // hasLocalRoutes: useLocaleRoutes
52
+ // }
53
+ // )
54
+ // );
55
+ // ioc.bind(
56
+ // I.ThemeService,
57
+ // new ThemeService({
58
+ // ...themeConfig,
59
+ // storage: ioc.get<SyncStorageInterface<string, string>>(I.LocalStorage)
60
+ // })
61
+ // );
62
+ // ioc.bind(I.I18nKeyErrorPlugin, ioc.get(I18nKeyErrorPlugin));
63
+ // ioc.bind(I.ProcesserExecutorInterface, ioc.get(ProcesserExecutor));
64
+ // ioc.bind(I.UserServiceInterface, ioc.get(UserService));
65
+ // ioc.bind(I.RequestCatcherInterface, ioc.get(RequestStatusCatcher));
66
+ // ioc.bind(I.ExecutorPageBridgeInterface, ioc.get(ExecutorPageBridge));
67
+ // ioc.bind(I.JSONStoragePageInterface, ioc.get(JSONStoragePageBridge));
68
+ // ioc.bind(I.RequestPageBridgeInterface, ioc.get(RequestPageBridge));
69
+ }
70
+
71
+ protected registerCommon(_ioc: IOCContainer): void {
72
+ // const { appConfig } = this.options;
73
+ // const logger = ioc.get(I.Logger);
74
+ // const feApiRequestCommonPlugin = new RequestCommonPlugin({
75
+ // tokenPrefix: appConfig.openAiTokenPrefix,
76
+ // requiredToken: true,
77
+ // token: () => ioc.get(UserService).getToken()
78
+ // });
79
+ // const apiMockPlugin = new ApiMockPlugin(mockDataJson, logger);
80
+ // const apiCatchPlugin = new ApiCatchPlugin(
81
+ // logger,
82
+ // ioc.get(RequestStatusCatcher)
83
+ // );
84
+ // ioc.bind(I.FeApiCommonPlugin, feApiRequestCommonPlugin);
85
+ // ioc.bind(I.ApiMockPlugin, apiMockPlugin);
86
+ // ioc.bind(I.ApiCatchPlugin, apiCatchPlugin);
87
+ }
88
+
89
+ /**
90
+ * @override
91
+ */
92
+ register(
93
+ ioc: IOCContainer,
94
+ _manager: IOCManagerInterface<IOCContainer>
95
+ ): void {
96
+ this.registerGlobals(ioc);
97
+ this.registerCommon(ioc);
98
+ this.registerImplement(ioc);
99
+ }
100
+ }
@@ -0,0 +1,98 @@
1
+ import { Bootstrap, createIOCFunction } from '@qlover/corekit-bridge';
2
+ import { isObject } from 'lodash';
3
+ import { browserGlobalsName } from '@config/common';
4
+ import { InversifyContainer } from '@/base/cases/InversifyContainer';
5
+ import { BootstrapsRegistry } from './BootstrapsRegistry';
6
+ import * as globals from '../globals';
7
+ import { appConfig } from '../globals';
8
+ import { IocRegisterImpl } from '../IocRegisterImpl';
9
+ import type { IOCContainer } from '../IOC';
10
+ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
11
+ import type {
12
+ EnvConfigInterface,
13
+ IOCContainerInterface,
14
+ IOCFunctionInterface,
15
+ IOCManagerInterface
16
+ } from '@qlover/corekit-bridge';
17
+
18
+ export type BootstrapAppArgs = {
19
+ /**
20
+ * 启动的根节点,通常是window
21
+ */
22
+ root: unknown;
23
+ /**
24
+ * 当前路径
25
+ */
26
+ pathname: string;
27
+ /**
28
+ * IOC容器
29
+ */
30
+ IOC: IOCFunctionInterface<IOCIdentifierMap, IOCContainerInterface>;
31
+ };
32
+
33
+ export class BootstrapClient {
34
+ private static _ioc: IOCFunctionInterface<
35
+ IOCIdentifierMap,
36
+ IOCContainerInterface
37
+ > | null = null;
38
+
39
+ static createSingletonIOC(): IOCFunctionInterface<
40
+ IOCIdentifierMap,
41
+ IOCContainerInterface
42
+ > {
43
+ if (BootstrapClient._ioc) {
44
+ return BootstrapClient._ioc;
45
+ }
46
+
47
+ BootstrapClient._ioc = createIOCFunction<IOCIdentifierMap>(
48
+ /**
49
+ * If not inversify, you can use any IOC container,
50
+ * then replace the InversifyContainer with your own IOC container
51
+ */
52
+ new InversifyContainer()
53
+ );
54
+
55
+ new IocRegisterImpl({
56
+ appConfig: appConfig as EnvConfigInterface
57
+ }).register(
58
+ BootstrapClient._ioc.implemention as IOCContainer,
59
+ BootstrapClient._ioc as IOCManagerInterface<IOCContainer>
60
+ );
61
+
62
+ return BootstrapClient._ioc;
63
+ }
64
+
65
+ static async main(args: BootstrapAppArgs): Promise<BootstrapAppArgs> {
66
+ const { root, IOC } = args;
67
+
68
+ if (!isObject(root)) {
69
+ throw new Error('root is not an object');
70
+ }
71
+
72
+ const { logger, appConfig } = globals;
73
+
74
+ const bootstrap = new Bootstrap({
75
+ root,
76
+ logger,
77
+ ioc: {
78
+ manager: IOC
79
+ },
80
+ globalOptions: {
81
+ sources: globals,
82
+ target: browserGlobalsName
83
+ }
84
+ });
85
+
86
+ try {
87
+ await bootstrap.initialize();
88
+
89
+ const bootstrapsRegistry = new BootstrapsRegistry(args);
90
+
91
+ await bootstrap.use(bootstrapsRegistry.register()).start();
92
+ } catch (error) {
93
+ logger.error(`${appConfig.appName} starup error:`, error);
94
+ }
95
+
96
+ return args;
97
+ }
98
+ }
@@ -0,0 +1,47 @@
1
+ import { IOCIdentifier } from '@config/IOCIdentifier';
2
+ import { IocIdentifierTest } from './IocIdentifierTest';
3
+ import { printBootstrap } from './PrintBootstrap';
4
+ import type { BootstrapAppArgs } from './BootstrapClient';
5
+ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
6
+ import type {
7
+ BootstrapExecutorPlugin,
8
+ EnvConfigInterface,
9
+ IOCContainerInterface,
10
+ IOCFunctionInterface
11
+ } from '@qlover/corekit-bridge';
12
+
13
+ export class BootstrapsRegistry {
14
+ constructor(protected args: BootstrapAppArgs) {}
15
+
16
+ get IOC(): IOCFunctionInterface<IOCIdentifierMap, IOCContainerInterface> {
17
+ return this.args.IOC;
18
+ }
19
+
20
+ get appConfig(): EnvConfigInterface {
21
+ return this.IOC(IOCIdentifier.AppConfig);
22
+ }
23
+
24
+ register(): BootstrapExecutorPlugin[] {
25
+ const IOC = this.IOC;
26
+
27
+ const i18nService = IOC(IOCIdentifier.I18nServiceInterface);
28
+ i18nService.setPathname(this.args.pathname);
29
+
30
+ const bootstrapList: BootstrapExecutorPlugin[] = [
31
+ i18nService
32
+ // new UserApiBootstarp(),
33
+ // new FeApiBootstarp(),
34
+ // AiApiBootstarp,
35
+ // IOC(IOCIdentifier.I18nKeyErrorPlugin)
36
+ ];
37
+
38
+ if (!this.appConfig.isProduction) {
39
+ bootstrapList.push(printBootstrap);
40
+ }
41
+
42
+ bootstrapList.push(IocIdentifierTest);
43
+ // TODO: 需要使用到
44
+
45
+ return bootstrapList;
46
+ }
47
+ }
@@ -0,0 +1,26 @@
1
+ import { IOCIdentifier } from '@config/IOCIdentifier';
2
+ import type { BootstrapExecutorPlugin } from '@qlover/corekit-bridge';
3
+
4
+ export const IocIdentifierTest: BootstrapExecutorPlugin = {
5
+ pluginName: 'IocIdentifierTest',
6
+ onSuccess({ parameters: { logger, ioc } }) {
7
+ const errorList: string[] = [];
8
+ const keyList: string[] = Object.keys(IOCIdentifier);
9
+ keyList.forEach((key) => {
10
+ try {
11
+ const value = ioc.get(key);
12
+ if (value === undefined) {
13
+ errorList.push(key);
14
+ }
15
+ } catch {
16
+ errorList.push(key);
17
+ }
18
+ });
19
+
20
+ if (errorList.length > 0) {
21
+ logger.warn(`IOC ${errorList.join(', ')} is not found`);
22
+ } else {
23
+ logger.info(`IOC all identifiers are found ${keyList.length}`);
24
+ }
25
+ }
26
+ };
@@ -0,0 +1,18 @@
1
+ import { browserGlobalsName } from '@config/common';
2
+ import { AppConfig } from '@/base/cases/AppConfig';
3
+ import type { BootstrapExecutorPlugin } from '@qlover/corekit-bridge';
4
+
5
+ export const printBootstrap: BootstrapExecutorPlugin = {
6
+ pluginName: 'PrintBootstrap',
7
+ onSuccess({ parameters: { logger, ioc } }) {
8
+ const appConfig = ioc.get<AppConfig>(AppConfig);
9
+ logger.info(
10
+ 'bootstrap in(' +
11
+ appConfig.env +
12
+ ')success!\n\n' +
13
+ `You can use \`%cwindow.${browserGlobalsName}%c\` to access the globals`,
14
+ 'color: #0ff; font-weight: bold;',
15
+ 'all: unset;'
16
+ );
17
+ }
18
+ };