create-dwy 0.15.0 → 0.16.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.
- package/CHANGELOG.md +16 -0
- package/bin/index.js +5 -3
- package/package.json +3 -2
- package/src/searchable-select.js +419 -0
- package/src/skills-install.js +48 -7
- package/src/sync-all.js +17 -25
- package/src/sync.js +38 -32
- package/templates/ai-tools/CHANGELOG.md +76 -0
- package/templates/ai-tools/CLAUDE.md +27 -0
- package/templates/ai-tools/VERSION +1 -0
- package/templates/ai-tools/hook-manifests/hooks.json +7 -0
- package/templates/ai-tools/hooks/Git/pre-git-commit-ai-signature-check.sh +57 -0
- package/templates/ai-tools/rules/Android/dwy-android-core.md +215 -4
- package/templates/ai-tools/rules/Android/dwy-android-pitfalls.md +52 -0
- package/templates/ai-tools/rules/Docker/dwy-docker.md +52 -1
- package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +50 -1
- package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-design.md +139 -0
- package/templates/ai-tools/rules/HarmonyOS/dwy-harmony-pitfalls.md +72 -0
- package/templates/ai-tools/rules/Python/dwy-python-backend.md +16 -63
- package/templates/ai-tools/rules/Python/dwy-python-base-lib.md +89 -0
- package/templates/ai-tools/rules/Python/dwy-python-pitfalls.md +49 -0
- package/templates/ai-tools/rules/Vue/dwy-vue-base-lib.md +127 -0
- package/templates/ai-tools/rules/Vue/dwy-vue-core.md +22 -86
- package/templates/ai-tools/rules/Vue/dwy-vue-pitfalls.md +66 -0
- package/templates/ai-tools/rules/iOS/dwy-ios-pitfalls.md +7 -0
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dependency-freshness.md +59 -0
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-git-commit.md +9 -6
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-development.md +47 -2
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/SKILL.md +511 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-data.md +126 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-network.md +88 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-runtime.md +195 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/security-doc-search.md +179 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_base.sh +39 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_capacity.sh +278 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_db.sh +147 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_docker.sh +230 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_env.sh +64 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_https.sh +60 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_logs.sh +209 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_nginx.sh +100 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_resilience.sh +157 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_secrets.sh +265 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_services.sh +94 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_ssh.sh +56 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/run_all.sh +105 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-acceptance/SKILL.md +3 -2
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +51 -9
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-commercial/SKILL.md +58 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-competitor/SKILL.md +2 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-explore/SKILL.md +83 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-mvp/SKILL.md +2 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prd/SKILL.md +1 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/SKILL.md +14 -9
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +65 -13
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +30 -11
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-release/SKILL.md +3 -2
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-design/SKILL.md +17 -10
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-dev/SKILL.md +42 -10
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-launch/SKILL.md +22 -10
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-prd/SKILL.md +13 -4
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-ship/SKILL.md +13 -4
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tasks/SKILL.md +12 -2
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tdd-dev/SKILL.md +15 -6
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-validate/SKILL.md +2 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-version/SKILL.md +1 -0
- package/templates/ai-tools/skills//345/205/203/345/267/245/345/205/267/dwy-shared/SKILL.md +22 -1
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/SKILL.md +1 -1
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/references/checks-runtime.md +2 -2
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_docker.sh +3 -3
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_secrets.sh +1 -1
- package/templates/ai-tools/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-first/SKILL.md +176 -0
- package/templates/ai-tools/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-first/references/chapter-01-mirrors.md +112 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-ideate/SKILL.md +0 -21
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-0to1/SKILL.md +0 -113
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-0to1/templates/STATE.md +0 -56
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dev-qa/SKILL.md +0 -117
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-market-analysis/SKILL.md +0 -92
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-coarse/SKILL.md +0 -72
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-coarse/templates/prd-coarse.md +0 -61
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-detailed/SKILL.md +0 -76
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-detailed/templates/prd-detailed.md +0 -119
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prototype/SKILL.md +0 -104
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-spike/SKILL.md +0 -81
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-dev/SKILL.md +0 -212
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-dev/templates/impl-plan.md +0 -115
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tech-feasibility/SKILL.md +0 -105
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-version-plan/SKILL.md +0 -64
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-version-plan/templates/version-plan.md +0 -72
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 团队内部基础库强制复用规范(eui UI 组件 + ekit 工具:禁重复造轮子、HTTP/存储/日期/校验/脱敏走 ekit、UI 走 eui)
|
|
3
|
+
paths:
|
|
4
|
+
- "**/*.vue"
|
|
5
|
+
- "**/*.ts"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Vue 团队基础库强制复用规范
|
|
9
|
+
|
|
10
|
+
`dwy-vue-core` rule 的团队内补充。通用 Vue 工程规范见 `dwy-vue-core`;本文件只管「凡 eui / ekit 已提供的能力,禁止自行实现,也禁止引入其他库重复造轮子」。
|
|
11
|
+
|
|
12
|
+
`@dwydev/eui` 与 `@dwydev/ekit` 是团队内部 npm 基础库,分别覆盖 UI 组件层与通用工具层。
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 一、必须复用的能力域
|
|
17
|
+
|
|
18
|
+
| 能力域 | 归属 |
|
|
19
|
+
|--------|------|
|
|
20
|
+
| 通用 UI 组件(Button / Input / Select / Dialog / Drawer / Form / Table 等) | eui |
|
|
21
|
+
| 主题 / 设计 tokens / 暗色模式 | eui |
|
|
22
|
+
| 表单校验(vee-validate + zod 集成) | eui + ekit |
|
|
23
|
+
| 中后台 / 落地页设计规范 | eui(references) |
|
|
24
|
+
| HTTP 客户端(拦截器 / token / 刷新 / 解包) | ekit |
|
|
25
|
+
| 本地存储 / Cookie | ekit(storage / cookie) |
|
|
26
|
+
| 日期 / 时间格式化 | ekit(date) |
|
|
27
|
+
| 数据校验 schema | ekit(validators) |
|
|
28
|
+
| PII 数据脱敏 | ekit(masking) |
|
|
29
|
+
| 剪贴板 / 文件下载 / qs 序列化 | ekit |
|
|
30
|
+
| VueUse 再导出(debounce / throttle / clickOutside / mediaQuery 等) | ekit(hooks) |
|
|
31
|
+
|
|
32
|
+
### 查阅方式(关键)
|
|
33
|
+
|
|
34
|
+
**在编写任何 Vue 代码前,以及调用任何 eui / ekit 提供的 API 之前,必须先用 `Skill` 工具加载对应 skill 查阅当前最新接口。** 本文件只规定约束与边界,**不固化**组件 props、函数签名 —— 这些会随版本演进,固化在 rule 里会与实际包不同步。
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
Skill 工具调用:
|
|
38
|
+
- skill="dwy-eui" 组件库 API 与设计规范
|
|
39
|
+
- skill="dwy-ekit" 工具库 API 与契约
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 二、强制规则
|
|
45
|
+
|
|
46
|
+
- **禁止**业务代码出现 `axios` / `fetch` / `new XMLHttpRequest`,HTTP 走 ekit `createRequest`
|
|
47
|
+
- **禁止**直接 `localStorage.getItem` / `setItem` / `removeItem`,走 ekit storage / `useStorage`
|
|
48
|
+
- **禁止**直接 `document.cookie` 或单独引入 `js-cookie`,走 ekit cookie
|
|
49
|
+
- **禁止**业务代码 `new Date()` / `toLocaleString` / 直接 `import dayjs`,走 ekit date
|
|
50
|
+
- **禁止**手写 `navigator.clipboard` / `document.addEventListener('click', ...)` 监听,走 ekit copy / hooks
|
|
51
|
+
- **禁止**自定义已有 UI 组件(Button / Input / Select / Dialog / Form / Table 等),走 eui
|
|
52
|
+
- **禁止**自定义已有日期 / 时间 / 验证 / 脱敏 / 下载 工具函数,走 ekit
|
|
53
|
+
- 涉及任一能力域,**先查 skill 拿到当前 API,再写代码**
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 三、API 请求
|
|
58
|
+
|
|
59
|
+
### 强制规则
|
|
60
|
+
|
|
61
|
+
- HTTP 客户端**必须**用 ekit `createRequest` 创建,全局唯一实例放 `core/http.ts`(具体参数与插件查 `dwy-ekit` skill)
|
|
62
|
+
- **禁止**业务代码 `axios.create()` / `fetch()` / `new XMLHttpRequest()`
|
|
63
|
+
- 响应解包契约与 dwyeapi 对齐(`{ code, message, data }`),具体类型与守卫查 `dwy-ekit` skill
|
|
64
|
+
- 401 / 422 / 业务错误的统一处理由 ekit request 插件完成(具体 API 查 `dwy-ekit` skill)
|
|
65
|
+
- 业务侧 catch 用 ekit 提供的 `isApiBusinessError` 类型守卫做分支(`NOT_FOUND` / `VALIDATION_ERROR` / `PERMISSION_DENIED` 等)
|
|
66
|
+
|
|
67
|
+
> API 函数的目录归属、独立导出、类型标注等结构约定见 `dwy-vue-core` 第六节。
|
|
68
|
+
|
|
69
|
+
### 示例
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// core/http.ts —— 全局唯一 HTTP 客户端(具体 plugins 查 dwy-ekit skill)
|
|
73
|
+
import { createRequest } from '@dwydev/ekit'
|
|
74
|
+
|
|
75
|
+
export const request = createRequest({
|
|
76
|
+
baseURL: import.meta.env.VITE_API_BASE_URL,
|
|
77
|
+
plugins: [
|
|
78
|
+
/* tokenPlugin / refreshTokenPlugin / unwrapPlugin —— 具体用法查 dwy-ekit skill */
|
|
79
|
+
],
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// modules/users/api.ts —— 该业务的 API
|
|
83
|
+
import { request } from '@/core/http'
|
|
84
|
+
import type { UserInfo, UserCreate } from './types'
|
|
85
|
+
|
|
86
|
+
export function getUsers(params: { page: number; page_size: number }) {
|
|
87
|
+
return request.get<{ items: UserInfo[]; total: number }>('/users', { params })
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function createUser(data: UserCreate) {
|
|
91
|
+
return request.post<UserInfo>('/users', data)
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 四、通用 hooks 走 ekit
|
|
98
|
+
|
|
99
|
+
通用 hooks(debounce / throttle / clickOutside / mediaQuery 等)**禁止**自己造,走 ekit hooks(具体 API 查 `dwy-ekit` skill)。
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 五、样式覆盖 eui
|
|
104
|
+
|
|
105
|
+
覆盖 eui 内部样式用 scoped CSS + `:deep(...)`(其余样式规范见 `dwy-vue-core` 第八节,优先 Tailwind)。
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 六、错误拦截走 ekit
|
|
110
|
+
|
|
111
|
+
401 / 422 / 业务错误的拦截走 ekit request 插件(查 `dwy-ekit` skill),**禁止**每个 API 都包 try-catch。业务侧统一在页面级或 store 级处理,用 `isApiBusinessError` 守卫分支。
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 七、违规检测清单(基础库相关)
|
|
116
|
+
|
|
117
|
+
AI 编写或审查 Vue 代码时,涉及 eui / ekit 必须检查以下违规模式(通用框架违规清单见 `dwy-vue-core` 第十四节):
|
|
118
|
+
|
|
119
|
+
| 检查项 | 违规模式 | 严重程度 |
|
|
120
|
+
|--------|---------|---------|
|
|
121
|
+
| 未查 skill | 使用 eui / ekit API 前未先查 `dwy-eui` / `dwy-ekit` skill | 高 |
|
|
122
|
+
| ekit 重复造轮子 | 自造 HTTP / storage / cookie / date / 校验 / 脱敏 等 ekit 已提供能力 | 高 |
|
|
123
|
+
| eui 重复造轮子 | 自造已有 UI 组件(Button / Input / Dialog 等) | 高 |
|
|
124
|
+
| 组件直调 axios | 组件内 `axios.get` / `fetch` / `new XMLHttpRequest` | **致命 → STOP** |
|
|
125
|
+
| 直接 localStorage | `localStorage.getItem` / `setItem` 出现在业务代码 | 高 |
|
|
126
|
+
| 直接 new Date | `new Date()` / `toLocaleString` / 直接 `import dayjs` | 高 |
|
|
127
|
+
| 旧 axios 例子 | 业务里出现 `axios.create()` / `interceptors.request.use(...)` | **致命 → STOP** |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Vue 3 + TS + Vite + Tailwind
|
|
2
|
+
description: Vue 3 + TS + Vite + Tailwind 应用层通用规范(业务聚合结构、组件 / TS / 路由 / API / 模板 / 样式 / 错误处理 / 注释)
|
|
3
3
|
paths:
|
|
4
4
|
- "**/*.vue"
|
|
5
5
|
- "**/*.ts"
|
|
@@ -8,50 +8,7 @@ paths:
|
|
|
8
8
|
|
|
9
9
|
# Vue 3 应用层开发规范
|
|
10
10
|
|
|
11
|
-
适用于基于 Vue 3 + TypeScript + Vite + Tailwind CSS
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## 零、优先使用 eui / ekit 基础库(强制)
|
|
16
|
-
|
|
17
|
-
`@dwydev/eui` 与 `@dwydev/ekit` 是团队内部 npm 基础库,分别覆盖 UI 组件层与通用工具层。**凡是 eui / ekit 已提供的能力,禁止自行实现,也禁止引入其他库重复造轮子。**
|
|
18
|
-
|
|
19
|
-
### 必须复用的能力域
|
|
20
|
-
|
|
21
|
-
| 能力域 | 归属 |
|
|
22
|
-
|--------|------|
|
|
23
|
-
| 通用 UI 组件(Button / Input / Select / Dialog / Drawer / Form / Table 等) | eui |
|
|
24
|
-
| 主题 / 设计 tokens / 暗色模式 | eui |
|
|
25
|
-
| 表单校验(vee-validate + zod 集成) | eui + ekit |
|
|
26
|
-
| 中后台 / 落地页设计规范 | eui(references) |
|
|
27
|
-
| HTTP 客户端(拦截器 / token / 刷新 / 解包) | ekit |
|
|
28
|
-
| 本地存储 / Cookie | ekit(storage / cookie) |
|
|
29
|
-
| 日期 / 时间格式化 | ekit(date) |
|
|
30
|
-
| 数据校验 schema | ekit(validators) |
|
|
31
|
-
| PII 数据脱敏 | ekit(masking) |
|
|
32
|
-
| 剪贴板 / 文件下载 / qs 序列化 | ekit |
|
|
33
|
-
| VueUse 再导出(debounce / throttle / clickOutside / mediaQuery 等) | ekit(hooks) |
|
|
34
|
-
|
|
35
|
-
### 查阅方式(关键)
|
|
36
|
-
|
|
37
|
-
**在编写任何 Vue 代码前,以及调用任何 eui / ekit 提供的 API 之前,必须先用 `Skill` 工具加载对应 skill 查阅当前最新接口。** 本文件只规定约束与边界,**不固化**组件 props、函数签名 —— 这些会随版本演进,固化在 rule 里会与实际包不同步。
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
Skill 工具调用:
|
|
41
|
-
- skill="dwy-eui" 组件库 API 与设计规范
|
|
42
|
-
- skill="dwy-ekit" 工具库 API 与契约
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### 强制规则
|
|
46
|
-
|
|
47
|
-
- **禁止**业务代码出现 `axios` / `fetch` / `new XMLHttpRequest`,HTTP 走 ekit `createRequest`
|
|
48
|
-
- **禁止**直接 `localStorage.getItem` / `setItem` / `removeItem`,走 ekit storage / `useStorage`
|
|
49
|
-
- **禁止**直接 `document.cookie` 或单独引入 `js-cookie`,走 ekit cookie
|
|
50
|
-
- **禁止**业务代码 `new Date()` / `toLocaleString` / 直接 `import dayjs`,走 ekit date
|
|
51
|
-
- **禁止**手写 `navigator.clipboard` / `document.addEventListener('click', ...)` 监听,走 ekit copy / hooks
|
|
52
|
-
- **禁止**自定义已有 UI 组件(Button / Input / Select / Dialog / Form / Table 等),走 eui
|
|
53
|
-
- **禁止**自定义已有日期 / 时间 / 验证 / 脱敏 / 下载 工具函数,走 ekit
|
|
54
|
-
- 涉及任一能力域,**先查 skill 拿到当前 API,再写代码**
|
|
11
|
+
适用于基于 Vue 3 + TypeScript + Vite + Tailwind CSS 的前端项目。团队内部 eui / ekit 基础库强制复用见 `dwy-vue-base-lib` rule;状态管理见 `dwy-vue-pinia` rule;测试见 `dwy-vue-testing` rule。
|
|
55
12
|
|
|
56
13
|
---
|
|
57
14
|
|
|
@@ -72,9 +29,9 @@ Skill 工具调用:
|
|
|
72
29
|
|
|
73
30
|
```
|
|
74
31
|
src/
|
|
75
|
-
├──
|
|
32
|
+
├── features/ # 业务功能根目录
|
|
76
33
|
│ ├── users/ # 用户功能
|
|
77
|
-
│ │ ├── api.ts # 该业务的 API
|
|
34
|
+
│ │ ├── api.ts # 该业务的 API(用全局 HTTP 客户端)
|
|
78
35
|
│ │ ├── store.ts # 该业务的 Pinia store
|
|
79
36
|
│ │ ├── types.ts # 该业务的类型
|
|
80
37
|
│ │ ├── views/ # 该业务的页面组件
|
|
@@ -92,7 +49,7 @@ src/
|
|
|
92
49
|
│ ├── composables/
|
|
93
50
|
│ └── constants/
|
|
94
51
|
├── core/ # 全局基础设施:HTTP client / 路由根 / 主题 / 启动
|
|
95
|
-
│ ├── http.ts #
|
|
52
|
+
│ ├── http.ts # 全局唯一 HTTP 客户端实例
|
|
96
53
|
│ ├── router.ts
|
|
97
54
|
│ └── main.ts
|
|
98
55
|
├── App.vue
|
|
@@ -135,12 +92,11 @@ export default {
|
|
|
135
92
|
```vue
|
|
136
93
|
<script setup lang="ts">
|
|
137
94
|
// 1. 类型导入
|
|
138
|
-
import type { UserInfo } from '@/
|
|
95
|
+
import type { UserInfo } from '@/features/users/types'
|
|
139
96
|
|
|
140
97
|
// 2. 组件 / 工具导入
|
|
141
98
|
import { ref, computed, onMounted } from 'vue'
|
|
142
99
|
import { useRouter } from 'vue-router'
|
|
143
|
-
import { EButton, EInput } from '@dwydev/eui'
|
|
144
100
|
|
|
145
101
|
// 3. Props / Emits
|
|
146
102
|
const props = defineProps<{ userId: number; title?: string }>()
|
|
@@ -250,7 +206,7 @@ const router = createRouter({
|
|
|
250
206
|
{
|
|
251
207
|
path: '/dashboard',
|
|
252
208
|
name: 'Dashboard',
|
|
253
|
-
component: () => import('@/
|
|
209
|
+
component: () => import('@/features/dashboard/views/DashboardView.vue'),
|
|
254
210
|
meta: { requiresAuth: true },
|
|
255
211
|
},
|
|
256
212
|
],
|
|
@@ -297,7 +253,7 @@ export function useList<T>(fetchFn: () => Promise<T[]>) {
|
|
|
297
253
|
}
|
|
298
254
|
```
|
|
299
255
|
|
|
300
|
-
通用 hooks(debounce / throttle / clickOutside / mediaQuery
|
|
256
|
+
通用 hooks(debounce / throttle / clickOutside / mediaQuery 等)**禁止**自己造,优先复用成熟现成方案。
|
|
301
257
|
|
|
302
258
|
---
|
|
303
259
|
|
|
@@ -305,27 +261,17 @@ export function useList<T>(fetchFn: () => Promise<T[]>) {
|
|
|
305
261
|
|
|
306
262
|
### 强制规则
|
|
307
263
|
|
|
308
|
-
- HTTP
|
|
309
|
-
- **禁止**业务代码 `axios.create()` / `fetch()` / `new XMLHttpRequest()`
|
|
264
|
+
- HTTP 客户端全局唯一实例放 `core/http.ts`,业务代码从该实例导入,**禁止**每个模块各自创建客户端
|
|
310
265
|
- 每个业务模块的 API 函数集中在该业务目录的 `api.ts`,按业务域隔离
|
|
311
266
|
- 每个 API 函数**独立导出**,**禁止** `default export`
|
|
312
267
|
- 请求参数 / 响应**必须**类型标注
|
|
313
|
-
|
|
268
|
+
|
|
269
|
+
> 团队项目 HTTP 客户端的创建(createRequest)、响应解包契约、错误处理守卫见 `dwy-vue-base-lib` rule。
|
|
314
270
|
|
|
315
271
|
### 示例
|
|
316
272
|
|
|
317
273
|
```typescript
|
|
318
|
-
//
|
|
319
|
-
import { createRequest } from '@dwydev/ekit'
|
|
320
|
-
|
|
321
|
-
export const request = createRequest({
|
|
322
|
-
baseURL: import.meta.env.VITE_API_BASE_URL,
|
|
323
|
-
plugins: [
|
|
324
|
-
/* tokenPlugin / refreshTokenPlugin / unwrapPlugin —— 具体用法查 dwy-ekit skill */
|
|
325
|
-
],
|
|
326
|
-
})
|
|
327
|
-
|
|
328
|
-
// modules/users/api.ts —— 该业务的 API
|
|
274
|
+
// features/users/api.ts —— 该业务的 API
|
|
329
275
|
import { request } from '@/core/http'
|
|
330
276
|
import type { UserInfo, UserCreate } from './types'
|
|
331
277
|
|
|
@@ -340,8 +286,7 @@ export function createUser(data: UserCreate) {
|
|
|
340
286
|
|
|
341
287
|
### 错误处理契约
|
|
342
288
|
|
|
343
|
-
- 401 / 422 /
|
|
344
|
-
- 业务侧 catch 用 ekit 提供的 `isApiBusinessError` 类型守卫做分支(`NOT_FOUND` / `VALIDATION_ERROR` / `PERMISSION_DENIED` 等)
|
|
289
|
+
- 401 / 422 / 业务错误的统一处理放在 HTTP 客户端拦截层,不散落到每个调用点
|
|
345
290
|
- **禁止**每个 API 调用都包 `try/catch`,只在页面级或 store 级统一处理
|
|
346
291
|
|
|
347
292
|
---
|
|
@@ -360,7 +305,7 @@ export function createUser(data: UserCreate) {
|
|
|
360
305
|
<UserCard :user="user" @click="handleSelect" />
|
|
361
306
|
|
|
362
307
|
<!-- 双向绑定优先 v-model -->
|
|
363
|
-
<
|
|
308
|
+
<MyInput v-model="form.name" placeholder="请输入" />
|
|
364
309
|
```
|
|
365
310
|
|
|
366
311
|
**禁止**:
|
|
@@ -384,7 +329,7 @@ export function createUser(data: UserCreate) {
|
|
|
384
329
|
|
|
385
330
|
### 允许 scoped CSS 的场景
|
|
386
331
|
|
|
387
|
-
-
|
|
332
|
+
- 第三方组件库内部样式覆盖(`:deep(...)`)
|
|
388
333
|
- 复杂动画定义
|
|
389
334
|
- 无法用 Tailwind 表达的样式
|
|
390
335
|
|
|
@@ -448,7 +393,7 @@ async function handleSubmit() {
|
|
|
448
393
|
}
|
|
449
394
|
```
|
|
450
395
|
|
|
451
|
-
- 401 / 422 /
|
|
396
|
+
- 401 / 422 / 业务错误的拦截放在 HTTP 客户端拦截层统一处理,**禁止**每个 API 都包 try-catch
|
|
452
397
|
- **禁止**空 catch(`try { ... } catch {}` 吞异常)
|
|
453
398
|
- 异步操作**必须**有 loading 状态
|
|
454
399
|
|
|
@@ -482,16 +427,12 @@ VITE_API_BASE_URL=/api
|
|
|
482
427
|
import { ref, computed } from 'vue'
|
|
483
428
|
import { useRouter } from 'vue-router'
|
|
484
429
|
|
|
485
|
-
// 2. 第三方
|
|
430
|
+
// 2. 第三方 / 内部包
|
|
486
431
|
import { storeToRefs } from 'pinia'
|
|
487
432
|
|
|
488
|
-
// 3.
|
|
489
|
-
import {
|
|
490
|
-
import {
|
|
491
|
-
|
|
492
|
-
// 4. 项目内(@ 别名)
|
|
493
|
-
import { useAuthStore } from '@/modules/users/store'
|
|
494
|
-
import type { UserInfo } from '@/modules/users/types'
|
|
433
|
+
// 3. 项目内(@ 别名)
|
|
434
|
+
import { useAuthStore } from '@/features/users/store'
|
|
435
|
+
import type { UserInfo } from '@/features/users/types'
|
|
495
436
|
```
|
|
496
437
|
|
|
497
438
|
### 强制规则
|
|
@@ -555,19 +496,14 @@ AI 编写或审查 Vue 代码时,**必须**检查以下违规模式:
|
|
|
555
496
|
| **高** | 必须修正后才能继续,向用户说明 |
|
|
556
497
|
| **中** | 提示风险,建议修正 |
|
|
557
498
|
|
|
499
|
+
> 团队内部 eui / ekit 基础库相关的违规项(未查 skill、重复造轮子、组件直调 axios、直接 localStorage / new Date 等)见 `dwy-vue-base-lib` rule 第七节。
|
|
500
|
+
|
|
558
501
|
| 检查项 | 违规模式 | 严重程度 |
|
|
559
502
|
|--------|---------|---------|
|
|
560
|
-
| 未查 skill | 使用 eui / ekit API 前未先查 `dwy-eui` / `dwy-ekit` skill | 高 |
|
|
561
|
-
| ekit 重复造轮子 | 自造 HTTP / storage / cookie / date / 校验 / 脱敏 等 ekit 已提供能力 | 高 |
|
|
562
|
-
| eui 重复造轮子 | 自造已有 UI 组件(Button / Input / Dialog 等) | 高 |
|
|
563
|
-
| 组件直调 axios | 组件内 `axios.get` / `fetch` / `new XMLHttpRequest` | **致命 → STOP** |
|
|
564
|
-
| 直接 localStorage | `localStorage.getItem` / `setItem` 出现在业务代码 | 高 |
|
|
565
|
-
| 直接 new Date | `new Date()` / `toLocaleString` / 直接 `import dayjs` | 高 |
|
|
566
503
|
| 技术层散落 | 顶级 `views/` / `components/` / `stores/` / `api/` 把不同业务混放 | 高 |
|
|
567
504
|
| Options API | `<script>` 中 `export default { data, methods, ... }` | **致命 → STOP** |
|
|
568
505
|
| Options Store | `defineStore(id, { state, actions })` | **致命 → STOP** |
|
|
569
506
|
| any 无注释 | 使用 `any` 但未注释说明原因 | 高 |
|
|
570
|
-
| 旧 axios 例子 | 业务里出现 `axios.create()` / `interceptors.request.use(...)` | **致命 → STOP** |
|
|
571
507
|
| `v-if` + `v-for` 同元素 | 同一元素既 `v-if` 又 `v-for` | 高 |
|
|
572
508
|
| 模板复杂表达式 | 模板中 `.filter(...).length > 0` 等多语句 | 中 |
|
|
573
509
|
| 解构 store 丢响应性 | `const { x } = store`(应用 `storeToRefs`) | 高 |
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Vue 3 前端开发避坑清单(CSS 动画 / 响应式 / 路由 / 构建 / SSR 踩坑),按分类持续追加
|
|
3
|
+
paths:
|
|
4
|
+
- "**/*.vue"
|
|
5
|
+
- "**/*.ts"
|
|
6
|
+
- "**/*.css"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Vue 3 前端开发坑清单
|
|
10
|
+
|
|
11
|
+
> 动手前先对照本清单避开已知坑;踩到新坑随手按四栏格式(现象 / 根因 / 正确做法 / 适用)追加到对应分类,没有合适分类就新建一个。
|
|
12
|
+
>
|
|
13
|
+
> 通用开发规范见 `dwy-vue-core` rule,本文件只记**非显而易见的实战坑**。
|
|
14
|
+
|
|
15
|
+
## 分类索引
|
|
16
|
+
|
|
17
|
+
- CSS 动画 / 过渡
|
|
18
|
+
- 响应式 / 组件(ref / reactive / 生命周期 / props)
|
|
19
|
+
- 路由(vue-router / 守卫 / 懒加载)
|
|
20
|
+
- 构建 / SSR(Vite / vite-ssg / 预渲染)
|
|
21
|
+
- i18n / 内容
|
|
22
|
+
- 排查方法论
|
|
23
|
+
- 其他分类遇到再加
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## CSS 动画 / 过渡
|
|
28
|
+
|
|
29
|
+
### View Transitions 圆形过渡:收回方向(clip-path 缩到 0)收尾闪一帧
|
|
30
|
+
|
|
31
|
+
- **现象**:用 View Transitions API + `clip-path: circle()` 做主题切换的圆形润开过渡。散开方向(圆 0→大)正常;收回方向(圆 大→0,旧层缩回圆心露出底下新主题)动画走完后页面闪一下。
|
|
32
|
+
- **根因**:CSS keyframes 默认 `animation-fill-mode: none`——**动画结束瞬间元素恢复到原始属性值,再等浏览器销毁 VT 伪元素树**。收回方向动画挂在 `::view-transition-old(root)`(旧主题层),从 `circle(175vmax)` 缩到 `circle(0)`;动画一结束,`clip-path` 因 `fill-mode:none` **回弹到无裁切(全屏可见)**,旧主题瞬间全屏重现一帧 → 这就是「闪」。散开方向不闪是因为它的动画挂在 new 层,回弹后的终态(全屏新主题)恰好 == 真实 DOM 态,肉眼看不出。不对称的本质:正在动画的上层,其回弹终态是否等于真实页面态。
|
|
33
|
+
- **正确做法**(纯 CSS,不动 JS):① 给做圆形动画的层加 **`forwards`**,动画结束后保持终态(缩到 `circle(0)` 的层保持不可见),杜绝回弹全屏闪现——这是关键修复;② 给静止的对侧层补一段**恒等铺满动画 + `both`**(`from`/`to` 都是 `circle(175vmax)`),让它整个过渡期间也作为活动合成层,与对侧合成待遇对称,收尾两层合成组同步解散,消除高对比(纯黑↔纯白)下的交接重绘。
|
|
34
|
+
```css
|
|
35
|
+
@keyframes vt-hold-full {
|
|
36
|
+
from { clip-path: circle(175vmax at var(--vt-x, 50%) var(--vt-y, 50%)); }
|
|
37
|
+
to { clip-path: circle(175vmax at var(--vt-x, 50%) var(--vt-y, 50%)); }
|
|
38
|
+
}
|
|
39
|
+
html[data-vt-direction='conceal']::view-transition-new(root) {
|
|
40
|
+
z-index: 1; animation: vt-hold-full var(--vt-dur) var(--vt-ease) both;
|
|
41
|
+
}
|
|
42
|
+
html[data-vt-direction='conceal']::view-transition-old(root) {
|
|
43
|
+
z-index: 2; animation: vt-circle-conceal var(--vt-dur) var(--vt-ease) forwards;
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
- **适用**:通用。推广教训:① 凡 keyframes 动画**结束态偏离元素静止态**(clip-path / transform / opacity 终值非默认),必须显式 `animation-fill-mode: forwards`(或 `both`),否则结束瞬间回弹一帧;不限 View Transitions。② 高对比色块下任何一帧亮度跳变都肉眼可见,动画收尾要让「正在动画的上层终态 == 真实 DOM 态」。③ 逐帧闪烁靠截图基本抓不到(只在单帧出现),定位靠根因推理 + CSSOM 确认规则生效,别依赖视觉快照验证。
|
|
47
|
+
|
|
48
|
+
## 响应式 / 组件
|
|
49
|
+
|
|
50
|
+
<!-- 待填:踩到坑按四栏格式追加 -->
|
|
51
|
+
|
|
52
|
+
## 路由
|
|
53
|
+
|
|
54
|
+
<!-- 待填 -->
|
|
55
|
+
|
|
56
|
+
## 构建 / SSR
|
|
57
|
+
|
|
58
|
+
<!-- 待填 -->
|
|
59
|
+
|
|
60
|
+
## i18n / 内容
|
|
61
|
+
|
|
62
|
+
<!-- 待填 -->
|
|
63
|
+
|
|
64
|
+
## 排查方法论
|
|
65
|
+
|
|
66
|
+
<!-- 待填 -->
|
|
@@ -157,3 +157,10 @@ paths:
|
|
|
157
157
|
- **根因**:`switch` 在视图树里每次只保留当前 case 的子树,切 tab 时 SwiftUI **销毁**旧 tab 视图、**重建**新 tab 视图,`@State` 随之重置。它不是缓存容器。
|
|
158
158
|
- **正确做法**:用系统 `TabView(selection:)` + 每页 `.tag(...)`。TabView 保活所有 tab 页,切换只是显隐,状态天然保留。隐藏系统 tab bar(`.toolbar(.hidden, for: .tabBar)`)后用自绘底栏,`selection` 与自绘栏共用同一 `@State`。注意别用 `.tabViewStyle(.page)`(会引入左右滑动切页,与边缘返回手势冲突)。
|
|
159
159
|
- **适用**:iOS 16+ SwiftUI 多 tab 且需保留各 tab 页内状态时。
|
|
160
|
+
|
|
161
|
+
### Mac(Designed for iPhone/iPad)上 sheet 不继承 Observable environment,弹层即崩
|
|
162
|
+
|
|
163
|
+
- **现象**:iPhone 真机正常;把 iOS app 装到 macOS(「在 Mac 上使用 iPhone/iPad App」)后,一点「解锁」/付费墙/编辑设备/键盘 sheet 就崩。崩溃报告 `EXC_BREAKPOINT` / `SIGTRAP`,栈顶 `libswiftCore _assertionFailure` → `SwiftUICore EnvironmentValues.subscript.getter` → `SheetBridge.present` / `PresentationHostingController`。应用日志只到 `app 启动`。
|
|
164
|
+
- **根因**:`@Environment(Store.self)` / `@Environment(AppRouter.self)` 在未注入时会 fatal。iPhone 上 sheet 内容通常继承父树的 `.environment(...)`;Mac 上 UIKitMacHelper 走 `PresentationHostingController`,**不把父视图的 Observable environment 传进 sheet**,于是 `PaywallView` 等一 present 就 assertionFailure。
|
|
165
|
+
- **正确做法**:凡 sheet / fullScreenCover 内容里读了 `@Environment(SomeObservable.self)`,在 content 上**显式** `.environment(store)` / `.environment(router)` 重注入,不要依赖继承。嵌套 sheet 同样要再注一次。例:`PaywallView().environment(store)`、`DeviceEditSheet(...).environment(router)`、`KeyboardSheet().environment(router)`。
|
|
166
|
+
- **适用**:iOS 17+ `@Observable` + `.environment(instance)`,且 app 会在 Mac 上以 Designed for iPhone/iPad 运行时。iPhone 上不重注入多数也能跑,但为跨平台一致应一律重注入。
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 依赖版本新鲜度通用规则(首次技术选型下载依赖时,发布 <7 天的版本不采用;跨 npm/uv/Docker/Android/iOS/鸿蒙/Flutter 等所有栈)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# 依赖版本新鲜度规则
|
|
6
|
+
|
|
7
|
+
> 首次技术选型下载依赖(引新包、选 base 镜像 / SDK 版本)时,刚发布的新版本常带回归 bug:构建失败、Docker 镜像缺层、API breaking change。本规则约束 AI 在装新依赖前主动查「版本发布时间」,避开 <7 天的版本。
|
|
8
|
+
>
|
|
9
|
+
> 适用**所有技术栈**:npm/pnpm、uv/pip、Docker、Android(Gradle/Maven)、iOS(CocoaPods/SPM)、鸿蒙(ohpm)、Flutter(pub) 以及未来出现的任何包管理器。下面给出总约束 + 各栈示例,未列出的栈按「查官方 registry 发布时间」通用流程自行判断。
|
|
10
|
+
|
|
11
|
+
## 一、适用边界
|
|
12
|
+
|
|
13
|
+
- **仅约束首次技术选型下载依赖**:新项目引入新包、新搭脚手架选 base 镜像 / SDK 版本、原项目引入此前没用过的依赖。
|
|
14
|
+
- **已安装的旧版本无需为此升级**。本规则只管「新装 / 新选」,不管「存量升级」。
|
|
15
|
+
- 7 天是**下限排除条件**,不是「必须选最近一个 ≥7 天的版本」。最终选哪个稳定版由 AI 综合判断(成熟度、维护活跃度、依赖体积、与项目栈兼容性)。
|
|
16
|
+
|
|
17
|
+
## 二、核心约束
|
|
18
|
+
|
|
19
|
+
- **必须**在首次安装 / 选定版本前,查候选版本的「首次发布时间」
|
|
20
|
+
- 用官方 registry 的发布时间,**不是**上传时间、**不是** `latest` / `stable` tag 时间
|
|
21
|
+
- **禁止**采用发布时间距今 **<7 天**的版本
|
|
22
|
+
- **禁止**无脑用 `@latest` / `latest` / `stable` / 浮动 tag / 不带版本号安装——这些随时可能落到刚发布的新版本上
|
|
23
|
+
- 已安装版本**无需**为此规则升级
|
|
24
|
+
|
|
25
|
+
## 三、各栈查发布时间示例
|
|
26
|
+
|
|
27
|
+
未列出的栈,按「查官方 registry 元数据 → 取发布时间」自行处理。
|
|
28
|
+
|
|
29
|
+
| 栈 | 查发布时间方法 |
|
|
30
|
+
|---|---|
|
|
31
|
+
| npm / pnpm | `npm view <pkg> time --json`(取目标版本对应的时间字段) |
|
|
32
|
+
| uv / pip | PyPI JSON API:`https://pypi.org/pypi/<pkg>/json` 的 `releases[<version>][0].upload_time` |
|
|
33
|
+
| Docker | Docker Hub 镜像页 / API 的 `last_updated`;优先看具体 tag 的 push 时间 |
|
|
34
|
+
| Android (Gradle/Maven) | Maven Central `maven-metadata.xml` 的 `<lastUpdated>`(格式 yyyyMMddHHmmss) |
|
|
35
|
+
| iOS (CocoaPods) | `https://github.com/CocoaPods/Specs` 或仓库 release / tag 时间 |
|
|
36
|
+
| iOS (SPM) | 源仓库 release / tag 时间 |
|
|
37
|
+
| 鸿蒙 (ohpm) | ohpm registry 包元数据 publishTime |
|
|
38
|
+
| Flutter (pub) | `https://pub.dev/packages/<pkg>/versions` 页 published 时间 |
|
|
39
|
+
|
|
40
|
+
## 四、正反例
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# 反例:直接装 latest,可能落到昨天才发的版本
|
|
44
|
+
pnpm add some-pkg@latest
|
|
45
|
+
uv add some-pkg # 不锁版本
|
|
46
|
+
FROM node:latest # 随时被新镜像覆盖
|
|
47
|
+
implementation 'com.example:lib:latest'
|
|
48
|
+
|
|
49
|
+
# 正例:先查发布时间,选发布 ≥7 天的稳定版
|
|
50
|
+
npm view some-pkg time --json # 看候选版本发布时间
|
|
51
|
+
pnpm add some-pkg@1.2.3 # 1.2.3 发布已满 7 天
|
|
52
|
+
FROM node:20.18.1-slim # 该 tag push 已满 7 天
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 五、AI 自判提示
|
|
56
|
+
|
|
57
|
+
- 栈未列全时,先找该栈的**官方 registry**(包管理器背后的中央仓库),查目标版本的发布时间字段。
|
|
58
|
+
- 拿不到精确发布时间时,保守处理:选发布更早、更稳定的版本,不要选「最新」。
|
|
59
|
+
- 7 天窗口内的新版本,**等待**而非采用,**无任何例外**(含安全补丁)。等满 7 天再用。
|
package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-git-commit.md
CHANGED
|
@@ -135,16 +135,19 @@ Scope 由 AI 根据变更模块与项目语义自行决定,保持与现有提
|
|
|
135
135
|
|
|
136
136
|
## 8. AI 署名与生成声明治理
|
|
137
137
|
|
|
138
|
-
commit 的 subject、body、footer、trailer
|
|
138
|
+
commit 的 subject、body、footer、trailer 中禁止出现任何形式的 AI 署名或生成声明。AI 应自行判断:凡由 AI 工具(Claude、ChatGPT、GPT、Copilot、Cursor、Gemini、Llama、LLM 等)生成或参与撰写的署名、尾随声明、自动生成标记,均不得写入 commit message。
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
- `
|
|
143
|
-
- `Claude
|
|
144
|
-
-
|
|
140
|
+
包括但不限于:
|
|
141
|
+
|
|
142
|
+
- `Co-Authored-By: <AI 名称> <noreply@...>`(任何 AI 产品 + 邮箱组合)
|
|
143
|
+
- `Generated with Claude Code` / `Generated with ...`(任何"由 XX 生成"声明)
|
|
144
|
+
- 含 AI 产品名的署名行(Claude、ChatGPT、GPT、Copilot、Cursor、Gemini、LLM 等)
|
|
145
|
+
- AI 平台noreply 邮箱(noreply@anthropic.com、noreply@openai.com 等)
|
|
145
146
|
|
|
146
147
|
`git commit -m "..."` 不得附带 AI 署名 trailer。
|
|
147
148
|
|
|
149
|
+
> PreToolUse hook `pre-git-commit-ai-signature-check.sh` 会在 commit message 中检出 Co-Authored-By / Generated with 等 AI 署名模式时硬拦截(exit 2),规则约束 + 工程兜底双保险。
|
|
150
|
+
|
|
148
151
|
## 9. 参考示例
|
|
149
152
|
|
|
150
153
|
- `feat(eui): 添加 Image 组件懒加载支持`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: TDD 开发流程(新功能/bug 修复/重构的测试先行与回归策略)
|
|
2
|
+
description: TDD 开发流程(新功能/bug 修复/重构的测试先行与回归策略)与测试-源码分隔(测试工程独立 tests/ 目录、镜像源码结构、各栈目录约定)
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# TDD 开发规则
|
|
@@ -26,6 +26,42 @@ description: TDD 开发流程(新功能/bug 修复/重构的测试先行与回
|
|
|
26
26
|
2. 再重构 — 修改代码
|
|
27
27
|
3. 再跑测试 — 确认行为没变
|
|
28
28
|
|
|
29
|
+
## 测试与源码分隔(硬约束)
|
|
30
|
+
|
|
31
|
+
测试工程必须与源码分隔,不混在一起。
|
|
32
|
+
|
|
33
|
+
### 核心规则
|
|
34
|
+
|
|
35
|
+
- 测试代码**禁止**与源码同目录混放;测试集中在项目根 / 包根的独立 `tests/` 目录。
|
|
36
|
+
- `tests/` **镜像源码结构**,测试文件与被测源码一一对应、可反查:
|
|
37
|
+
- `src/foo/bar.ts` → `tests/foo/bar.test.ts`
|
|
38
|
+
- `src/services/user.py` → `tests/test_user.py`
|
|
39
|
+
- 源码目录(`src/`)保持纯净,**只放产品代码**;打包 / 发布产物天然不含测试,CI 用一个目录通配即可圈定全部测试。
|
|
40
|
+
- 测试用的 fixture / mock 数据 / 测试工具,放 `tests/` 内(如 `tests/fixtures/`、`tests/conftest.py`),不散落进源码目录。
|
|
41
|
+
|
|
42
|
+
### 为什么(约束的根因)
|
|
43
|
+
|
|
44
|
+
测试混入源码目录会引发一连串问题,分隔后一次性收口:
|
|
45
|
+
|
|
46
|
+
- **被打包进发布产物**:测试随源码一起发到 npm / PyPI,污染产物、撑大体积。
|
|
47
|
+
- **source map / 内部细节泄露**:测试常含内部实现假设、mock 的真实数据,泄露到产物有安全风险。
|
|
48
|
+
- **覆盖率 / 构建统计污染**:测试文件被计入源码统计,覆盖率、行数失真。
|
|
49
|
+
- **CI 难圈定范围**:源码与测试交织,CI 无法用一条目录规则排除测试。
|
|
50
|
+
- **AI / 工具难定位**:找不到「源码↔测试」对应关系,反复把测试误写进 `src/`。
|
|
51
|
+
|
|
52
|
+
### 各栈目录与命令速查表
|
|
53
|
+
|
|
54
|
+
| 栈 | 测试目录 | 命名约定 | 运行命令 |
|
|
55
|
+
|----|---------|---------|---------|
|
|
56
|
+
| Vue / Vitest | `tests/` 镜像 `src/` | `*.test.ts` | `pnpm vitest run [路径]` |
|
|
57
|
+
| Python / pytest | `tests/` | `test_*.py` | `pytest tests/ -v` / `uv run pytest` |
|
|
58
|
+
| Android | `src/test/`(单元)+ `src/androidTest/`(仪器) | `*Test.kt` | `./gradlew test` / `./gradlew connectedAndroidTest` |
|
|
59
|
+
| Node / CLI | `tests/` 镜像 `src/` | `*.test.ts` | 对应 runner(vitest / jest) |
|
|
60
|
+
|
|
61
|
+
注:Android 的 `src/test` / `src/androidTest` 是 Gradle 生态**强约定**的独立测试源集(test source set),与 `src/main` 源码完全分隔,符合本规范"测试与源码分隔"理念,按其生态默认即可,**属合规分隔**,不视为"测试写进 src"。
|
|
62
|
+
|
|
63
|
+
**通用准则(表中未列的栈)**:遵循「独立测试目录 + 镜像源码结构 + 该生态主流 runner 约定」三条原则自行判断,不硬套上表布局。
|
|
64
|
+
|
|
29
65
|
## 回归测试
|
|
30
66
|
|
|
31
67
|
### 开发中:只跑受影响的测试
|
|
@@ -55,7 +91,13 @@ pnpm vitest run tests/utils/format.test.ts # 改了单个工具函数
|
|
|
55
91
|
|
|
56
92
|
## TEST_CASES.md
|
|
57
93
|
|
|
58
|
-
每个包/项目维护一份 `TEST_CASES.md
|
|
94
|
+
每个包/项目维护一份 `TEST_CASES.md`,记录所有测试用例的清单。**`TEST_CASES.md` 跟测试目录走,不放项目根**:
|
|
95
|
+
|
|
96
|
+
- 有 `tests/` → 放 `tests/TEST_CASES.md`
|
|
97
|
+
- 测试源集分散(如 Android `src/test/`)→ 放对应测试根(`src/test/TEST_CASES.md`),与该模块测试同处
|
|
98
|
+
- 理念:测试清单是测试工程的一部分,归测试目录,与「测试与源码分隔」一致
|
|
99
|
+
|
|
100
|
+
内容要求:
|
|
59
101
|
|
|
60
102
|
- 人类可读,方便审查和手动编辑
|
|
61
103
|
- 新增/修改/删除功能时必须同步更新
|
|
@@ -68,3 +110,6 @@ pnpm vitest run tests/utils/format.test.ts # 改了单个工具函数
|
|
|
68
110
|
- **禁止**写完代码再补测试(先测试后代码)
|
|
69
111
|
- **禁止**测试不通过就提交
|
|
70
112
|
- **禁止** TEST_CASES.md 与实际测试不同步
|
|
113
|
+
- **禁止**测试文件写进 `src/` 源码目录(Android `src/test`、`src/androidTest` 例外,是 Gradle 标准测试源集)
|
|
114
|
+
- **禁止**测试目录结构与源码脱节(找不到「源码↔测试」对应关系)
|
|
115
|
+
- **禁止**把测试 fixture / mock 数据散落进源码目录
|