@deepstorm/cli 0.10.2 → 0.11.1

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 (124) hide show
  1. package/README.md +8 -8
  2. package/dist/build-registry.js +11 -5
  3. package/dist/cli.js +1256 -634
  4. package/dist/mcp/code-hosting/github.json +14 -1
  5. package/dist/mcp/docs-reference/context7.json +1 -10
  6. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +8 -5
  7. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +10 -8
  8. package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +21 -20
  9. package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +17 -17
  10. package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +20 -20
  11. package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +8 -8
  12. package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +13 -12
  13. package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +10 -10
  14. package/dist/registry.json +10 -0
  15. package/dist/skills/atoll-ops/SKILL.md +4 -0
  16. package/dist/skills/reef-commit/SKILL.md +9 -5
  17. package/dist/skills/reef-commit/scripts/branch-check.mjs +5 -11
  18. package/dist/skills/reef-commit/scripts/check-openspec-status.mjs +6 -12
  19. package/dist/skills/reef-commit/scripts/collect-git-context.mjs +13 -11
  20. package/dist/skills/reef-gen-backend/variants/java/steps.md +7 -7
  21. package/dist/skills/reef-gen-backend/variants/nodejs/steps.md +7 -7
  22. package/dist/skills/reef-gen-backend/variants/python/steps.md +7 -7
  23. package/dist/skills/reef-gen-frontend/variants/angular/steps.md +5 -5
  24. package/dist/skills/reef-gen-frontend/variants/react/steps.md +6 -6
  25. package/dist/skills/reef-gen-frontend/variants/vue/steps.md +6 -6
  26. package/dist/skills/reef-harden/EXAMPLES.md +12 -10
  27. package/dist/skills/reef-harden/SKILL.md +6 -0
  28. package/dist/skills/reef-harden/scripts/find-change-dir.mjs +13 -13
  29. package/dist/skills/reef-pr/SKILL.md +7 -0
  30. package/dist/skills/reef-pr/scripts/create-pr.mjs +31 -29
  31. package/dist/skills/reef-scope/SKILL.md +6 -6
  32. package/dist/skills/reef-start/references/jira-start-subagent.md +7 -7
  33. package/dist/skills/reef-start/references/risk-routing-card.md +28 -25
  34. package/dist/skills/reef-start/references/stage-4-implementation.md +33 -21
  35. package/dist/skills/reef-start/references/superpowers-gate.md +9 -9
  36. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +2 -2
  37. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +11 -0
  38. package/dist/skills/reef-style-backend/fragments/java/api-spec/jackson-polymorphism.md +36 -40
  39. package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +11 -10
  40. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +7 -7
  41. package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +24 -21
  42. package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +7 -7
  43. package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +10 -8
  44. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +2 -2
  45. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +5 -5
  46. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +1 -0
  47. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +12 -12
  48. package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +13 -12
  49. package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +11 -9
  50. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +7 -7
  51. package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +6 -6
  52. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +8 -8
  53. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +8 -7
  54. package/dist/skills/reef-style-backend/fragments/nodejs/eslint-config.json +1 -4
  55. package/dist/skills/reef-style-backend/fragments/nodejs/nestjs-structure.md +9 -9
  56. package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +2 -1
  57. package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +10 -10
  58. package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +24 -22
  59. package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +10 -9
  60. package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +3 -0
  61. package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +10 -7
  62. package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +2 -0
  63. package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +2 -0
  64. package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +11 -9
  65. package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +3 -0
  66. package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +3 -4
  67. package/dist/skills/reef-style-backend/variants/java/quick-reference.md +31 -29
  68. package/dist/skills/reef-style-backend/variants/nodejs/examples/module-example.md +2 -8
  69. package/dist/skills/reef-style-backend/variants/nodejs/quick-reference.md +35 -36
  70. package/dist/skills/reef-style-backend/variants/python/quick-reference.md +32 -31
  71. package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +9 -7
  72. package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +9 -4
  73. package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +4 -4
  74. package/dist/skills/reef-style-frontend/fragments/test/vitest-react/examples/testing.md +135 -140
  75. package/dist/skills/reef-style-frontend/fragments/test/vitest-react/quick-reference.md +32 -32
  76. package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/examples/testing.md +161 -162
  77. package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/quick-reference.md +42 -42
  78. package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +16 -10
  79. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/examples/ui-components.md +72 -87
  80. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/quick-reference.md +14 -10
  81. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/examples/ui-components.md +93 -111
  82. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/quick-reference.md +29 -33
  83. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +14 -23
  84. package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +15 -17
  85. package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +22 -5
  86. package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +52 -22
  87. package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +17 -20
  88. package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +8 -11
  89. package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +27 -27
  90. package/dist/skills/reef-style-frontend/variants/react/examples/code-wrapping.md +14 -27
  91. package/dist/skills/reef-style-frontend/variants/react/examples/component-types-pipes.md +45 -52
  92. package/dist/skills/reef-style-frontend/variants/react/examples/entity-types.md +44 -44
  93. package/dist/skills/reef-style-frontend/variants/react/examples/forms-layer.md +56 -85
  94. package/dist/skills/reef-style-frontend/variants/react/examples/service-routing.md +114 -88
  95. package/dist/skills/reef-style-frontend/variants/react/quick-reference.md +48 -42
  96. package/dist/skills/reef-style-frontend/variants/vue/examples/code-wrapping.md +24 -43
  97. package/dist/skills/reef-style-frontend/variants/vue/examples/component-types-pipes.md +48 -49
  98. package/dist/skills/reef-style-frontend/variants/vue/examples/entity-types.md +45 -45
  99. package/dist/skills/reef-style-frontend/variants/vue/examples/forms-layer.md +64 -68
  100. package/dist/skills/reef-style-frontend/variants/vue/examples/service-routing.md +85 -84
  101. package/dist/skills/reef-style-frontend/variants/vue/quick-reference.md +46 -51
  102. package/dist/skills/reef-testcase/SKILL.md +19 -19
  103. package/dist/skills/reef-testcase/references/coverage-dimensions.md +8 -0
  104. package/dist/skills/reef-testcase/references/test-case-template.md +16 -16
  105. package/dist/skills/sweep-explore/SKILL.md +824 -0
  106. package/dist/skills/sweep-explore/references/explore-flow-template.md +61 -0
  107. package/dist/skills/sweep-init/SKILL.md +4 -0
  108. package/dist/skills/sweep-init/scripts/flow-selector.mjs +6 -11
  109. package/dist/skills/sweep-init/scripts/init-project.mjs +68 -51
  110. package/dist/skills/sweep-plan/references/test-flow-template.md +12 -6
  111. package/dist/skills/sweep-record/SKILL.md +358 -0
  112. package/dist/skills/sweep-run/SKILL.md +59 -50
  113. package/dist/skills/sweep-run/scripts/env-manager.mjs +4 -5
  114. package/dist/skills/sweep-run/scripts/flow-parser.mjs +7 -6
  115. package/dist/skills/sweep-run/scripts/flow-selector.mjs +4 -15
  116. package/dist/skills/sweep-run/scripts/generate-report.mjs +1 -2
  117. package/dist/skills/sweep-run/scripts/mcp-manager.mjs +107 -14
  118. package/dist/skills/sweep-run/scripts/spec-compiler.mjs +19 -9
  119. package/dist/skills/tide-discuss/references/checklists.md +32 -32
  120. package/dist/skills/tide-discuss/references/data-format.md +85 -77
  121. package/dist/skills/tide-discuss/references/prd-template.md +21 -19
  122. package/dist/skills/tide-discuss/references/publish-flow.md +30 -11
  123. package/dist/skills/tide-discuss/references/role-prompts.md +5 -5
  124. package/package.json +5 -3
@@ -4,14 +4,31 @@
4
4
 
5
5
  ```typescript
6
6
  type FormControl = TextControl | NumberControl | ChoiceControl;
7
- interface TextControl { type: 'TextControl'; label: string; maxLength?: number; }
8
- interface NumberControl { type: 'NumberControl'; min?: number; max?: number; }
9
- interface ChoiceControl { type: 'ChoiceControl'; options: Option[]; }
7
+ interface TextControl {
8
+ type: 'TextControl';
9
+ label: string;
10
+ maxLength?: number;
11
+ }
12
+ interface NumberControl {
13
+ type: 'NumberControl';
14
+ min?: number;
15
+ max?: number;
16
+ }
17
+ interface ChoiceControl {
18
+ type: 'ChoiceControl';
19
+ options: Option[];
20
+ }
10
21
 
11
22
  // Answer 多态(对应 Java 子类)
12
23
  type Answer = UuidAnswer | TextAnswer | ChoiceAnswer | TableAnswer;
13
- interface UuidAnswer { type: 'UuidAnswer'; value: string | null; }
14
- interface TextAnswer { type: 'TextAnswer'; value: string | null; }
24
+ interface UuidAnswer {
25
+ type: 'UuidAnswer';
26
+ value: string | null;
27
+ }
28
+ interface TextAnswer {
29
+ type: 'TextAnswer';
30
+ value: string | null;
31
+ }
15
32
  ```
16
33
 
17
34
  ## 2. 日期 Pipe
@@ -5,13 +5,27 @@
5
5
  实体接口镜像后端实体层次,定义在 `shared/base.ts`:
6
6
 
7
7
  ```typescript
8
- export interface ImmutableEntity { id: number; createdAt: LocalDateTime; }
9
- export interface Entity extends ImmutableEntity { lastModifiedAt: LocalDateTime; }
10
- export interface ImmutableAuditable<T> extends ImmutableEntity { createdBy: T; }
11
- export interface Auditable<T> extends Entity { createdBy: T; lastModifiedBy: T; }
8
+ export interface ImmutableEntity {
9
+ id: number;
10
+ createdAt: LocalDateTime;
11
+ }
12
+ export interface Entity extends ImmutableEntity {
13
+ lastModifiedAt: LocalDateTime;
14
+ }
15
+ export interface ImmutableAuditable<T> extends ImmutableEntity {
16
+ createdBy: T;
17
+ }
18
+ export interface Auditable<T> extends Entity {
19
+ createdBy: T;
20
+ lastModifiedBy: T;
21
+ }
12
22
 
13
23
  // UserSummary(审计字段常用类型)
14
- export interface UserSummary { id: number; username: string; fullName: string; }
24
+ export interface UserSummary {
25
+ id: number;
26
+ username: string;
27
+ fullName: string;
28
+ }
15
29
  ```
16
30
 
17
31
  > `LocalDateTime` 来自 `@js-joda/core`,配合 `iso8601Interceptor` 自动从 JSON 反序列化。
@@ -39,7 +53,11 @@ export interface FormResponse extends Auditable<UserSummary> {
39
53
  ## 3. 分页响应
40
54
 
41
55
  ```typescript
42
- export interface PageRequest { page: number; size: number; sort: string; }
56
+ export interface PageRequest {
57
+ page: number;
58
+ size: number;
59
+ sort: string;
60
+ }
43
61
  export interface PagedResponse<T extends ImmutableEntity> {
44
62
  items: T[];
45
63
  totalItems: number;
@@ -63,9 +81,7 @@ export class DataObjectInstanceService {
63
81
  list(objectId: signal<number | undefined>) {
64
82
  return httpResource<PagedResponse<DataObjectInstance>>(() => {
65
83
  const id = objectId();
66
- return id !== undefined
67
- ? `/api/v1/apps/${this.appService.appId()}/objects/${id}/instances`
68
- : undefined;
84
+ return id !== undefined ? `/api/v1/apps/${this.appService.appId()}/objects/${id}/instances` : undefined;
69
85
  });
70
86
  }
71
87
  }
@@ -76,25 +92,39 @@ export class DataObjectInstanceService {
76
92
  ```typescript
77
93
  // 用户类型(字符串字面量 discrimination)
78
94
  interface BaseUser extends Entity {
79
- type: string; username: string; fullName: string; role: Role; disabled: boolean;
95
+ type: string;
96
+ username: string;
97
+ fullName: string;
98
+ role: Role;
99
+ disabled: boolean;
100
+ }
101
+ export interface InternalUser extends BaseUser {
102
+ type: 'InternalUser';
103
+ }
104
+ export interface CdyxUser extends BaseUser {
105
+ type: 'CdyxUser';
80
106
  }
81
- export interface InternalUser extends BaseUser { type: 'InternalUser'; }
82
- export interface CdyxUser extends BaseUser { type: 'CdyxUser'; }
83
107
  export type User = InternalUser | CdyxUser;
84
108
 
85
109
  // 字段值类型(按 type 字段分发)
86
110
  export type FieldValue = TextValue | NumberValue | BooleanValue;
87
- export interface TextValue { type: 'text'; value: string | null; }
88
- export interface NumberValue { type: 'number'; value: number | null; }
111
+ export interface TextValue {
112
+ type: 'text';
113
+ value: string | null;
114
+ }
115
+ export interface NumberValue {
116
+ type: 'number';
117
+ value: number | null;
118
+ }
89
119
  ```
90
120
 
91
121
  ## 5. 关键规则
92
122
 
93
- | 规则 | 说明 |
94
- |------|------|
95
- | **interface,不是 class** | 实体全是 `interface`,没有 `class` |
96
- | **类型参数 `T`** | 审计字段的 `T` 通常是 `UserSummary` |
97
- | **必要字段** | 字段不设可选(`?`),后端保证返回 |
98
- | **枚举用 union** | 状态字段用 literal union,不用 TypeScript `enum` |
99
- | **字段注释** | Entity/接口字段加 `/** */` 注释;简单的 DTO 不加 |
100
- | **参数顺序** | `appId` → 父级 ID → 自身 ID → name/描述 |
123
+ | 规则 | 说明 |
124
+ | ------------------------- | ------------------------------------------------ |
125
+ | **interface,不是 class** | 实体全是 `interface`,没有 `class` |
126
+ | **类型参数 `T`** | 审计字段的 `T` 通常是 `UserSummary` |
127
+ | **必要字段** | 字段不设可选(`?`),后端保证返回 |
128
+ | **枚举用 union** | 状态字段用 literal union,不用 TypeScript `enum` |
129
+ | **字段注释** | Entity/接口字段加 `/** */` 注释;简单的 DTO 不加 |
130
+ | **参数顺序** | `appId` → 父级 ID → 自身 ID → name/描述 |
@@ -12,18 +12,9 @@ import { form, FormField, FormRoot, required } from '@angular/forms/signals';
12
12
  imports: [Button, Dialog, FormField, FormRoot, InputText],
13
13
  template: `
14
14
  <p-button label="创建" (onClick)="onCreate()" />
15
- <p-dialog
16
- [(visible)]="visible"
17
- [header]="isEditing() ? '编辑' : '创建'"
18
- modal
19
- (onHide)="onHide()"
20
- >
15
+ <p-dialog [(visible)]="visible" [header]="isEditing() ? '编辑' : '创建'" modal (onHide)="onHide()">
21
16
  <form [formRoot]="form" (submit)="submitted.set(true)">
22
- <input
23
- pInputText
24
- [formField]="form.name"
25
- [pInvalid]="submitted() && form.name().invalid()"
26
- />
17
+ <input pInputText [formField]="form.name" [pInvalid]="submitted() && form.name().invalid()" />
27
18
  @if (submitted() && form.name().invalid()) {
28
19
  <app-error-messages [errors]="form.name().errors()" />
29
20
  }
@@ -40,15 +31,21 @@ export class CrudDialogComponent {
40
31
  readonly isEditing = signal(false);
41
32
  readonly submitted = signal(false);
42
33
 
43
- readonly form = form(signal({ name: '' }), (path) => {
44
- required(path.name, { message: '名称不能为空' });
45
- }, {
46
- submission: { action: async () => {
47
- await lastValueFrom(this.service.create(this.form().value()));
48
- this.messageService.add({ severity: 'success', summary: '创建成功' });
49
- this.visible.set(false);
50
- }},
51
- });
34
+ readonly form = form(
35
+ signal({ name: '' }),
36
+ (path) => {
37
+ required(path.name, { message: '名称不能为空' });
38
+ },
39
+ {
40
+ submission: {
41
+ action: async () => {
42
+ await lastValueFrom(this.service.create(this.form().value()));
43
+ this.messageService.add({ severity: 'success', summary: '创建成功' });
44
+ this.visible.set(false);
45
+ },
46
+ },
47
+ },
48
+ );
52
49
 
53
50
  onCreate() {
54
51
  this.isEditing.set(false);
@@ -15,15 +15,11 @@ export class ItemService {
15
15
 
16
16
  /** 写操作:HttpClient + finalize reload */
17
17
  create(request: { name: string }) {
18
- return this.http.post<Item>('/api/v1/items', request).pipe(
19
- finalize(() => this.itemsResource.reload()),
20
- );
18
+ return this.http.post<Item>('/api/v1/items', request).pipe(finalize(() => this.itemsResource.reload()));
21
19
  }
22
20
 
23
21
  delete(id: number) {
24
- return this.http.delete(`/api/v1/items/${id}`).pipe(
25
- finalize(() => this.itemsResource.reload()),
26
- );
22
+ return this.http.delete(`/api/v1/items/${id}`).pipe(finalize(() => this.itemsResource.reload()));
27
23
  }
28
24
  }
29
25
  ```
@@ -59,7 +55,7 @@ export const routes: Routes = [
59
55
  {
60
56
  path: 'dashboard',
61
57
  title: '仪表盘',
62
- loadComponent: () => import('./dashboard/dashboard.component').then(m => m.DashboardComponent),
58
+ loadComponent: () => import('./dashboard/dashboard.component').then((m) => m.DashboardComponent),
63
59
  },
64
60
  ],
65
61
  },
@@ -72,24 +68,25 @@ export const routes: Routes = [
72
68
  {
73
69
  path: 'users',
74
70
  title: '用户管理',
75
- loadComponent: () => import('./admin/user-list/user-list.component').then(m => m.UserListComponent),
71
+ loadComponent: () => import('./admin/user-list/user-list.component').then((m) => m.UserListComponent),
76
72
  },
77
73
  {
78
74
  path: 'role-mappings',
79
75
  canActivate: [authGuard],
80
76
  data: { requiredAuthority: 'ADMIN', requireRootTenant: true },
81
- loadComponent: () => import('./admin/role-mappings/role-mappings.component').then(m => m.RoleMappingsComponent),
77
+ loadComponent: () =>
78
+ import('./admin/role-mappings/role-mappings.component').then((m) => m.RoleMappingsComponent),
82
79
  },
83
80
  ],
84
81
  },
85
82
  {
86
83
  path: 'login',
87
- loadComponent: () => import('./login/login.component').then(m => m.LoginComponent),
84
+ loadComponent: () => import('./login/login.component').then((m) => m.LoginComponent),
88
85
  canActivate: [authGuard],
89
86
  },
90
87
  {
91
88
  path: '**',
92
- loadComponent: () => import('./page-not-found/page-not-found.component').then(m => m.PageNotFoundComponent),
89
+ loadComponent: () => import('./page-not-found/page-not-found.component').then((m) => m.PageNotFoundComponent),
93
90
  },
94
91
  ];
95
92
  ```
@@ -6,14 +6,14 @@
6
6
 
7
7
  ## 速查
8
8
 
9
- | 场景 | 决策 |
10
- | --- | --- |
11
- | 新建组件 | Standalone + OnPush + inline template + inject() |
12
- | 新建表单 | Signal Forms(`form()`),非 Reactive Forms |
13
- | 读数据 | `httpResource()` |
14
- | 写数据 | `HttpClient` + `finalize(() => resource.reload())` |
15
- | Service 职责 | 只负责网络请求,不管理 UI 状态 |
16
- | 路由 | `loadComponent` 懒加载 |
9
+ | 场景 | 决策 |
10
+ | ------------ | -------------------------------------------------- |
11
+ | 新建组件 | Standalone + OnPush + inline template + inject() |
12
+ | 新建表单 | Signal Forms(`form()`),非 Reactive Forms |
13
+ | 读数据 | `httpResource()` |
14
+ | 写数据 | `HttpClient` + `finalize(() => resource.reload())` |
15
+ | Service 职责 | 只负责网络请求,不管理 UI 状态 |
16
+ | 路由 | `loadComponent` 懒加载 |
17
17
 
18
18
  ## 新建组件
19
19
 
@@ -31,13 +31,13 @@ export class ExampleComponent {
31
31
  }
32
32
  ```
33
33
 
34
- | 规则 | 要求 |
35
- | --- | --- |
36
- | Standalone | 禁止 NgModule,`imports: []` 显式导入 |
37
- | `OnPush` | 模板中所有输入 immutable |
38
- | `inject()` | 禁止构造函数注入 |
39
- | `output()` | 禁止 `@Output()` + `EventEmitter` |
40
- | `host` | 容器样式用 `host: { class: '...' }`,不包裹 `<div>` |
34
+ | 规则 | 要求 |
35
+ | ---------- | --------------------------------------------------- |
36
+ | Standalone | 禁止 NgModule,`imports: []` 显式导入 |
37
+ | `OnPush` | 模板中所有输入 immutable |
38
+ | `inject()` | 禁止构造函数注入 |
39
+ | `output()` | 禁止 `@Output()` + `EventEmitter` |
40
+ | `host` | 容器样式用 `host: { class: '...' }`,不包裹 `<div>` |
41
41
 
42
42
  ## Signal 状态
43
43
 
@@ -86,10 +86,10 @@ readonly form = form(signal({ name: '', email: '' }), (path) => {
86
86
 
87
87
  ## 注释规则
88
88
 
89
- | 文件类型 | 注释要求 |
90
- |---------|---------|
91
- | **类型定义** | 复杂类型加行内注释说明字段含义 |
92
- | **Service** | 简要说明每个方法的功能和返回类型 |
89
+ | 文件类型 | 注释要求 |
90
+ | ------------- | ---------------------------------------- |
91
+ | **类型定义** | 复杂类型加行内注释说明字段含义 |
92
+ | **Service** | 简要说明每个方法的功能和返回类型 |
93
93
  | **Component** | 关键组件加注释说明用途;复杂逻辑行内注释 |
94
94
 
95
95
  ## 前端实体类型
@@ -98,11 +98,11 @@ readonly form = form(signal({ name: '', email: '' }), (path) => {
98
98
 
99
99
  ## 常见坑
100
100
 
101
- | 场景 | 问题 | 正确做法 |
102
- |------|------|---------|
103
- | Blob 下载 | 后端返回 JSON 时 err.error 被包装为 Blob | `err.error.text().then(text => JSON.parse(text))` |
104
- | httpResource 初始化 | URL 返回 undefined 时 res.value() 为 undefined | `computed(() => res.value()?.items ?? [])` |
105
- | `form().reset()` | 只传部分字段,缺失字段不被重置 | `form().reset({ name: '' })` 补全所有字段 |
106
- | Service 状态泄漏 | 在 Service 中放 signal 管理 UI 状态 | Service 只做网络请求,状态放在 Component 中 |
107
- | `model()` vs `output()` | 需双向绑定时用了 output() | 用 `model()`,父组件 `[(visible)]="xxx"` |
108
- | submit handler | 未阻止默认提交行为 | 用 `FormRoot` 指令,或 `(submit)="check()"` + 检查 valid |
101
+ | 场景 | 问题 | 正确做法 |
102
+ | ----------------------- | ---------------------------------------------- | -------------------------------------------------------- |
103
+ | Blob 下载 | 后端返回 JSON 时 err.error 被包装为 Blob | `err.error.text().then(text => JSON.parse(text))` |
104
+ | httpResource 初始化 | URL 返回 undefined 时 res.value() 为 undefined | `computed(() => res.value()?.items ?? [])` |
105
+ | `form().reset()` | 只传部分字段,缺失字段不被重置 | `form().reset({ name: '' })` 补全所有字段 |
106
+ | Service 状态泄漏 | 在 Service 中放 signal 管理 UI 状态 | Service 只做网络请求,状态放在 Component 中 |
107
+ | `model()` vs `output()` | 需双向绑定时用了 output() | 用 `model()`,父组件 `[(visible)]="xxx"` |
108
+ | submit handler | 未阻止默认提交行为 | 用 `FormRoot` 指令,或 `(submit)="check()"` + 检查 valid |
@@ -44,47 +44,34 @@
44
44
 
45
45
  ```tsx
46
46
  // ✅ 简单的三元表达式保持行内
47
- return <div>{loading ? <Spin /> : <UserTable users={users} />}</div>
47
+ return <div>{loading ? <Spin /> : <UserTable users={users} />}</div>;
48
48
 
49
49
  // ✅ 复杂条件使用 && 或三元换行
50
50
  return (
51
51
  <div>
52
- {error && (
53
- <Alert
54
- type="error"
55
- message={error.message}
56
- closable
57
- onClose={clearError}
58
- />
59
- )}
52
+ {error && <Alert type="error" message={error.message} closable onClose={clearError} />}
60
53
  {!loading && !error && <UserTable users={users} />}
61
54
  </div>
62
- )
55
+ );
63
56
  ```
64
57
 
65
58
  ## Hook 声明
66
59
 
67
60
  ```tsx
68
61
  // ✅ Hook 声明连续排列,按逻辑分组
69
- const [users, setUsers] = useState<User[]>([])
70
- const [loading, setLoading] = useState(true)
71
- const [search, setSearch] = useState('')
62
+ const [users, setUsers] = useState<User[]>([]);
63
+ const [loading, setLoading] = useState(true);
64
+ const [search, setSearch] = useState('');
72
65
 
73
66
  // useEffect 依赖较长时换行
74
67
  useEffect(() => {
75
- fetchUsers(search)
76
- }, [search]) // 短依赖数组行内
68
+ fetchUsers(search);
69
+ }, [search]); // 短依赖数组行内
77
70
 
78
71
  // ✅ 长依赖数组换行
79
72
  useEffect(() => {
80
- fetchFilteredUsers({ search, role, status, page, size })
81
- }, [
82
- search,
83
- role,
84
- status,
85
- page,
86
- size,
87
- ])
73
+ fetchFilteredUsers({ search, role, status, page, size });
74
+ }, [search, role, status, page, size]);
88
75
  ```
89
76
 
90
77
  ## 函数 Props 换行
@@ -114,11 +101,11 @@ useEffect(() => {
114
101
  ```tsx
115
102
  // ✅ Props 类型每个属性一行
116
103
  interface UserFormProps {
117
- user?: User
118
- onSubmit: (values: CreateUserRequest) => Promise<void>
119
- onCancel: () => void
104
+ user?: User;
105
+ onSubmit: (values: CreateUserRequest) => Promise<void>;
106
+ onCancel: () => void;
120
107
  }
121
108
 
122
109
  // ✅ 联合类型换行
123
- type UserStatus = 'active' | 'inactive' | 'locked' | 'pending'
110
+ type UserStatus = 'active' | 'inactive' | 'locked' | 'pending';
124
111
  ```
@@ -9,26 +9,29 @@ Angular Pipe 在 React 中用自定义 Hook 或纯函数替代:
9
9
  ```tsx
10
10
  // ✅ 方案1: 纯函数(无状态转换)
11
11
  function formatDate(date: string | Date, format: 'short' | 'long' = 'short'): string {
12
- const d = typeof date === 'string' ? new Date(date) : date
12
+ const d = typeof date === 'string' ? new Date(date) : date;
13
13
  if (format === 'short') {
14
- return d.toLocaleDateString('zh-CN')
14
+ return d.toLocaleDateString('zh-CN');
15
15
  }
16
16
  return d.toLocaleDateString('zh-CN', {
17
- year: 'numeric', month: 'long', day: 'numeric',
18
- hour: '2-digit', minute: '2-digit',
19
- })
17
+ year: 'numeric',
18
+ month: 'long',
19
+ day: 'numeric',
20
+ hour: '2-digit',
21
+ minute: '2-digit',
22
+ });
20
23
  }
21
24
 
22
25
  function formatCurrency(amount: number, currency = 'CNY'): string {
23
26
  return new Intl.NumberFormat('zh-CN', {
24
27
  style: 'currency',
25
28
  currency,
26
- }).format(amount)
29
+ }).format(amount);
27
30
  }
28
31
 
29
32
  // 使用
30
33
  const UserList: React.FC = () => {
31
- const { users } = useUsers()
34
+ const { users } = useUsers();
32
35
  return (
33
36
  <Table
34
37
  dataSource={users}
@@ -37,18 +40,16 @@ const UserList: React.FC = () => {
37
40
  { title: '金额', dataIndex: 'amount', render: (v: number) => formatCurrency(v) },
38
41
  ]}
39
42
  />
40
- )
41
- }
43
+ );
44
+ };
42
45
 
43
46
  // ✅ 方案2: 自定义 Hook(有状态转换 — 如搜索/过滤)
44
47
  function useUserFilter(users: User[], search: string): User[] {
45
48
  return useMemo(() => {
46
- if (!search.trim()) return users
47
- const q = search.toLowerCase()
48
- return users.filter(
49
- u => u.username.toLowerCase().includes(q) || u.email.toLowerCase().includes(q)
50
- )
51
- }, [users, search])
49
+ if (!search.trim()) return users;
50
+ const q = search.toLowerCase();
51
+ return users.filter((u) => u.username.toLowerCase().includes(q) || u.email.toLowerCase().includes(q));
52
+ }, [users, search]);
52
53
  }
53
54
  ```
54
55
 
@@ -57,32 +58,29 @@ function useUserFilter(users: User[], search: string): User[] {
57
58
  ```tsx
58
59
  // hoc/withAuth.tsx
59
60
  interface WithAuthProps {
60
- requiredRole?: UserRole
61
+ requiredRole?: UserRole;
61
62
  }
62
63
 
63
- export function withAuth<P extends object>(
64
- Component: React.ComponentType<P>,
65
- options?: WithAuthProps
66
- ) {
64
+ export function withAuth<P extends object>(Component: React.ComponentType<P>, options?: WithAuthProps) {
67
65
  const AuthenticatedComponent: React.FC<P> = (props) => {
68
- const { user, loading } = useAuth()
66
+ const { user, loading } = useAuth();
69
67
 
70
- if (loading) return <Spin />
71
- if (!user) return <Navigate to="/login" replace />
68
+ if (loading) return <Spin />;
69
+ if (!user) return <Navigate to="/login" replace />;
72
70
 
73
71
  if (options?.requiredRole && user.role !== options.requiredRole) {
74
- return <Result status="403" title="无权限访问" />
72
+ return <Result status="403" title="无权限访问" />;
75
73
  }
76
74
 
77
- return <Component {...props} />
78
- }
75
+ return <Component {...props} />;
76
+ };
79
77
 
80
- AuthenticatedComponent.displayName = `withAuth(${Component.displayName || Component.name})`
81
- return AuthenticatedComponent
78
+ AuthenticatedComponent.displayName = `withAuth(${Component.displayName || Component.name})`;
79
+ return AuthenticatedComponent;
82
80
  }
83
81
 
84
82
  // 使用
85
- const AdminDashboard = withAuth(Dashboard, { requiredRole: UserRole.Admin })
83
+ const AdminDashboard = withAuth(Dashboard, { requiredRole: UserRole.Admin });
86
84
  ```
87
85
 
88
86
  ## 组件组合模式
@@ -128,33 +126,33 @@ Card.Footer = ({ children }) => (
128
126
  ```tsx
129
127
  // 数据获取 Render Props(复用数据加载逻辑)
130
128
  interface DataLoaderProps<T> {
131
- fetchFn: () => Promise<T>
132
- children: (result: { data: T | null; loading: boolean; error: Error | null }) => React.ReactNode
129
+ fetchFn: () => Promise<T>;
130
+ children: (result: { data: T | null; loading: boolean; error: Error | null }) => React.ReactNode;
133
131
  }
134
132
 
135
133
  function DataLoader<T>({ fetchFn, children }: DataLoaderProps<T>) {
136
- const [data, setData] = useState<T | null>(null)
137
- const [loading, setLoading] = useState(true)
138
- const [error, setError] = useState<Error | null>(null)
134
+ const [data, setData] = useState<T | null>(null);
135
+ const [loading, setLoading] = useState(true);
136
+ const [error, setError] = useState<Error | null>(null);
139
137
 
140
138
  useEffect(() => {
141
139
  fetchFn()
142
140
  .then(setData)
143
141
  .catch(setError)
144
- .finally(() => setLoading(false))
145
- }, [])
142
+ .finally(() => setLoading(false));
143
+ }, []);
146
144
 
147
- return <>{children({ data, loading, error })}</>
145
+ return <>{children({ data, loading, error })}</>;
148
146
  }
149
147
 
150
148
  // 使用
151
149
  <DataLoader fetchFn={() => userService.getById(id)}>
152
150
  {({ data: user, loading, error }) => {
153
- if (loading) return <Skeleton active />
154
- if (error) return <Alert type="error" message={error.message} />
155
- return <UserForm user={user} />
151
+ if (loading) return <Skeleton active />;
152
+ if (error) return <Alert type="error" message={error.message} />;
153
+ return <UserForm user={user} />;
156
154
  }}
157
- </DataLoader>
155
+ </DataLoader>;
158
156
  ```
159
157
 
160
158
  ## Discriminated Union 组件模式
@@ -164,12 +162,12 @@ function DataLoader<T>({ fetchFn, children }: DataLoaderProps<T>) {
164
162
  type NotificationCardProps =
165
163
  | { type: 'success'; message: string }
166
164
  | { type: 'error'; message: string; errorCode?: string }
167
- | { type: 'confirm'; message: string; onConfirm: () => void; onCancel: () => void }
165
+ | { type: 'confirm'; message: string; onConfirm: () => void; onCancel: () => void };
168
166
 
169
167
  const NotificationCard: React.FC<NotificationCardProps> = (props) => {
170
168
  switch (props.type) {
171
169
  case 'success':
172
- return <Alert type="success" message={props.message} showIcon />
170
+ return <Alert type="success" message={props.message} showIcon />;
173
171
  case 'error':
174
172
  return (
175
173
  <Alert
@@ -178,18 +176,13 @@ const NotificationCard: React.FC<NotificationCardProps> = (props) => {
178
176
  description={props.errorCode ? `错误码: ${props.errorCode}` : undefined}
179
177
  showIcon
180
178
  />
181
- )
179
+ );
182
180
  case 'confirm':
183
181
  return (
184
- <Modal
185
- open
186
- title="确认"
187
- onOk={props.onConfirm}
188
- onCancel={props.onCancel}
189
- >
182
+ <Modal open title="确认" onOk={props.onConfirm} onCancel={props.onCancel}>
190
183
  <p>{props.message}</p>
191
184
  </Modal>
192
- )
185
+ );
193
186
  }
194
- }
187
+ };
195
188
  ```