@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.
- package/CHANGELOG.md +9 -0
- package/package.json +1 -1
- package/templates/react-app/.env +23 -2
- package/templates/react-app/config/common.ts +3 -0
- package/templates/react-app/lib/bootstrap/Bootstrap.ts +36 -0
- package/templates/react-app/lib/bootstrap/BootstrapExecutorPlugin.ts +20 -0
- package/templates/react-app/lib/bootstrap/IOCContainerInterface.ts +33 -0
- package/templates/react-app/lib/bootstrap/IOCManagerInterface.ts +12 -0
- package/templates/react-app/lib/bootstrap/index.ts +7 -0
- package/templates/react-app/lib/bootstrap/plugins/InjectEnv.ts +61 -0
- package/templates/react-app/lib/bootstrap/plugins/InjectGlobal.ts +36 -0
- package/templates/react-app/lib/bootstrap/plugins/InjectIOC.ts +24 -0
- package/templates/react-app/lib/env-config/injectPkgConfig.ts +11 -0
- package/templates/react-app/lib/openAiApi/OpenAIClient.ts +1 -1
- package/templates/react-app/lib/router-loader/Page.ts +64 -0
- package/templates/react-app/lib/router-loader/RouterLoader.ts +90 -0
- package/templates/react-app/package.json +3 -1
- package/templates/react-app/src/App.tsx +27 -6
- package/templates/react-app/src/base/consts/IOCIdentifier.ts +16 -0
- package/templates/react-app/src/base/port/IOCFunctionInterface.ts +39 -0
- package/templates/react-app/src/base/port/InversifyIocInterface.ts +9 -0
- package/templates/react-app/src/base/port/StorageTokenInterface.ts +22 -0
- package/templates/react-app/src/base/types/Page.ts +5 -17
- package/templates/react-app/src/base/types/global.d.ts +2 -1
- package/templates/react-app/src/components/RouterRenderComponent.tsx +16 -0
- package/templates/react-app/src/components/ThemeSwitcher.tsx +1 -1
- package/templates/react-app/src/core/AppConfig.ts +27 -0
- package/templates/react-app/src/core/AppIOCContainer.ts +30 -0
- package/templates/react-app/src/core/IOC.ts +48 -0
- package/templates/react-app/src/core/bootstrap.ts +60 -15
- package/templates/react-app/src/core/globals.ts +1 -1
- package/templates/react-app/src/core/registers/RegisterApi.ts +59 -0
- package/templates/react-app/src/core/registers/RegisterCommon.ts +21 -0
- package/templates/react-app/src/core/{feIOC → registers}/RegisterControllers.ts +16 -11
- package/templates/react-app/src/core/registers/RegisterGlobals.ts +20 -0
- package/templates/react-app/src/core/registers/index.ts +17 -0
- package/templates/react-app/src/main.tsx +4 -3
- package/templates/react-app/src/pages/auth/Layout.tsx +1 -1
- package/templates/react-app/src/pages/auth/Login.tsx +4 -4
- package/templates/react-app/src/pages/base/Executor.tsx +1 -1
- package/templates/react-app/src/pages/base/JSONStorage.tsx +1 -1
- package/templates/react-app/src/pages/base/Request.tsx +1 -1
- package/templates/react-app/src/services/{i18n/index.ts → I18nService.ts} +25 -17
- package/templates/react-app/src/services/processer/ProcesserService.ts +2 -2
- package/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +7 -1
- package/templates/react-app/src/uikit/controllers/RouterController.ts +13 -16
- package/templates/react-app/src/uikit/hooks/useLanguageGuard.ts +1 -1
- package/templates/react-app/src/uikit/providers/ProcessProvider.tsx +2 -4
- package/templates/react-app/src/uikit/utils/RequestLogger.ts +4 -1
- package/templates/react-app/tsconfig.json +2 -1
- package/templates/react-app/tsconfig.node.json +6 -2
- package/templates/react-app/vite.config.ts +14 -0
- package/templates/react-app/config/app.common.ts +0 -52
- package/templates/react-app/src/base/port/IOCInterface.ts +0 -53
- package/templates/react-app/src/core/feIOC/FeIOC.ts +0 -32
- package/templates/react-app/src/core/feIOC/RegisterApi.ts +0 -41
- package/templates/react-app/src/core/feIOC/RegisterCommon.ts +0 -20
- package/templates/react-app/src/core/index.ts +0 -31
- package/templates/react-app/src/pages/index.tsx +0 -113
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
+
## [0.1.12](https://github.com/qlover/fe-base/compare/create-app-v0.1.11...create-app-v0.1.12) (2025-03-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add inversify ioc ([#270](https://github.com/qlover/fe-base/issues/270)) ([8c7ba06](https://github.com/qlover/fe-base/commit/8c7ba06bc5bef63d85c59a94737afac9be59138f))
|
|
9
|
+
|
|
10
|
+
## [0.1.11](https://github.com/qlover/fe-base/compare/create-app-v0.1.10...create-app-v0.1.11) (2025-03-12)
|
|
11
|
+
|
|
3
12
|
## [0.1.10](https://github.com/qlover/fe-base/compare/create-app-v0.1.9...create-app-v0.1.10) (2025-02-20)
|
|
4
13
|
|
|
5
14
|
## [0.1.9](https://github.com/qlover/fe-base/compare/create-app-v0.1.8...create-app-v0.1.9) (2025-02-20)
|
package/package.json
CHANGED
package/templates/react-app/.env
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
NODE_ENV=production
|
|
2
|
+
|
|
3
|
+
# ci
|
|
4
|
+
NPM_TOKEN=
|
|
5
|
+
GITHUB_TOKEN=
|
|
6
|
+
|
|
7
|
+
# fe-scripts
|
|
8
|
+
FE_RELEASE_BRANCH=master
|
|
9
|
+
FE_RELEASE=false
|
|
10
|
+
FE_RELEASE_ENV=production
|
|
11
|
+
|
|
12
|
+
# ===== build
|
|
13
|
+
VITE_PUBLIC_PATH=
|
|
1
14
|
VITE_SERVER_PORT=3200
|
|
2
|
-
|
|
3
|
-
|
|
15
|
+
|
|
16
|
+
# ===== app config
|
|
17
|
+
VITE_USER_TOKEN_STORAGE_KEY=fe_user_token
|
|
18
|
+
VITE_OPEN_AI_MODELS='["gpt-4o-mini","gpt-3.5-turbo","gpt-3.5-turbo-2","gpt-4","gpt-4-32k"]'
|
|
19
|
+
VITE_OPEN_AI_BASE_URL=https://api.openai.com/v1
|
|
20
|
+
VITE_OPEN_AI_TOKEN=sk-proj-1234567890
|
|
21
|
+
VITE_OPEN_AI_TOKEN_PREFIX=Bearer
|
|
22
|
+
VITE_OPEN_AI_REQUIRE_TOKEN=true
|
|
23
|
+
VITE_LOGIN_USER=admin
|
|
24
|
+
VITE_LOGIN_PASSWORD=123456
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { IOCContainerInterface } from './IOCContainerInterface';
|
|
2
|
+
import { SyncExecutor } from '@qlover/fe-utils';
|
|
3
|
+
import { BootstrapExecutorPlugin } from './BootstrapExecutorPlugin';
|
|
4
|
+
|
|
5
|
+
export class Bootstrap extends SyncExecutor {
|
|
6
|
+
constructor(private IOCContainer: IOCContainerInterface) {
|
|
7
|
+
super();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getIOCContainer(): IOCContainerInterface {
|
|
11
|
+
return this.IOCContainer;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
use(plugin: BootstrapExecutorPlugin | BootstrapExecutorPlugin[]): this {
|
|
15
|
+
if (Array.isArray(plugin)) {
|
|
16
|
+
plugin.forEach((p) => super.use(p));
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
super.use(plugin);
|
|
21
|
+
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
start(root: unknown): void {
|
|
26
|
+
this.exec({ root, ioc: this.IOCContainer }, () => {
|
|
27
|
+
// nothing to do
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
startNoError(root: unknown): void {
|
|
32
|
+
this.execNoError({ root, ioc: this.IOCContainer }, () => {
|
|
33
|
+
// nothing to do
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { IOCContainerInterface } from './IOCContainerInterface';
|
|
2
|
+
import { ExecutorContext, ExecutorPlugin } from '@qlover/fe-utils';
|
|
3
|
+
|
|
4
|
+
export type BootstrapArgs = {
|
|
5
|
+
/**
|
|
6
|
+
* starup global object
|
|
7
|
+
*
|
|
8
|
+
* maybe window or globalThis
|
|
9
|
+
*/
|
|
10
|
+
root: unknown;
|
|
11
|
+
/**
|
|
12
|
+
* IOC container
|
|
13
|
+
*/
|
|
14
|
+
ioc: IOCContainerInterface;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export interface BootstrapExecutorPlugin
|
|
18
|
+
extends ExecutorPlugin<BootstrapArgs> {}
|
|
19
|
+
|
|
20
|
+
export type BootstrapContext = ExecutorContext<BootstrapArgs>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IOC container
|
|
3
|
+
*
|
|
4
|
+
*/
|
|
5
|
+
export interface IOCContainerInterface {
|
|
6
|
+
/**
|
|
7
|
+
* configure IOC container
|
|
8
|
+
*
|
|
9
|
+
* eg. may need to manually bind implementation classes
|
|
10
|
+
*/
|
|
11
|
+
configure(): void;
|
|
12
|
+
configure<Container>(registers?: IOCRegisterInterface<Container>[]): void;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* bind instance
|
|
16
|
+
*
|
|
17
|
+
* @param serviceIdentifier
|
|
18
|
+
* @param value
|
|
19
|
+
*/
|
|
20
|
+
bind<T>(serviceIdentifier: unknown, value: T): void;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* get instance
|
|
24
|
+
*
|
|
25
|
+
* @param serviceIdentifier
|
|
26
|
+
* @returns
|
|
27
|
+
*/
|
|
28
|
+
get<T>(serviceIdentifier: unknown): T;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IOCRegisterInterface<T> {
|
|
32
|
+
register(container: T, thisArg: IOCContainerInterface): void;
|
|
33
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IOCContainerInterface } from './IOCContainerInterface';
|
|
2
|
+
|
|
3
|
+
export interface IOCManagerInterface {
|
|
4
|
+
get<T>(identifier: unknown): T;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* implement IOC container
|
|
8
|
+
*/
|
|
9
|
+
implement(container: IOCContainerInterface): void;
|
|
10
|
+
|
|
11
|
+
get implemention(): IOCContainerInterface | null;
|
|
12
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './Bootstrap';
|
|
2
|
+
export * from './BootstrapExecutorPlugin';
|
|
3
|
+
export * from './plugins/InjectIOC';
|
|
4
|
+
export * from './plugins/InjectEnv';
|
|
5
|
+
export * from './plugins/InjectGlobal';
|
|
6
|
+
export * from './IOCContainerInterface';
|
|
7
|
+
export * from './IOCManagerInterface';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { BootstrapExecutorPlugin } from '../BootstrapExecutorPlugin';
|
|
2
|
+
|
|
3
|
+
export interface EnvConfigInterface {
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class InjectEnv implements BootstrapExecutorPlugin {
|
|
9
|
+
readonly pluginName = 'InjectEnv';
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
private target: EnvConfigInterface,
|
|
13
|
+
private source: Record<string, unknown>,
|
|
14
|
+
private envPrefix: string = '',
|
|
15
|
+
private envWhiteList: string[] = ['env', 'userNodeEnv']
|
|
16
|
+
) {}
|
|
17
|
+
|
|
18
|
+
static isJSONString(value: string): boolean {
|
|
19
|
+
return (
|
|
20
|
+
typeof value === 'string' &&
|
|
21
|
+
value !== '' &&
|
|
22
|
+
(value === 'true' ||
|
|
23
|
+
value === 'false' ||
|
|
24
|
+
value.startsWith('{') ||
|
|
25
|
+
value.startsWith('['))
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
env<D>(key: string, defaultValue?: D): D {
|
|
30
|
+
// transform key to env key
|
|
31
|
+
const formattedKey = key.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase();
|
|
32
|
+
|
|
33
|
+
const envKey = `${this.envPrefix}${formattedKey}`;
|
|
34
|
+
const value = this.source[envKey];
|
|
35
|
+
// if it is a json string, parse it
|
|
36
|
+
if (typeof value === 'string' && InjectEnv.isJSONString(value)) {
|
|
37
|
+
return JSON.parse(value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (value ?? defaultValue) as D;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
inject(config: EnvConfigInterface): void {
|
|
44
|
+
for (const key in config) {
|
|
45
|
+
if (this.envWhiteList.includes(key)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const value = config[key as keyof typeof config];
|
|
50
|
+
|
|
51
|
+
config[key as keyof typeof config] = this.env(key, value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
onBefore(): void {
|
|
56
|
+
this.inject(this.target);
|
|
57
|
+
|
|
58
|
+
// transform readonly to writable
|
|
59
|
+
Object.freeze(this.target);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ExecutorContext } from '@qlover/fe-utils';
|
|
2
|
+
import type {
|
|
3
|
+
BootstrapArgs,
|
|
4
|
+
BootstrapExecutorPlugin
|
|
5
|
+
} from '../BootstrapExecutorPlugin';
|
|
6
|
+
|
|
7
|
+
export class InjectGlobal implements BootstrapExecutorPlugin {
|
|
8
|
+
readonly pluginName = 'InjectGlobal';
|
|
9
|
+
|
|
10
|
+
constructor(
|
|
11
|
+
private sources: Record<string, unknown>,
|
|
12
|
+
private target?: string | Record<string, unknown>
|
|
13
|
+
) {}
|
|
14
|
+
|
|
15
|
+
onBefore(context: ExecutorContext<BootstrapArgs>): void {
|
|
16
|
+
// if target is provided, inject globals to target
|
|
17
|
+
if (typeof this.target === 'string') {
|
|
18
|
+
Object.assign(context.parameters.root!, {
|
|
19
|
+
[this.target]: Object.freeze(Object.assign({}, this.sources))
|
|
20
|
+
});
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const target = this.target || context.parameters.root;
|
|
25
|
+
|
|
26
|
+
if (typeof target !== 'object' || target === null) {
|
|
27
|
+
throw new Error('target must be an object');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// inject globals to root
|
|
31
|
+
for (const key in this.sources) {
|
|
32
|
+
const element = this.sources[key];
|
|
33
|
+
Object.assign(target, { [key]: element });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { IOCRegisterInterface } from '../IOCContainerInterface';
|
|
2
|
+
import type { IOCManagerInterface } from '../IOCManagerInterface';
|
|
3
|
+
import type {
|
|
4
|
+
BootstrapContext,
|
|
5
|
+
BootstrapExecutorPlugin
|
|
6
|
+
} from '../BootstrapExecutorPlugin';
|
|
7
|
+
import { Container } from 'inversify';
|
|
8
|
+
|
|
9
|
+
export class InjectIOC implements BootstrapExecutorPlugin {
|
|
10
|
+
readonly pluginName = 'InjectIOC';
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
private IOC: IOCManagerInterface,
|
|
14
|
+
private registeres: IOCRegisterInterface<Container>[]
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
onBefore(context: BootstrapContext): void {
|
|
18
|
+
const { ioc } = context.parameters;
|
|
19
|
+
this.IOC.implement(ioc);
|
|
20
|
+
|
|
21
|
+
// maybe runtimes configure
|
|
22
|
+
ioc.configure(this.registeres);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { LazyExoticComponent, PropsWithChildren } from 'react';
|
|
2
|
+
import { RouteObject } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
import { TFunction } from 'i18next';
|
|
5
|
+
import { UseTranslationResponse } from 'react-i18next';
|
|
6
|
+
|
|
7
|
+
export interface BasePageProvider {
|
|
8
|
+
meta: RouteMeta;
|
|
9
|
+
i18n: UseTranslationResponse<string, string>;
|
|
10
|
+
t: TFunction<string, string>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type RouteCategory = 'main' | 'auth' | 'common';
|
|
14
|
+
|
|
15
|
+
export interface RouteMeta {
|
|
16
|
+
category?: RouteCategory;
|
|
17
|
+
/**
|
|
18
|
+
* from app.router.json
|
|
19
|
+
*/
|
|
20
|
+
title?: string;
|
|
21
|
+
icon?: string;
|
|
22
|
+
/**
|
|
23
|
+
* from app.router.json
|
|
24
|
+
*
|
|
25
|
+
* @default 'common'
|
|
26
|
+
*/
|
|
27
|
+
localNamespace?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type RouteType = RouteObject & {
|
|
31
|
+
meta?: RouteMeta;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type RouteConfig = {
|
|
35
|
+
routes: RouteType[];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type PagesMaps = Record<
|
|
39
|
+
string,
|
|
40
|
+
| (() => LazyExoticComponent<React.ComponentType<unknown>>)
|
|
41
|
+
| (() => React.ComponentType<unknown>)
|
|
42
|
+
>;
|
|
43
|
+
|
|
44
|
+
export type LoadProps = {
|
|
45
|
+
pagesMaps: PagesMaps;
|
|
46
|
+
componentPath: string;
|
|
47
|
+
route: RouteType;
|
|
48
|
+
Provider?: React.ComponentType<PropsWithChildren<RouteMeta>>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
type ComponentValue = React.ComponentType<unknown> | LazyExoticComponent<React.ComponentType<unknown>>;
|
|
53
|
+
|
|
54
|
+
export type ComponentMaps = {
|
|
55
|
+
/**
|
|
56
|
+
* key: ./xxx/bbb.(jsx,js,tsx,ts)
|
|
57
|
+
*/
|
|
58
|
+
[key: string]: ComponentValue | (() => Promise<ComponentValue>) | (() => ComponentValue);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// new RouterManager({
|
|
62
|
+
// routes: [],
|
|
63
|
+
// componentMaps:
|
|
64
|
+
// });
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { RouteObject } from 'react-router-dom';
|
|
2
|
+
import isString from 'lodash/isString';
|
|
3
|
+
import type { ComponentType, LazyExoticComponent, ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
type ComponentValue = Record<string, () => unknown>;
|
|
6
|
+
|
|
7
|
+
type RouteComponentType<T = unknown> =
|
|
8
|
+
| ComponentType<T>
|
|
9
|
+
| LazyExoticComponent<ComponentType<T>>;
|
|
10
|
+
|
|
11
|
+
export type RouteConfigValue = Omit<RouteObject, 'element' | 'children'> & {
|
|
12
|
+
/**
|
|
13
|
+
* 路径
|
|
14
|
+
*
|
|
15
|
+
* FIXME: support `ReactNode`
|
|
16
|
+
*/
|
|
17
|
+
element?: string;
|
|
18
|
+
children?: RouteConfigValue[];
|
|
19
|
+
meta?: Record<string, unknown>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type RouterLoaderRender = (
|
|
23
|
+
route: Omit<RouteConfigValue, 'element'> & {
|
|
24
|
+
element: () => RouteComponentType;
|
|
25
|
+
}
|
|
26
|
+
) => ReactNode;
|
|
27
|
+
|
|
28
|
+
export type RouterLoaderOptions = {
|
|
29
|
+
/**
|
|
30
|
+
* 路由配置
|
|
31
|
+
*/
|
|
32
|
+
routes?: RouteConfigValue[];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 组件映射表
|
|
36
|
+
*/
|
|
37
|
+
componentMaps?: ComponentValue;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 渲染路由
|
|
41
|
+
*/
|
|
42
|
+
render: RouterLoaderRender;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export class RouterLoader {
|
|
46
|
+
constructor(private readonly options: RouterLoaderOptions) {
|
|
47
|
+
if (!options.render) {
|
|
48
|
+
throw new Error('RouterLoader render is required');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getComponentMaps(): ComponentValue {
|
|
53
|
+
const { componentMaps = {} } = this.options;
|
|
54
|
+
|
|
55
|
+
return componentMaps;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getComponent(element: string): () => RouteComponentType {
|
|
59
|
+
const maps = this.getComponentMaps();
|
|
60
|
+
|
|
61
|
+
const component = maps[element];
|
|
62
|
+
|
|
63
|
+
if (!component) {
|
|
64
|
+
throw new Error(`Component not found: ${element}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return component as () => RouteComponentType;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
toRoute(route: RouteConfigValue): RouteObject {
|
|
71
|
+
const { render } = this.options;
|
|
72
|
+
const { element, children, ...rest } = route;
|
|
73
|
+
|
|
74
|
+
if (!element || !isString(element)) {
|
|
75
|
+
console.warn(
|
|
76
|
+
`Invalid route, path is: ${route.path}, element is: ${element}`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const componet = this.getComponent(element || '404');
|
|
81
|
+
const Element = render({ ...rest, element: componet });
|
|
82
|
+
|
|
83
|
+
// @ts-expect-error
|
|
84
|
+
return {
|
|
85
|
+
...rest,
|
|
86
|
+
element: Element,
|
|
87
|
+
children: children?.map((child) => this.toRoute(child))
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -56,11 +56,13 @@
|
|
|
56
56
|
"i18next": "^24.2.0",
|
|
57
57
|
"i18next-browser-languagedetector": "^8.0.2",
|
|
58
58
|
"i18next-http-backend": "^3.0.1",
|
|
59
|
+
"inversify": "^7.1.0",
|
|
59
60
|
"lodash": "^4.17.21",
|
|
60
61
|
"react": "^18.3.1",
|
|
61
62
|
"react-dom": "^18.3.1",
|
|
62
63
|
"react-i18next": "^15.2.0",
|
|
63
|
-
"react-router-dom": "^7.1.5"
|
|
64
|
+
"react-router-dom": "^7.1.5",
|
|
65
|
+
"reflect-metadata": "^0.2.2"
|
|
64
66
|
},
|
|
65
67
|
"devDependencies": {
|
|
66
68
|
"@eslint/js": "^9.11.1",
|
|
@@ -1,14 +1,35 @@
|
|
|
1
1
|
import '@/uikit/styles/css/index.css';
|
|
2
2
|
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
3
|
+
import { lazy, useMemo } from 'react';
|
|
4
|
+
import { RouterLoader } from '@lib/router-loader/RouterLoader';
|
|
5
|
+
import appConfig from '@config/app.router.json';
|
|
6
|
+
import { RouterRenderComponent } from './components/RouterRenderComponent';
|
|
7
|
+
import { PagesMaps } from './base/types/Page';
|
|
8
|
+
|
|
9
|
+
function getAllPages() {
|
|
10
|
+
const modules = import.meta.glob('./pages/**/*.tsx');
|
|
11
|
+
return Object.keys(modules).reduce((acc, path) => {
|
|
12
|
+
const componentName = path.replace(/^\.\/pages\/(.*)\.tsx$/, '$1');
|
|
13
|
+
acc[componentName] = () =>
|
|
14
|
+
lazy(
|
|
15
|
+
modules[path] as () => Promise<{
|
|
16
|
+
default: React.ComponentType<unknown>;
|
|
17
|
+
}>
|
|
18
|
+
);
|
|
19
|
+
return acc;
|
|
20
|
+
}, {} as PagesMaps);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const routerLoader = new RouterLoader({
|
|
24
|
+
componentMaps: getAllPages(),
|
|
25
|
+
render: RouterRenderComponent
|
|
26
|
+
});
|
|
7
27
|
|
|
8
28
|
function App() {
|
|
9
29
|
const routerBase = useMemo(() => {
|
|
10
|
-
const
|
|
11
|
-
|
|
30
|
+
const routes = appConfig.base.routes.map((route) =>
|
|
31
|
+
routerLoader.toRoute(route)
|
|
32
|
+
);
|
|
12
33
|
const router = createBrowserRouter(routes);
|
|
13
34
|
return router;
|
|
14
35
|
}, []);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IOC identifier
|
|
3
|
+
*
|
|
4
|
+
* @description
|
|
5
|
+
* IOC identifier is used to identify the service in the IOC container.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const a = IOC(IOCIdentifier.JSON);
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export const IOCIdentifier = Object.freeze({
|
|
13
|
+
JSON: 'JSON',
|
|
14
|
+
JSONStorage: 'JSONStorage',
|
|
15
|
+
Logger: 'Logger'
|
|
16
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { IOCManagerInterface } from '@lib/bootstrap';
|
|
2
|
+
import type { JSONSerializer, JSONStorage, Logger } from '@qlover/fe-utils';
|
|
3
|
+
import type { ServiceIdentifier } from 'inversify';
|
|
4
|
+
import type { IOCIdentifier } from '@/base/consts/IOCIdentifier';
|
|
5
|
+
|
|
6
|
+
export type IOCIdentifierMap = {
|
|
7
|
+
[IOCIdentifier.JSON]: JSONSerializer;
|
|
8
|
+
[IOCIdentifier.JSONStorage]: JSONStorage;
|
|
9
|
+
[IOCIdentifier.Logger]: Logger;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* IOC function
|
|
14
|
+
*
|
|
15
|
+
* eg.
|
|
16
|
+
* ```ts
|
|
17
|
+
* const a = IOC(A);
|
|
18
|
+
* const logger = IOC(ConstanstIdentifier.Logger);
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
*/
|
|
22
|
+
export interface IOCFunctionInterface extends IOCManagerInterface {
|
|
23
|
+
/**
|
|
24
|
+
* get constant identifier
|
|
25
|
+
*
|
|
26
|
+
* Preferred match for simple types
|
|
27
|
+
*/
|
|
28
|
+
<K extends keyof IOCIdentifierMap>(serviceIdentifier: K): IOCIdentifierMap[K];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* get service identifier
|
|
32
|
+
*/
|
|
33
|
+
<T>(serviceIdentifier: ServiceIdentifier<T>): T;
|
|
34
|
+
|
|
35
|
+
get<K extends keyof IOCIdentifierMap>(
|
|
36
|
+
serviceIdentifier: K
|
|
37
|
+
): IOCIdentifierMap[K];
|
|
38
|
+
get<T>(serviceIdentifier: ServiceIdentifier<T>): T;
|
|
39
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IOCRegisterInterface } from '@lib/bootstrap';
|
|
2
|
+
import type { Container } from 'inversify';
|
|
3
|
+
|
|
4
|
+
export type InversifyRegisterContainer = Container;
|
|
5
|
+
/**
|
|
6
|
+
* Inversify register interface.
|
|
7
|
+
*/
|
|
8
|
+
export interface InversifyRegisterInterface
|
|
9
|
+
extends IOCRegisterInterface<InversifyRegisterContainer> {}
|
|
@@ -1,5 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage token interface
|
|
3
|
+
*
|
|
4
|
+
* @description
|
|
5
|
+
* Storage token interface is used to store and retrieve token from the storage.
|
|
6
|
+
*/
|
|
1
7
|
export interface StorageTokenInterface {
|
|
8
|
+
/**
|
|
9
|
+
* Get token
|
|
10
|
+
*
|
|
11
|
+
* @returns {string} token
|
|
12
|
+
*/
|
|
2
13
|
getToken(): string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Set token
|
|
17
|
+
*
|
|
18
|
+
* @param {string} token
|
|
19
|
+
* @param {number} expireTime
|
|
20
|
+
*/
|
|
3
21
|
setToken(token: string, expireTime?: number): void;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Remove token
|
|
25
|
+
*/
|
|
4
26
|
removeToken(): void;
|
|
5
27
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { LazyExoticComponent, PropsWithChildren } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import { UseTranslationResponse } from 'react-i18next';
|
|
1
|
+
import type { LazyExoticComponent, PropsWithChildren } from 'react';
|
|
2
|
+
import type { TFunction } from 'i18next';
|
|
3
|
+
import type { UseTranslationResponse } from 'react-i18next';
|
|
4
|
+
import type { RouteConfigValue } from '@lib/router-loader/RouterLoader';
|
|
6
5
|
|
|
7
6
|
export interface BasePageProvider {
|
|
8
7
|
meta: RouteMeta;
|
|
@@ -27,12 +26,8 @@ export interface RouteMeta {
|
|
|
27
26
|
localNamespace?: string;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
export type RouteType = RouteObject & {
|
|
31
|
-
meta?: RouteMeta;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
29
|
export type RouteConfig = {
|
|
35
|
-
routes:
|
|
30
|
+
routes: RouteConfigValue[];
|
|
36
31
|
};
|
|
37
32
|
|
|
38
33
|
export type PagesMaps = Record<
|
|
@@ -40,10 +35,3 @@ export type PagesMaps = Record<
|
|
|
40
35
|
| (() => LazyExoticComponent<React.ComponentType<unknown>>)
|
|
41
36
|
| (() => React.ComponentType<unknown>)
|
|
42
37
|
>;
|
|
43
|
-
|
|
44
|
-
export type LoadProps = {
|
|
45
|
-
pagesMaps: PagesMaps;
|
|
46
|
-
componentPath: string;
|
|
47
|
-
route: RouteType;
|
|
48
|
-
Provider?: React.ComponentType<PropsWithChildren<RouteMeta>>;
|
|
49
|
-
};
|