@qlover/create-app 0.6.2 → 0.6.3

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 (37) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/react-app/README.en.md +257 -0
  5. package/dist/templates/react-app/README.md +29 -231
  6. package/dist/templates/react-app/docs/en/bootstrap.md +562 -0
  7. package/dist/templates/react-app/docs/en/development-guide.md +523 -0
  8. package/dist/templates/react-app/docs/en/env.md +482 -0
  9. package/dist/templates/react-app/docs/en/global.md +509 -0
  10. package/dist/templates/react-app/docs/en/i18n.md +268 -0
  11. package/dist/templates/react-app/docs/en/index.md +173 -0
  12. package/dist/templates/react-app/docs/en/ioc.md +424 -0
  13. package/dist/templates/react-app/docs/en/project-structure.md +434 -0
  14. package/dist/templates/react-app/docs/en/request.md +425 -0
  15. package/dist/templates/react-app/docs/en/router.md +404 -0
  16. package/dist/templates/react-app/docs/en/store.md +321 -0
  17. package/dist/templates/react-app/docs/en/theme.md +424 -0
  18. package/dist/templates/react-app/docs/en/typescript-guide.md +473 -0
  19. package/dist/templates/react-app/docs/zh/bootstrap.md +7 -0
  20. package/dist/templates/react-app/docs/zh/development-guide.md +523 -0
  21. package/dist/templates/react-app/docs/zh/env.md +24 -25
  22. package/dist/templates/react-app/docs/zh/global.md +28 -27
  23. package/dist/templates/react-app/docs/zh/i18n.md +268 -0
  24. package/dist/templates/react-app/docs/zh/index.md +173 -0
  25. package/dist/templates/react-app/docs/zh/ioc.md +44 -32
  26. package/dist/templates/react-app/docs/zh/project-structure.md +434 -0
  27. package/dist/templates/react-app/docs/zh/request.md +429 -0
  28. package/dist/templates/react-app/docs/zh/router.md +408 -0
  29. package/dist/templates/react-app/docs/zh/store.md +321 -0
  30. package/dist/templates/react-app/docs/zh/theme.md +424 -0
  31. package/dist/templates/react-app/docs/zh/typescript-guide.md +473 -0
  32. package/dist/templates/react-app/package.json +1 -1
  33. package/dist/templates/react-app/src/styles/css/antd-themes/dark.css +3 -1
  34. package/dist/templates/react-app/src/styles/css/antd-themes/index.css +1 -1
  35. package/dist/templates/react-app/src/styles/css/antd-themes/pink.css +6 -1
  36. package/dist/templates/react-app/src/styles/css/page.css +1 -1
  37. package/package.json +1 -1
@@ -0,0 +1,424 @@
1
+ # IOC Container
2
+
3
+ ## What is IOC?
4
+
5
+ IOC (Inversion of Control) is a design pattern that hands over the creation of objects and management of their dependencies to a container, rather than creating objects directly in the code.
6
+
7
+ **In simple terms**: Like a factory producing products, you don't need to know how products are manufactured; you just tell the factory what product you need, and the factory will provide it.
8
+
9
+ ## IOC Implementation in Project
10
+
11
+ ### Core Technology Stack
12
+
13
+ - **InversifyJS**: As the IOC container implementation (you can also manually implement your own container)
14
+ - **TypeScript**: Provides type safety
15
+ - **Decorator Pattern**: Uses `@injectable()` and `@inject()` decorators
16
+
17
+ ### Core File Structure
18
+
19
+ ```
20
+ config/
21
+ ├── IOCIdentifier.ts # IOC identifier definitions
22
+ src/
23
+ ├── core/
24
+ │ ├── IOC.ts # IOC main entry
25
+ │ ├── registers/ # Registers directory
26
+ │ │ ├── RegisterGlobals.ts # Global service registration
27
+ │ │ ├── RegisterCommon.ts # Common service registration
28
+ │ │ └── RegisterControllers.ts # Controller registration
29
+ │ └── globals.ts # Global instances
30
+ ├── base/
31
+ │ └── cases/
32
+ │ └── InversifyContainer.ts # Inversify container implementation
33
+ ```
34
+
35
+ ## Basic Concepts
36
+
37
+ ### 1. IOC Identifiers
38
+
39
+ ```tsx
40
+ // config/IOCIdentifier.ts
41
+ export const IOCIdentifier = Object.freeze({
42
+ JSON: 'JSON',
43
+ LocalStorage: 'LocalStorage',
44
+ Logger: 'Logger',
45
+ AppConfig: 'AppConfig'
46
+ // ... more identifiers
47
+ });
48
+ ```
49
+
50
+ ### 2. IOC Identifier Mapping
51
+
52
+ ```tsx
53
+ // core/IOC.ts
54
+ export interface IOCIdentifierMap {
55
+ [IOCIdentifier.JSON]: import('@qlover/fe-corekit').JSONSerializer;
56
+ [IOCIdentifier.LocalStorage]: import('@qlover/fe-corekit').ObjectStorage<
57
+ string,
58
+ string
59
+ >;
60
+ [IOCIdentifier.Logger]: import('@qlover/logger').LoggerInterface;
61
+ [IOCIdentifier.AppConfig]: import('@qlover/corekit-bridge').EnvConfigInterface;
62
+ // ... more mappings
63
+ }
64
+ ```
65
+
66
+ ### 3. IOC Function
67
+
68
+ ```tsx
69
+ // core/IOC.ts
70
+ export const IOC = createIOCFunction<IOCIdentifierMap>(
71
+ new InversifyContainer()
72
+ );
73
+ ```
74
+
75
+ ## Usage
76
+
77
+ ### 1. Getting Service Instances
78
+
79
+ ```tsx
80
+ // Using class name
81
+ const userService = IOC(UserService);
82
+
83
+ // Using string identifier
84
+ const logger = IOC('Logger');
85
+ // Or
86
+ const logger = IOC(IOCIdentifier.Logger);
87
+
88
+ // Using AppConfig
89
+ const appConfig = IOC(IOCIdentifier.AppConfig);
90
+ ```
91
+
92
+ ### 2. Using Dependency Injection in Services
93
+
94
+ ```tsx
95
+ import { inject, injectable } from 'inversify';
96
+ import { UserApi } from '@/base/apis/userApi/UserApi';
97
+ import { RouteService } from './RouteService';
98
+ import { IOCIdentifier } from '@config/IOCIdentifier';
99
+
100
+ @injectable()
101
+ export class UserService extends UserAuthService<UserInfo> {
102
+ constructor(
103
+ @inject(RouteService) protected routerService: RouteService,
104
+ @inject(UserApi) userApi: UserAuthApiInterface<UserInfo>,
105
+ @inject(IOCIdentifier.AppConfig) appConfig: AppConfig,
106
+ @inject(IOCIdentifier.LocalStorageEncrypt)
107
+ storage: SyncStorageInterface<string, string>
108
+ ) {
109
+ super(userApi, {
110
+ userStorage: {
111
+ key: appConfig.userInfoStorageKey,
112
+ storage: storage
113
+ },
114
+ credentialStorage: {
115
+ key: appConfig.userTokenStorageKey,
116
+ storage: storage
117
+ }
118
+ });
119
+ }
120
+ }
121
+ ```
122
+
123
+ ### 3. Using in Bootstrap
124
+
125
+ ```tsx
126
+ // Register services in bootstrapper
127
+ bootstrap.use([
128
+ IOC(UserService), // User service
129
+ IOC(I18nService), // Internationalization service
130
+ new UserApiBootstarp() // API configuration
131
+ ]);
132
+ ```
133
+
134
+ ## Service Registration
135
+
136
+ ### 1. Global Service Registration
137
+
138
+ ```tsx
139
+ // core/registers/RegisterGlobals.ts
140
+ export const RegisterGlobals: IOCRegister = {
141
+ register(container, _, options): void {
142
+ // Register application configuration
143
+ container.bind(IOCIdentifier.AppConfig, options!.appConfig);
144
+
145
+ // Register logging service
146
+ container.bind(Logger, logger);
147
+ container.bind(IOCIdentifier.Logger, logger);
148
+
149
+ // Register storage services
150
+ container.bind(IOCIdentifier.LocalStorage, localStorage);
151
+ container.bind(IOCIdentifier.LocalStorageEncrypt, localStorageEncrypt);
152
+ container.bind(IOCIdentifier.CookieStorage, cookieStorage);
153
+ }
154
+ };
155
+ ```
156
+
157
+ ### 2. Common Service Registration
158
+
159
+ ```tsx
160
+ // core/registers/RegisterCommon.ts
161
+ export const RegisterCommon: IOCRegister = {
162
+ register(container, _, options): void {
163
+ const AppConfig = container.get(IOCIdentifier.AppConfig);
164
+
165
+ // Register API-related services
166
+ const feApiToken = new TokenStorage(AppConfig.userTokenStorageKey, {
167
+ storage: container.get(IOCIdentifier.LocalStorageEncrypt)
168
+ });
169
+
170
+ container.bind(IOCIdentifier.FeApiToken, feApiToken);
171
+ container.bind(IOCIdentifier.FeApiCommonPlugin, feApiRequestCommonPlugin);
172
+
173
+ // Register theme service
174
+ container.bind(
175
+ ThemeService,
176
+ new ThemeService({
177
+ ...themeConfig,
178
+ storage: localStorage
179
+ })
180
+ );
181
+
182
+ // Register router service
183
+ container.bind(
184
+ RouteService,
185
+ new RouteService({
186
+ routes: baseRoutes,
187
+ logger
188
+ })
189
+ );
190
+
191
+ // Register i18n service
192
+ container.bind(I18nService, new I18nService(options!.pathname));
193
+ }
194
+ };
195
+ ```
196
+
197
+ ### 3. Controller Registration
198
+
199
+ ```tsx
200
+ // core/registers/RegisterControllers.ts
201
+ export class RegisterControllers implements IOCRegister {
202
+ register(
203
+ container: IOCContainer,
204
+ _: IOCManagerInterface<IOCContainer>
205
+ ): void {
206
+ // Register controllers
207
+ const jsonStorageController = new JSONStorageController(localStorage);
208
+ container.bind(JSONStorageController, jsonStorageController);
209
+
210
+ // Configure processor
211
+ container
212
+ .get(ProcesserExecutor)
213
+ .use(container.get(I18nKeyErrorPlugin))
214
+ .use(container.get(UserService));
215
+ }
216
+ }
217
+ ```
218
+
219
+ ## Practical Application Scenarios
220
+
221
+ ### 1. User Authentication Service
222
+
223
+ ```tsx
224
+ @injectable()
225
+ export class UserService extends UserAuthService<UserInfo> {
226
+ constructor(
227
+ @inject(RouteService) protected routerService: RouteService,
228
+ @inject(UserApi) userApi: UserAuthApiInterface<UserInfo>,
229
+ @inject(IOCIdentifier.AppConfig) appConfig: AppConfig,
230
+ @inject(IOCIdentifier.LocalStorageEncrypt)
231
+ storage: SyncStorageInterface<string, string>
232
+ ) {
233
+ super(userApi, {
234
+ userStorage: {
235
+ key: appConfig.userInfoStorageKey,
236
+ storage: storage
237
+ },
238
+ credentialStorage: {
239
+ key: appConfig.userTokenStorageKey,
240
+ storage: storage
241
+ }
242
+ });
243
+ }
244
+
245
+ async onBefore(): Promise<void> {
246
+ if (this.isAuthenticated()) {
247
+ return;
248
+ }
249
+
250
+ const userToken = this.getToken();
251
+ if (!userToken) {
252
+ throw new AppError('NO_USER_TOKEN');
253
+ }
254
+
255
+ await this.userInfo();
256
+ this.store.authSuccess();
257
+ }
258
+ }
259
+ ```
260
+
261
+ ### 2. API Configuration Service
262
+
263
+ ```tsx
264
+ export class UserApiBootstarp implements BootstrapExecutorPlugin {
265
+ readonly pluginName = 'UserApiBootstarp';
266
+
267
+ onBefore({ parameters: { ioc } }: BootstrapContext): void {
268
+ // Get UserApi instance through IOC and configure plugins
269
+ ioc
270
+ .get<UserApi>(UserApi)
271
+ .usePlugin(new FetchURLPlugin())
272
+ .usePlugin(IOC.get(IOCIdentifier.ApiMockPlugin))
273
+ .usePlugin(IOC.get(RequestLogger));
274
+ }
275
+ }
276
+ ```
277
+
278
+ ### 3. Using in Components
279
+
280
+ ```tsx
281
+ // Using IOC services in React components
282
+ function UserProfile() {
283
+ const userService = IOC(UserService);
284
+ const { user } = useStore(userService.store);
285
+
286
+ return (
287
+ <div>
288
+ <h1>Welcome, {user?.name}</h1>
289
+ <button onClick={() => userService.logout()}>Logout</button>
290
+ </div>
291
+ );
292
+ }
293
+ ```
294
+
295
+ ## Best Practices
296
+
297
+ ### 1. Service Design Principles
298
+
299
+ ```tsx
300
+ // ✅ Good design: Single responsibility
301
+ @injectable()
302
+ export class UserService {
303
+ constructor(
304
+ @inject(UserApi) private userApi: UserApi,
305
+ @inject(IOCIdentifier.AppConfig) private appConfig: AppConfig
306
+ ) {}
307
+
308
+ async getUserInfo(): Promise<UserInfo> {
309
+ return this.userApi.getUserInfo();
310
+ }
311
+ }
312
+
313
+ // ❌ Bad design: Too many responsibilities
314
+ @injectable()
315
+ export class BadService {
316
+ constructor(
317
+ @inject(UserApi) private userApi: UserApi,
318
+ @inject(RouteService) private routeService: RouteService,
319
+ @inject(ThemeService) private themeService: ThemeService,
320
+ @inject(I18nService) private i18nService: I18nService
321
+ ) {}
322
+
323
+ // One service doing too many things
324
+ async handleUserAction(): Promise<void> {
325
+ // Handle user logic
326
+ // Handle routing logic
327
+ // Handle theme logic
328
+ // Handle i18n logic
329
+ }
330
+ }
331
+ ```
332
+
333
+ ### 2. Dependency Injection Best Practices
334
+
335
+ ```tsx
336
+ // ✅ Use interfaces instead of concrete implementations
337
+ @injectable()
338
+ export class UserService {
339
+ constructor(
340
+ @inject('UserApiInterface') private userApi: UserAuthApiInterface<UserInfo>
341
+ ) {}
342
+ }
343
+
344
+ // ✅ Use identifiers instead of class names
345
+ @injectable()
346
+ export class SomeService {
347
+ constructor(
348
+ @inject(IOCIdentifier.Logger) private logger: LoggerInterface,
349
+ @inject(IOCIdentifier.AppConfig) private appConfig: AppConfig
350
+ ) {}
351
+ }
352
+ ```
353
+
354
+ ### 3. Error Handling
355
+
356
+ ```tsx
357
+ @injectable()
358
+ export class SafeService {
359
+ constructor(@inject(IOCIdentifier.Logger) private logger: LoggerInterface) {}
360
+
361
+ async doSomething(): Promise<void> {
362
+ try {
363
+ // Business logic
364
+ } catch (error) {
365
+ this.logger.error('Operation failed:', error);
366
+ throw error;
367
+ }
368
+ }
369
+ }
370
+ ```
371
+
372
+ ## Debugging and Testing
373
+
374
+ ### 1. Debugging IOC Container
375
+
376
+ ```tsx
377
+ // Check if service is registered
378
+ const container = IOC.implemention;
379
+ const isRegistered = container.isBound(UserService);
380
+
381
+ // Get all registered services
382
+ const bindings = container.getAll(UserService);
383
+ ```
384
+
385
+ ### 2. Unit Testing
386
+
387
+ ```tsx
388
+ import { Container } from 'inversify';
389
+
390
+ describe('UserService', () => {
391
+ let container: Container;
392
+ let userService: UserService;
393
+
394
+ beforeEach(() => {
395
+ container = new Container();
396
+
397
+ // Register test dependencies
398
+ container.bind('UserApiInterface').toConstantValue(mockUserApi);
399
+ container.bind(IOCIdentifier.AppConfig).toConstantValue(mockAppConfig);
400
+ container
401
+ .bind(IOCIdentifier.LocalStorageEncrypt)
402
+ .toConstantValue(mockStorage);
403
+
404
+ userService = container.get(UserService);
405
+ });
406
+
407
+ it('should authenticate user successfully', async () => {
408
+ const result = await userService.onBefore();
409
+ expect(result).toBeDefined();
410
+ });
411
+ });
412
+ ```
413
+
414
+ ## Summary
415
+
416
+ The role of IOC container in the project:
417
+
418
+ 1. **Dependency Management**: Unified management of all service dependencies
419
+ 2. **Type Safety**: Compile-time type checking through TypeScript
420
+ 3. **Testability**: Easy to unit test and mock
421
+ 4. **Maintainability**: Clear dependency relationships, easy to understand and modify
422
+ 5. **Extensibility**: Easy to add new services and dependencies
423
+
424
+ Through proper use of the IOC container, code becomes more modular, testable, and maintainable.