@qlover/create-app 0.6.2 → 0.7.0

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 (81) hide show
  1. package/CHANGELOG.md +53 -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/__tests__/__mocks__/I18nService.ts +13 -0
  7. package/dist/templates/react-app/__tests__/__mocks__/MockAppConfit.ts +48 -0
  8. package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +16 -0
  9. package/dist/templates/react-app/__tests__/__mocks__/MockLogger.ts +14 -0
  10. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +92 -0
  11. package/dist/templates/react-app/__tests__/setup/index.ts +51 -0
  12. package/dist/templates/react-app/__tests__/src/App.test.tsx +139 -0
  13. package/dist/templates/react-app/__tests__/src/base/cases/AppConfig.test.ts +288 -0
  14. package/dist/templates/react-app/__tests__/src/base/cases/AppError.test.ts +102 -0
  15. package/dist/templates/react-app/__tests__/src/base/cases/DialogHandler.test.ts +228 -0
  16. package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +207 -0
  17. package/dist/templates/react-app/__tests__/src/base/cases/InversifyContainer.test.ts +181 -0
  18. package/dist/templates/react-app/__tests__/src/base/cases/PublicAssetsPath.test.ts +61 -0
  19. package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +199 -0
  20. package/dist/templates/react-app/__tests__/src/base/cases/RequestStatusCatcher.test.ts +192 -0
  21. package/dist/templates/react-app/__tests__/src/base/cases/RouterLoader.test.ts +235 -0
  22. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +224 -0
  23. package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +257 -0
  24. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +72 -0
  25. package/dist/templates/react-app/__tests__/src/main.integration.test.tsx +62 -0
  26. package/dist/templates/react-app/__tests__/src/main.test.tsx +46 -0
  27. package/dist/templates/react-app/__tests__/src/uikit/components/BaseHeader.test.tsx +88 -0
  28. package/dist/templates/react-app/config/app.router.ts +155 -0
  29. package/dist/templates/react-app/config/common.ts +9 -1
  30. package/dist/templates/react-app/docs/en/bootstrap.md +562 -0
  31. package/dist/templates/react-app/docs/en/development-guide.md +523 -0
  32. package/dist/templates/react-app/docs/en/env.md +482 -0
  33. package/dist/templates/react-app/docs/en/global.md +509 -0
  34. package/dist/templates/react-app/docs/en/i18n.md +268 -0
  35. package/dist/templates/react-app/docs/en/index.md +173 -0
  36. package/dist/templates/react-app/docs/en/ioc.md +424 -0
  37. package/dist/templates/react-app/docs/en/project-structure.md +434 -0
  38. package/dist/templates/react-app/docs/en/request.md +425 -0
  39. package/dist/templates/react-app/docs/en/router.md +404 -0
  40. package/dist/templates/react-app/docs/en/store.md +321 -0
  41. package/dist/templates/react-app/docs/en/test-guide.md +782 -0
  42. package/dist/templates/react-app/docs/en/theme.md +424 -0
  43. package/dist/templates/react-app/docs/en/typescript-guide.md +473 -0
  44. package/dist/templates/react-app/docs/zh/bootstrap.md +7 -0
  45. package/dist/templates/react-app/docs/zh/development-guide.md +523 -0
  46. package/dist/templates/react-app/docs/zh/env.md +24 -25
  47. package/dist/templates/react-app/docs/zh/global.md +28 -27
  48. package/dist/templates/react-app/docs/zh/i18n.md +268 -0
  49. package/dist/templates/react-app/docs/zh/index.md +173 -0
  50. package/dist/templates/react-app/docs/zh/ioc.md +44 -32
  51. package/dist/templates/react-app/docs/zh/project-structure.md +434 -0
  52. package/dist/templates/react-app/docs/zh/request.md +429 -0
  53. package/dist/templates/react-app/docs/zh/router.md +408 -0
  54. package/dist/templates/react-app/docs/zh/store.md +321 -0
  55. package/dist/templates/react-app/docs/zh/test-guide.md +782 -0
  56. package/dist/templates/react-app/docs/zh/theme.md +424 -0
  57. package/dist/templates/react-app/docs/zh/typescript-guide.md +473 -0
  58. package/dist/templates/react-app/package.json +9 -20
  59. package/dist/templates/react-app/src/base/cases/AppConfig.ts +16 -9
  60. package/dist/templates/react-app/src/base/cases/PublicAssetsPath.ts +7 -1
  61. package/dist/templates/react-app/src/base/services/I18nService.ts +15 -4
  62. package/dist/templates/react-app/src/base/services/RouteService.ts +43 -7
  63. package/dist/templates/react-app/src/core/bootstraps/BootstrapApp.ts +31 -10
  64. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +1 -1
  65. package/dist/templates/react-app/src/core/globals.ts +1 -3
  66. package/dist/templates/react-app/src/core/registers/RegisterCommon.ts +5 -3
  67. package/dist/templates/react-app/src/main.tsx +6 -1
  68. package/dist/templates/react-app/src/pages/404.tsx +0 -1
  69. package/dist/templates/react-app/src/pages/500.tsx +1 -1
  70. package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +3 -1
  71. package/dist/templates/react-app/src/styles/css/antd-themes/dark.css +3 -1
  72. package/dist/templates/react-app/src/styles/css/antd-themes/index.css +1 -1
  73. package/dist/templates/react-app/src/styles/css/antd-themes/pink.css +6 -1
  74. package/dist/templates/react-app/src/styles/css/page.css +1 -1
  75. package/dist/templates/react-app/src/uikit/components/BaseHeader.tsx +9 -2
  76. package/dist/templates/react-app/src/uikit/components/LocaleLink.tsx +5 -3
  77. package/dist/templates/react-app/src/uikit/hooks/useI18nGuard.ts +4 -6
  78. package/dist/templates/react-app/tsconfig.json +2 -1
  79. package/dist/templates/react-app/tsconfig.test.json +13 -0
  80. package/dist/templates/react-app/vite.config.ts +3 -2
  81. package/package.json +1 -1
@@ -0,0 +1,473 @@
1
+ # TypeScript Development Guide
2
+
3
+ ## Overview
4
+
5
+ This guide introduces TypeScript usage standards in the project, emphasizing three programming paradigms:
6
+
7
+ - Object-Oriented Programming (OOP)
8
+ - Interface-based Programming
9
+ - Configuration-based Programming
10
+
11
+ ## Programming Paradigms
12
+
13
+ ### 1. Object-Oriented Programming (OOP)
14
+
15
+ In the project, we extensively use object-oriented concepts to organize code, primarily manifested in:
16
+
17
+ #### Class Design
18
+
19
+ ```typescript
20
+ // Base class design
21
+ abstract class StoreInterface<T extends StoreStateInterface> {
22
+ protected state: T;
23
+
24
+ constructor(initialState: T) {
25
+ this.state = initialState;
26
+ }
27
+
28
+ abstract setState(state: Partial<T>): void;
29
+
30
+ getState(): T {
31
+ return this.state;
32
+ }
33
+ }
34
+
35
+ // Concrete implementation
36
+ @injectable()
37
+ class UserStore extends StoreInterface<UserState> {
38
+ constructor() {
39
+ super({
40
+ user: null,
41
+ loading: false
42
+ });
43
+ }
44
+
45
+ setState(state: Partial<UserState>): void {
46
+ this.state = { ...this.state, ...state };
47
+ this.notify();
48
+ }
49
+
50
+ private notify(): void {
51
+ // Notify observers
52
+ }
53
+ }
54
+ ```
55
+
56
+ #### Inheritance and Polymorphism
57
+
58
+ ```typescript
59
+ // Base service interface
60
+ interface ServiceInterface {
61
+ initialize(): Promise<void>;
62
+ destroy(): void;
63
+ }
64
+
65
+ // Abstract base class
66
+ abstract class BaseService implements ServiceInterface {
67
+ abstract initialize(): Promise<void>;
68
+
69
+ destroy(): void {
70
+ // Common cleanup logic
71
+ }
72
+ }
73
+
74
+ // Concrete service implementation
75
+ class UserService extends BaseService {
76
+ async initialize(): Promise<void> {
77
+ // User service initialization logic
78
+ }
79
+
80
+ // Extended specific methods
81
+ async login(): Promise<void> {
82
+ // Login logic
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### 2. Interface-based Programming
88
+
89
+ The project emphasizes using interfaces to define contracts, achieving loose coupling:
90
+
91
+ #### Interface Definition
92
+
93
+ ```typescript
94
+ // Define interface contract
95
+ interface StorageInterface<T> {
96
+ get(key: string): T | null;
97
+ set(key: string, value: T): void;
98
+ remove(key: string): void;
99
+ clear(): void;
100
+ }
101
+
102
+ // Interface implementation
103
+ class LocalStorage<T> implements StorageInterface<T> {
104
+ get(key: string): T | null {
105
+ const value = localStorage.getItem(key);
106
+ return value ? JSON.parse(value) : null;
107
+ }
108
+
109
+ set(key: string, value: T): void {
110
+ localStorage.setItem(key, JSON.stringify(value));
111
+ }
112
+
113
+ remove(key: string): void {
114
+ localStorage.removeItem(key);
115
+ }
116
+
117
+ clear(): void {
118
+ localStorage.clear();
119
+ }
120
+ }
121
+ ```
122
+
123
+ #### Dependency Injection
124
+
125
+ ```typescript
126
+ // Service interface
127
+ interface UserServiceInterface {
128
+ getCurrentUser(): User | null;
129
+ updateProfile(data: UserProfile): Promise<void>;
130
+ }
131
+
132
+ // Service implementation
133
+ @injectable()
134
+ class UserService implements UserServiceInterface {
135
+ constructor(
136
+ @inject('StorageService') private storage: StorageInterface<User>,
137
+ @inject('ApiService') private api: ApiInterface
138
+ ) {}
139
+
140
+ getCurrentUser(): User | null {
141
+ return this.storage.get('currentUser');
142
+ }
143
+
144
+ async updateProfile(data: UserProfile): Promise<void> {
145
+ await this.api.put('/user/profile', data);
146
+ }
147
+ }
148
+ ```
149
+
150
+ ### 3. Configuration-based Programming
151
+
152
+ The project adopts a configuration-driven approach to manage features:
153
+
154
+ #### Configuration Definition
155
+
156
+ ```typescript
157
+ // Configuration interface
158
+ interface AppConfig {
159
+ api: {
160
+ baseUrl: string;
161
+ timeout: number;
162
+ retries: number;
163
+ };
164
+ auth: {
165
+ tokenKey: string;
166
+ tokenPrefix: string;
167
+ expiresIn: number;
168
+ };
169
+ theme: {
170
+ defaultTheme: 'light' | 'dark';
171
+ enableUserTheme: boolean;
172
+ };
173
+ }
174
+
175
+ // Configuration implementation
176
+ const appConfig: AppConfig = {
177
+ api: {
178
+ baseUrl: process.env.API_BASE_URL || '/api',
179
+ timeout: 5000,
180
+ retries: 3
181
+ },
182
+ auth: {
183
+ tokenKey: 'auth_token',
184
+ tokenPrefix: 'Bearer',
185
+ expiresIn: 7200
186
+ },
187
+ theme: {
188
+ defaultTheme: 'light',
189
+ enableUserTheme: true
190
+ }
191
+ };
192
+ ```
193
+
194
+ #### Configuration-driven
195
+
196
+ ```typescript
197
+ // Configuration-driven feature implementation
198
+ @injectable()
199
+ class ApiService {
200
+ constructor(
201
+ @inject('AppConfig') private config: AppConfig,
202
+ @inject('HttpClient') private http: HttpClient
203
+ ) {
204
+ // Initialize service using configuration
205
+ this.http.setBaseUrl(config.api.baseUrl);
206
+ this.http.setTimeout(config.api.timeout);
207
+ }
208
+
209
+ async request<T>(options: RequestOptions): Promise<T> {
210
+ let retries = this.config.api.retries;
211
+
212
+ while (retries > 0) {
213
+ try {
214
+ return await this.http.request(options);
215
+ } catch (error) {
216
+ retries--;
217
+ if (retries === 0) throw error;
218
+ }
219
+ }
220
+ }
221
+ }
222
+ ```
223
+
224
+ ## Type System Usage
225
+
226
+ ### 1. Generics
227
+
228
+ ```typescript
229
+ // Generic interface
230
+ interface Repository<T> {
231
+ findById(id: string): Promise<T>;
232
+ findAll(): Promise<T[]>;
233
+ create(data: Omit<T, 'id'>): Promise<T>;
234
+ update(id: string, data: Partial<T>): Promise<T>;
235
+ }
236
+
237
+ // Generic class
238
+ class ApiRepository<T> implements Repository<T> {
239
+ constructor(private endpoint: string) {}
240
+
241
+ async findById(id: string): Promise<T> {
242
+ return api.get(`${this.endpoint}/${id}`);
243
+ }
244
+
245
+ // ... other method implementations
246
+ }
247
+ ```
248
+
249
+ ### 2. Type Utilities
250
+
251
+ ```typescript
252
+ // Type mapping
253
+ type Nullable<T> = { [P in keyof T]: T[P] | null };
254
+
255
+ // Conditional types
256
+ type ArrayType<T> = T extends Array<infer U> ? U : never;
257
+
258
+ // Utility types
259
+ type PartialDeep<T> = {
260
+ [P in keyof T]?: T[P] extends object ? PartialDeep<T[P]> : T[P];
261
+ };
262
+ ```
263
+
264
+ ### 3. Decorators
265
+
266
+ ```typescript
267
+ // Method decorator
268
+ function Cached(ttl: number = 3600) {
269
+ return function (
270
+ target: any,
271
+ propertyKey: string,
272
+ descriptor: PropertyDescriptor
273
+ ) {
274
+ const original = descriptor.value;
275
+ const cache = new Map();
276
+
277
+ descriptor.value = async function (...args: any[]) {
278
+ const key = JSON.stringify(args);
279
+ if (cache.has(key)) {
280
+ const { value, timestamp } = cache.get(key);
281
+ if (Date.now() - timestamp < ttl * 1000) {
282
+ return value;
283
+ }
284
+ }
285
+
286
+ const result = await original.apply(this, args);
287
+ cache.set(key, { value: result, timestamp: Date.now() });
288
+ return result;
289
+ };
290
+ };
291
+ }
292
+
293
+ // Using decorator
294
+ class UserService {
295
+ @Cached(1800)
296
+ async getUserProfile(id: string): Promise<UserProfile> {
297
+ return this.api.get(`/users/${id}`);
298
+ }
299
+ }
300
+ ```
301
+
302
+ ## Best Practices
303
+
304
+ ### 1. Type Definitions
305
+
306
+ - Use interface for object structures
307
+ - Use type for union types and utility types
308
+ - Prefer readonly to ensure immutability
309
+ - Use optional properties judiciously
310
+
311
+ ```typescript
312
+ // Good practices
313
+ interface UserProfile {
314
+ readonly id: string;
315
+ name: string;
316
+ email: string;
317
+ avatar?: string;
318
+ preferences: Readonly<UserPreferences>;
319
+ }
320
+
321
+ // Practices to avoid
322
+ interface BadUserProfile {
323
+ [key: string]: any; // Avoid using index signatures
324
+ data: any; // Avoid using any
325
+ }
326
+ ```
327
+
328
+ ### 2. Error Handling
329
+
330
+ ```typescript
331
+ // Custom error class
332
+ class ApiError extends Error {
333
+ constructor(
334
+ public code: string,
335
+ message: string,
336
+ public data?: any
337
+ ) {
338
+ super(message);
339
+ this.name = 'ApiError';
340
+ }
341
+ }
342
+
343
+ // Type guard
344
+ function isApiError(error: unknown): error is ApiError {
345
+ return error instanceof ApiError;
346
+ }
347
+
348
+ // Error handling
349
+ try {
350
+ await api.request();
351
+ } catch (error) {
352
+ if (isApiError(error)) {
353
+ handleApiError(error);
354
+ } else {
355
+ handleUnknownError(error);
356
+ }
357
+ }
358
+ ```
359
+
360
+ ### 3. Asynchronous Handling
361
+
362
+ ```typescript
363
+ // Async result type
364
+ interface AsyncResult<T, E = Error> {
365
+ data: T | null;
366
+ error: E | null;
367
+ loading: boolean;
368
+ }
369
+
370
+ // Async operation wrapper
371
+ async function asyncWrapper<T, E = Error>(
372
+ promise: Promise<T>
373
+ ): Promise<AsyncResult<T, E>> {
374
+ try {
375
+ const data = await promise;
376
+ return { data, error: null, loading: false };
377
+ } catch (error) {
378
+ return { data: null, error: error as E, loading: false };
379
+ }
380
+ }
381
+ ```
382
+
383
+ ## Development Tools Configuration
384
+
385
+ ### 1. TSConfig Configuration
386
+
387
+ ```json
388
+ {
389
+ "compilerOptions": {
390
+ "target": "ES2020",
391
+ "module": "ESNext",
392
+ "strict": true,
393
+ "esModuleInterop": true,
394
+ "skipLibCheck": true,
395
+ "forceConsistentCasingInFileNames": true,
396
+ "experimentalDecorators": true,
397
+ "emitDecoratorMetadata": true
398
+ }
399
+ }
400
+ ```
401
+
402
+ ### 2. ESLint Configuration
403
+
404
+ ```json
405
+ {
406
+ "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
407
+ "rules": {
408
+ "@typescript-eslint/explicit-function-return-type": "error",
409
+ "@typescript-eslint/no-explicit-any": "error",
410
+ "@typescript-eslint/no-unused-vars": "error"
411
+ }
412
+ }
413
+ ```
414
+
415
+ ## Debugging Techniques
416
+
417
+ ### 1. Type Assertions
418
+
419
+ ```typescript
420
+ // Type assertion
421
+ function processValue(value: unknown) {
422
+ if (typeof value === 'string') {
423
+ return value.toUpperCase();
424
+ }
425
+ return String(value);
426
+ }
427
+
428
+ // Type narrowing
429
+ function isString(value: unknown): value is string {
430
+ return typeof value === 'string';
431
+ }
432
+ ```
433
+
434
+ ### 2. Debugging Tools
435
+
436
+ ```typescript
437
+ // Type checking
438
+ type Debug<T> = {
439
+ [P in keyof T]: T[P];
440
+ };
441
+
442
+ // Runtime type checking
443
+ function assertType<T>(value: unknown, message?: string): asserts value is T {
444
+ if (!isValidType<T>(value)) {
445
+ throw new TypeError(message || 'Type assertion failed');
446
+ }
447
+ }
448
+ ```
449
+
450
+ ## Performance Optimization
451
+
452
+ ### 1. Type Optimization
453
+
454
+ ```typescript
455
+ // Use type caching
456
+ type CachedType<T> = T extends Function ? T : Readonly<T>;
457
+
458
+ // Avoid excessive use of generics
459
+ type SimpleType = string | number;
460
+ type ComplexType<T> = T extends SimpleType ? T : never;
461
+ ```
462
+
463
+ ### 2. Compilation Optimization
464
+
465
+ - Use project references
466
+ - Enable incremental compilation
467
+ - Optimize type imports
468
+
469
+ ## Reference Resources
470
+
471
+ - [TypeScript Official Documentation](https://www.typescriptlang.org/docs/)
472
+ - [TypeScript Design Patterns](https://refactoring.guru/design-patterns/typescript)
473
+ - [Clean Code TypeScript](https://github.com/labs42io/clean-code-typescript)
@@ -5,6 +5,7 @@
5
5
  Bootstrap 是一个应用启动器,它帮助我们在应用启动时统一管理各种初始化逻辑。
6
6
 
7
7
  **简单来说**:就像电脑开机时需要启动各种服务一样,我们的应用也需要在启动时做一些准备工作,比如:
8
+
8
9
  - 检查用户是否登录
9
10
  - 加载用户信息
10
11
  - 初始化 API 配置
@@ -19,6 +20,7 @@ Bootstrap 是一个应用启动器,它帮助我们在应用启动时统一管
19
20
  **文件入口**:`src/core/bootstraps/BootstrapApp.ts`
20
21
 
21
22
  **主要组成部分**:
23
+
22
24
  1. [IOC 容器](./ioc.md) - 依赖注入管理
23
25
  2. [环境变量的注入](./env.md) - 配置管理
24
26
  3. [浏览器全局变量的注入](./global.md) - 浏览器全局属性
@@ -209,6 +211,7 @@ await bootstrap.start();
209
211
  ```
210
212
 
211
213
  **对比结果**:
214
+
212
215
  - ✅ 组件变得简洁,只负责渲染
213
216
  - ✅ 业务逻辑被分离到启动器中
214
217
  - ✅ 可以独立测试业务逻辑
@@ -293,6 +296,7 @@ await bootstrap.start();
293
296
  ```
294
297
 
295
298
  **关键点**:
299
+
296
300
  - `UserApiBootstarp`:负责配置 API 请求
297
301
  - `UserService`:负责处理用户认证逻辑
298
302
  - `Bootstrap`:统一管理所有插件
@@ -358,6 +362,7 @@ bootstrap.use([
358
362
  如果你想要快速体验 Bootstrap,可以按照以下步骤:
359
363
 
360
364
  #### 步骤 1:创建简单的插件
365
+
361
366
  ```tsx
362
367
  // 创建一个简单的插件
363
368
  export class SimplePlugin implements BootstrapExecutorPlugin {
@@ -370,6 +375,7 @@ export class SimplePlugin implements BootstrapExecutorPlugin {
370
375
  ```
371
376
 
372
377
  #### 步骤 2:在启动器中注册
378
+
373
379
  ```tsx
374
380
  const bootstrap = new Bootstrap({
375
381
  root: window,
@@ -386,6 +392,7 @@ await bootstrap.start();
386
392
  ```
387
393
 
388
394
  #### 步骤 3:查看效果
395
+
389
396
  打开浏览器控制台,你会看到 "SimplePlugin 启动成功!" 的日志。
390
397
 
391
398
  **提示**:这个简单的例子展示了 Bootstrap 的基本用法。随着你对项目的了解,可以逐步添加更复杂的业务逻辑。