@qlover/create-app 0.1.10 → 0.1.12

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 (59) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/package.json +1 -1
  3. package/templates/react-app/.env +23 -2
  4. package/templates/react-app/config/common.ts +3 -0
  5. package/templates/react-app/lib/bootstrap/Bootstrap.ts +36 -0
  6. package/templates/react-app/lib/bootstrap/BootstrapExecutorPlugin.ts +20 -0
  7. package/templates/react-app/lib/bootstrap/IOCContainerInterface.ts +33 -0
  8. package/templates/react-app/lib/bootstrap/IOCManagerInterface.ts +12 -0
  9. package/templates/react-app/lib/bootstrap/index.ts +7 -0
  10. package/templates/react-app/lib/bootstrap/plugins/InjectEnv.ts +61 -0
  11. package/templates/react-app/lib/bootstrap/plugins/InjectGlobal.ts +36 -0
  12. package/templates/react-app/lib/bootstrap/plugins/InjectIOC.ts +24 -0
  13. package/templates/react-app/lib/env-config/injectPkgConfig.ts +11 -0
  14. package/templates/react-app/lib/openAiApi/OpenAIClient.ts +1 -1
  15. package/templates/react-app/lib/router-loader/Page.ts +64 -0
  16. package/templates/react-app/lib/router-loader/RouterLoader.ts +90 -0
  17. package/templates/react-app/package.json +3 -1
  18. package/templates/react-app/src/App.tsx +27 -6
  19. package/templates/react-app/src/base/consts/IOCIdentifier.ts +16 -0
  20. package/templates/react-app/src/base/port/IOCFunctionInterface.ts +39 -0
  21. package/templates/react-app/src/base/port/InversifyIocInterface.ts +9 -0
  22. package/templates/react-app/src/base/port/StorageTokenInterface.ts +22 -0
  23. package/templates/react-app/src/base/types/Page.ts +5 -17
  24. package/templates/react-app/src/base/types/global.d.ts +2 -1
  25. package/templates/react-app/src/components/RouterRenderComponent.tsx +16 -0
  26. package/templates/react-app/src/components/ThemeSwitcher.tsx +1 -1
  27. package/templates/react-app/src/core/AppConfig.ts +27 -0
  28. package/templates/react-app/src/core/AppIOCContainer.ts +30 -0
  29. package/templates/react-app/src/core/IOC.ts +48 -0
  30. package/templates/react-app/src/core/bootstrap.ts +60 -15
  31. package/templates/react-app/src/core/globals.ts +1 -1
  32. package/templates/react-app/src/core/registers/RegisterApi.ts +59 -0
  33. package/templates/react-app/src/core/registers/RegisterCommon.ts +21 -0
  34. package/templates/react-app/src/core/{feIOC → registers}/RegisterControllers.ts +16 -11
  35. package/templates/react-app/src/core/registers/RegisterGlobals.ts +20 -0
  36. package/templates/react-app/src/core/registers/index.ts +17 -0
  37. package/templates/react-app/src/main.tsx +4 -3
  38. package/templates/react-app/src/pages/auth/Layout.tsx +1 -1
  39. package/templates/react-app/src/pages/auth/Login.tsx +4 -4
  40. package/templates/react-app/src/pages/base/Executor.tsx +1 -1
  41. package/templates/react-app/src/pages/base/JSONStorage.tsx +1 -1
  42. package/templates/react-app/src/pages/base/Request.tsx +1 -1
  43. package/templates/react-app/src/services/{i18n/index.ts → I18nService.ts} +25 -17
  44. package/templates/react-app/src/services/processer/ProcesserService.ts +2 -2
  45. package/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +7 -1
  46. package/templates/react-app/src/uikit/controllers/RouterController.ts +13 -16
  47. package/templates/react-app/src/uikit/hooks/useLanguageGuard.ts +1 -1
  48. package/templates/react-app/src/uikit/providers/ProcessProvider.tsx +2 -4
  49. package/templates/react-app/src/uikit/utils/RequestLogger.ts +4 -1
  50. package/templates/react-app/tsconfig.json +2 -1
  51. package/templates/react-app/tsconfig.node.json +6 -2
  52. package/templates/react-app/vite.config.ts +14 -0
  53. package/templates/react-app/config/app.common.ts +0 -52
  54. package/templates/react-app/src/base/port/IOCInterface.ts +0 -53
  55. package/templates/react-app/src/core/feIOC/FeIOC.ts +0 -32
  56. package/templates/react-app/src/core/feIOC/RegisterApi.ts +0 -41
  57. package/templates/react-app/src/core/feIOC/RegisterCommon.ts +0 -20
  58. package/templates/react-app/src/core/index.ts +0 -31
  59. package/templates/react-app/src/pages/index.tsx +0 -113
@@ -1,16 +1,19 @@
1
+ import { IOCIdentifier } from '@/base/consts/IOCIdentifier';
1
2
  import {
2
3
  ExecutorPlugin,
3
4
  ExecutorContext,
4
5
  RequestAdapterFetchConfig,
5
6
  Logger
6
7
  } from '@qlover/fe-utils';
8
+ import { injectable, inject } from 'inversify';
7
9
 
10
+ @injectable()
8
11
  export class RequestLogger
9
12
  implements ExecutorPlugin<RequestAdapterFetchConfig>
10
13
  {
11
14
  readonly pluginName = 'RequestLogger';
12
15
 
13
- constructor(public logger: Logger) {}
16
+ constructor(@inject(IOCIdentifier.Logger) public logger: Logger) {}
14
17
 
15
18
  async onSuccess({
16
19
  parameters,
@@ -22,7 +22,8 @@
22
22
  "@/*": ["./src/*"],
23
23
  "@lib/*": ["./lib/*"],
24
24
  "@config/*": ["./config/*"]
25
- }
25
+ },
26
+ "experimentalDecorators": true
26
27
  },
27
28
  "include": ["src"],
28
29
  "references": [{ "path": "./tsconfig.node.json" }]
@@ -4,7 +4,11 @@
4
4
  "skipLibCheck": true,
5
5
  "module": "ESNext",
6
6
  "moduleResolution": "bundler",
7
- "allowSyntheticDefaultImports": true
7
+ "allowSyntheticDefaultImports": true,
8
+ "paths": {
9
+ "@lib/*": ["./lib/*"],
10
+ "@config/*": ["./config/*"]
11
+ }
8
12
  },
9
- "include": ["vite.config.ts"]
13
+ "include": ["vite.config.ts", "lib", "config"]
10
14
  }
@@ -4,11 +4,23 @@ import alias from '@rollup/plugin-alias';
4
4
  import tsappconfig from './tsconfig.json';
5
5
  import { resolve } from 'path';
6
6
  import { Env } from '@qlover/env-loader';
7
+ import { envPrefix } from './config/common';
8
+ import { name, version } from './package.json';
9
+ import { injectPkgConfig } from './lib/env-config/injectPkgConfig';
7
10
 
8
11
  Env.searchEnv();
9
12
 
10
13
  const tsAppPaths = tsappconfig.compilerOptions.paths || {};
11
14
 
15
+ // add version and name to env
16
+ injectPkgConfig(
17
+ [
18
+ ['APP_NAME', name],
19
+ ['APP_VERSION', version]
20
+ ],
21
+ envPrefix
22
+ );
23
+
12
24
  // convert tsconfig paths to vite alias
13
25
  const entries = Object.entries(tsAppPaths).reduce((acc, [key, value]) => {
14
26
  acc[key.replace('/*', '')] = resolve(__dirname, value[0].replace('/*', ''));
@@ -23,10 +35,12 @@ export default defineConfig({
23
35
  entries
24
36
  })
25
37
  ],
38
+ envPrefix: envPrefix,
26
39
  publicDir: 'public',
27
40
  server: {
28
41
  port: Number(process.env.VITE_SERVER_PORT || 3200)
29
42
  },
43
+ mode: process.env.NODE_ENV,
30
44
  test: {
31
45
  environment: 'jsdom',
32
46
  globals: true,
@@ -1,52 +0,0 @@
1
- import { i18nConfig } from '@config/i18n';
2
- import { OpenAIClientConfig } from '@lib/openAiApi';
3
- import { RequestCommonPluginConfig } from '@lib/request-common-plugin';
4
- import { RequestAdapterFetchConfig } from '@qlover/fe-utils';
5
-
6
- export const openAiModels = [
7
- 'gpt-3.5-turbo',
8
- 'gpt-3.5-turbo-2',
9
- 'gpt-4',
10
- 'gpt-4-32k'
11
- ];
12
-
13
- export const requestCommonPluginConfig: RequestCommonPluginConfig = {
14
- tokenPrefix: 'Bearer',
15
- defaultHeaders: {
16
- 'Content-Type': 'application/json'
17
- },
18
- defaultRequestData: {
19
- model: 'gpt-4o-mini',
20
- stream: true
21
- },
22
- requiredToken: true,
23
- token: import.meta.env.VITE_OPENAI_API_KEY
24
- };
25
-
26
- export const openAiConfig: OpenAIClientConfig = {
27
- baseURL: import.meta.env.VITE_OPENAI_API_URL,
28
- models: openAiModels,
29
- commonPluginConfig: requestCommonPluginConfig
30
- };
31
-
32
- export const defaultBaseRoutemeta = {
33
- localNamespace: i18nConfig.defaultNS,
34
- title: '',
35
- icon: ''
36
- } as const;
37
-
38
- export const defaultLoginInfo = {
39
- name: 'qlover',
40
- password: 'q1234566'
41
- };
42
-
43
- export const defaultFeApiConfig: {
44
- adapter: Partial<RequestAdapterFetchConfig>;
45
- commonPluginConfig: RequestCommonPluginConfig;
46
- } = {
47
- adapter: {
48
- responseType: 'json',
49
- baseURL: 'https://api.example.com/'
50
- },
51
- commonPluginConfig: requestCommonPluginConfig
52
- };
@@ -1,53 +0,0 @@
1
- export interface Abstract<T> {
2
- prototype: T;
3
- }
4
- export type Newable<T, Args extends unknown[] = unknown[]> = new (
5
- ...args: Args
6
- ) => T;
7
-
8
- export type ServiceIdentifier<T = unknown> =
9
- | string
10
- | symbol
11
- | Newable<T>
12
- | Abstract<T>;
13
-
14
- /**
15
- * IOC container
16
- *
17
- */
18
- export interface IOCInterface {
19
- /**
20
- * configure IOC container
21
- */
22
- configure(): void;
23
-
24
- /**
25
- * bind instance
26
- *
27
- * @param serviceIdentifier
28
- * @param value
29
- */
30
- bind<T>(serviceIdentifier: ServiceIdentifier<T>, value: T): void;
31
-
32
- /**
33
- * get instance
34
- *
35
- * @param serviceIdentifier
36
- * @returns
37
- */
38
- get<T>(serviceIdentifier: ServiceIdentifier<T>): T;
39
-
40
- /**
41
- * You can also use the override method to specify a replacement at runtime
42
- *
43
- * However, it may have performance issues because it creates an additional instance
44
- *
45
- * @param serviceIdentifier
46
- * @param value
47
- */
48
- override<T>(serviceIdentifier: ServiceIdentifier<T>, value: T): void;
49
- }
50
-
51
- export interface IOCRegisterInterface {
52
- register(container: IOCInterface): void;
53
- }
@@ -1,32 +0,0 @@
1
- import { IOCInterface } from '@/base/port/IOCInterface';
2
- import { RegisterApi } from './RegisterApi';
3
- import { RegisterControllers } from './RegisterControllers';
4
- import { RegisterCommon } from './RegisterCommon';
5
-
6
- export class FeIOC implements IOCInterface {
7
- private container: Map<string, unknown> = new Map();
8
-
9
- /**
10
- * order:
11
- * 1. RegisterCommon
12
- * 2. RegisterApi
13
- * 3. RegisterControllers
14
- */
15
- configure(): void {
16
- new RegisterCommon().register(this);
17
- new RegisterApi().register(this);
18
- new RegisterControllers().register(this);
19
- }
20
-
21
- bind<T>(key: string, value: T): void {
22
- this.container.set(key, value);
23
- }
24
-
25
- get<T>(key: string): T {
26
- return this.container.get(key) as T;
27
- }
28
-
29
- override<T>(key: string, value: T): void {
30
- this.bind(key, value);
31
- }
32
- }
@@ -1,41 +0,0 @@
1
- import { IOCInterface, IOCRegisterInterface } from '@/base/port/IOCInterface';
2
- import { RequestLogger } from '@/uikit/utils/RequestLogger';
3
- import { localJsonStorage } from '../globals';
4
- import { FetchAbortPlugin, FetchURLPlugin } from '@qlover/fe-utils';
5
- import { FeApiMockPlugin } from '@/base/apis/feApi';
6
- import { defaultFeApiConfig } from '@config/app.common';
7
- import { OpenAIClient } from '@lib/openAiApi';
8
- import { openAiConfig } from '@config/app.common';
9
- import { FeApi } from '@/base/apis/feApi';
10
- import { RequestCommonPlugin } from '@lib/request-common-plugin';
11
- import mockDataJson from '@config/feapi.mock.json';
12
-
13
- export class RegisterApi implements IOCRegisterInterface {
14
- register(container: IOCInterface): void {
15
- const openAiApi = new OpenAIClient({
16
- ...openAiConfig,
17
- commonPluginConfig: {
18
- ...openAiConfig.commonPluginConfig,
19
- requestDataSerializer: (data) => JSON.stringify(data)
20
- }
21
- }).usePlugin(container.get(RequestLogger));
22
-
23
- const feApi = new FeApi({
24
- abortPlugin: container.get(FetchAbortPlugin),
25
- config: defaultFeApiConfig.adapter
26
- })
27
- .usePlugin(new FetchURLPlugin())
28
- .usePlugin(
29
- new RequestCommonPlugin({
30
- ...defaultFeApiConfig.commonPluginConfig,
31
- token: () => localJsonStorage.getItem('fe_user_token')
32
- })
33
- )
34
- .usePlugin(new FeApiMockPlugin(mockDataJson))
35
- .usePlugin(container.get(RequestLogger))
36
- .usePlugin(container.get(FetchAbortPlugin));
37
-
38
- container.bind(OpenAIClient, openAiApi);
39
- container.bind(FeApi, feApi);
40
- }
41
- }
@@ -1,20 +0,0 @@
1
- import { IOCInterface, IOCRegisterInterface } from '@/base/port/IOCInterface';
2
- import { RequestLogger } from '@/uikit/utils/RequestLogger';
3
- import { localJsonStorage, logger } from '../globals';
4
- import { FetchAbortPlugin } from '@qlover/fe-utils';
5
- import { UserToken } from '@/base/cases/UserToken';
6
-
7
- export class RegisterCommon implements IOCRegisterInterface {
8
- register(container: IOCInterface): void {
9
- const requestLogger = new RequestLogger(logger);
10
- const feApiAbort = new FetchAbortPlugin();
11
- const userToken = new UserToken({
12
- storageKey: 'fe_user_token',
13
- storage: localJsonStorage
14
- });
15
-
16
- container.bind(RequestLogger, requestLogger);
17
- container.bind(FetchAbortPlugin, feApiAbort);
18
- container.bind(UserToken, userToken);
19
- }
20
- }
@@ -1,31 +0,0 @@
1
- // ! dont't import tsx, only ts file
2
- import { ServiceIdentifier, IOCInterface } from '@/base/port/IOCInterface';
3
-
4
- export type IOCFunction = {
5
- <T>(serviceIdentifier: ServiceIdentifier<T>): T;
6
- /**
7
- * IOC container instance
8
- */
9
- implemention: IOCInterface | null;
10
- /**
11
- * implement IOC container
12
- */
13
- implement(container: IOCInterface): void;
14
- };
15
-
16
- export const IOC: IOCFunction = Object.assign(
17
- function <T>(serviceIdentifier: ServiceIdentifier<T>): T {
18
- if (!IOC.implemention) {
19
- throw new Error('IOC is not implemented');
20
- }
21
- return IOC.implemention.get(serviceIdentifier);
22
- },
23
- {
24
- implemention: null,
25
- implement: (container: IOCInterface) => {
26
- // FIXME: maybe runtimes configure
27
- container.configure();
28
- IOC.implemention = container;
29
- }
30
- }
31
- );
@@ -1,113 +0,0 @@
1
- import { lazy, Suspense } from 'react';
2
- import isString from 'lodash/isString';
3
- import {
4
- LoadProps,
5
- PagesMaps,
6
- RouteCategory,
7
- RouteType
8
- } from '@/base/types/Page';
9
- import { Loading } from '@/components/Loading';
10
- import BaseRouteProvider from '../uikit/providers/BaseRouteProvider';
11
- import NotFound from './404';
12
- import NotFound500 from './500';
13
-
14
- const staticComponentsMaps: Record<string, () => React.ComponentType<unknown>> =
15
- {
16
- '404': () => NotFound as React.ComponentType<unknown>,
17
- '500': () => NotFound500 as React.ComponentType<unknown>
18
- };
19
-
20
- const getRealComponents = () => {
21
- return import.meta.glob('./*/**/*.tsx');
22
- };
23
-
24
- const getLazyComponentMaps = () => {
25
- const modules = getRealComponents();
26
-
27
- const pagesMaps: PagesMaps = {};
28
-
29
- for (const path in modules) {
30
- const componentName = path.replace(/^\.\/(.*)\.tsx$/, '$1');
31
-
32
- pagesMaps[componentName] = () =>
33
- lazy(
34
- modules[path] as () => Promise<{
35
- default: React.ComponentType<unknown>;
36
- }>
37
- );
38
- }
39
-
40
- return pagesMaps;
41
- };
42
-
43
- // 懒加载组件
44
- const lazyLoad = ({ pagesMaps, componentPath, route, Provider }: LoadProps) => {
45
- // first try static
46
- let loadedComponent = staticComponentsMaps[componentPath];
47
- if (!loadedComponent) {
48
- loadedComponent = pagesMaps[componentPath];
49
- }
50
-
51
- if (!loadedComponent) {
52
- console.warn(`Route ${componentPath} not found`);
53
- return <NotFound route={componentPath} />;
54
- }
55
-
56
- const Component = loadedComponent();
57
-
58
- return (
59
- <Suspense fallback={<Loading fullscreen />}>
60
- {Provider ? (
61
- <Provider {...route.meta}>
62
- <Component />
63
- </Provider>
64
- ) : (
65
- <Component />
66
- )}
67
- </Suspense>
68
- );
69
- };
70
-
71
- // 转换单个路由
72
- const transformRoute = (
73
- route: RouteType,
74
- options: Pick<LoadProps, 'pagesMaps' | 'Provider'>
75
- ): RouteType => {
76
- const result: RouteType = {
77
- ...route,
78
- path: route.path
79
- };
80
-
81
- if (isString(route.element)) {
82
- result.element = lazyLoad({
83
- ...options,
84
- componentPath: route.element,
85
- route
86
- });
87
- }
88
-
89
- if (route.children) {
90
- result.children = route.children.map((child) =>
91
- transformRoute(child, options)
92
- );
93
- }
94
-
95
- return result;
96
- };
97
-
98
- export function createFeReactRoutes(routes: RouteType[]) {
99
- const pagesMaps = getLazyComponentMaps();
100
-
101
- return routes.map((route) =>
102
- transformRoute(route, { pagesMaps, Provider: BaseRouteProvider })
103
- );
104
- }
105
-
106
- export function filterRoutesByCategory(
107
- routes: RouteType[],
108
- category: RouteCategory[]
109
- ): RouteType[] {
110
- return routes.filter((route) =>
111
- route.meta?.category ? category.includes(route.meta?.category) : true
112
- );
113
- }