feat: 配置代码质量工具链
添加 ESLint、Prettier、Husky 和 lint-staged 以自动化代码检查和格式化 新增 .prettierrc、.prettierignore、eslint.config.js 和 Husky pre-commit hook 在 package.json 中添加相关脚本和依赖,包括格式化、检查、测试覆盖率等命令 新增详细配置文档 HUSKY_LINT_STAGED.md 和 ESLINT_PRETTIER.md
Showing
8 changed files
with
932 additions
and
1 deletions
.husky/pre-commit
0 → 100755
.prettierignore
0 → 100644
| 1 | +# 依赖 | ||
| 2 | +node_modules/ | ||
| 3 | +dist/ | ||
| 4 | +dist.*/ | ||
| 5 | +build/ | ||
| 6 | + | ||
| 7 | +# 配置文件 | ||
| 8 | +package-lock.json | ||
| 9 | +pnpm-lock.yaml | ||
| 10 | +yarn.lock | ||
| 11 | + | ||
| 12 | +# 自动生成 | ||
| 13 | +src/auto-imports.d.ts | ||
| 14 | +src/components.d.ts | ||
| 15 | +src/typings/env.d.ts | ||
| 16 | +.eslintrc-auto-import.json | ||
| 17 | + | ||
| 18 | +# 静态资源 | ||
| 19 | +public/ | ||
| 20 | +**/*.svg | ||
| 21 | +**/*.png | ||
| 22 | +**/*.jpg | ||
| 23 | +**/*.jpeg | ||
| 24 | +**/*.gif | ||
| 25 | +**/*.ico | ||
| 26 | + | ||
| 27 | +# 其他 | ||
| 28 | +.cursor/ | ||
| 29 | +coverage/ | ||
| 30 | +.nyc_output/ | ||
| 31 | +*.min.js | ||
| 32 | +*.min.css | ||
| 33 | + | ||
| 34 | +# 文档 | ||
| 35 | +CHANGELOG.md | ||
| 36 | +LICENSE | ||
| 37 | +VITE*.md | ||
| 38 | + | ||
| 39 | +# 临时文件 | ||
| 40 | +*.log | ||
| 41 | +*.tmp | ||
| 42 | +*.temp | ||
| 43 | +.DS_Store |
.prettierrc
0 → 100644
| 1 | +{ | ||
| 2 | + "semi": false, | ||
| 3 | + "singleQuote": true, | ||
| 4 | + "quoteProps": "as-needed", | ||
| 5 | + "trailingComma": "es5", | ||
| 6 | + "bracketSpacing": true, | ||
| 7 | + "bracketSameLine": false, | ||
| 8 | + "arrowParens": "avoid", | ||
| 9 | + "printWidth": 100, | ||
| 10 | + "tabWidth": 2, | ||
| 11 | + "useTabs": false, | ||
| 12 | + "endOfLine": "lf", | ||
| 13 | + "plugins": ["prettier-plugin-tailwindcss"], | ||
| 14 | + "overrides": [ | ||
| 15 | + { | ||
| 16 | + "files": "*.vue", | ||
| 17 | + "options": { | ||
| 18 | + "parser": "vue" | ||
| 19 | + } | ||
| 20 | + }, | ||
| 21 | + { | ||
| 22 | + "files": "*.md", | ||
| 23 | + "options": { | ||
| 24 | + "proseWrap": "preserve" | ||
| 25 | + } | ||
| 26 | + } | ||
| 27 | + ] | ||
| 28 | +} |
docs/ESLINT_PRETTIER.md
0 → 100644
| 1 | +# ESLint + Prettier 配置说明 | ||
| 2 | + | ||
| 3 | +## ✅ 已安装的包 | ||
| 4 | + | ||
| 5 | +```json | ||
| 6 | +{ | ||
| 7 | + "devDependencies": { | ||
| 8 | + "eslint": "^9.39.2", | ||
| 9 | + "eslint-plugin-vue": "^10.7.0", | ||
| 10 | + "eslint-config-prettier": "^10.1.8", | ||
| 11 | + "@vue/eslint-config-prettier": "^10.2.0", | ||
| 12 | + "prettier": "^3.8.1", | ||
| 13 | + "prettier-plugin-tailwindcss": "^0.7.2" | ||
| 14 | + } | ||
| 15 | +} | ||
| 16 | +``` | ||
| 17 | + | ||
| 18 | +## 📁 配置文件 | ||
| 19 | + | ||
| 20 | +### ESLint 配置 | ||
| 21 | + | ||
| 22 | +**文件**: `eslint.config.js` (ESLint 9 扁平配置格式) | ||
| 23 | + | ||
| 24 | +**核心规则**: | ||
| 25 | + | ||
| 26 | +- Vue 3 规则(组件命名、props 检查、模板规范) | ||
| 27 | +- JavaScript 规则(no-var、prefer-const、箭头函数) | ||
| 28 | +- 代码质量(===、错误处理、安全) | ||
| 29 | +- 性能优化(避免循环中的函数、禁止 eval) | ||
| 30 | + | ||
| 31 | +**忽略文件**: | ||
| 32 | + | ||
| 33 | +```javascript | ||
| 34 | +ignores: [ | ||
| 35 | + 'node_modules/**', | ||
| 36 | + 'dist/**', | ||
| 37 | + 'dist.*', | ||
| 38 | + 'build/**', | ||
| 39 | + '*.min.js', | ||
| 40 | + 'public/**', | ||
| 41 | + 'VITE*.md', | ||
| 42 | + '.cursor/**', | ||
| 43 | + '.history/**', | ||
| 44 | + 'coverage/**', | ||
| 45 | + '.nyc_output/**', | ||
| 46 | + 'src/auto-imports.d.ts', | ||
| 47 | + 'src/components.d.ts', | ||
| 48 | + 'mlaj/**', | ||
| 49 | +] | ||
| 50 | +``` | ||
| 51 | + | ||
| 52 | +### Prettier 配置 | ||
| 53 | + | ||
| 54 | +**文件**: `.prettierrc` | ||
| 55 | + | ||
| 56 | +**核心配置**: | ||
| 57 | + | ||
| 58 | +```json | ||
| 59 | +{ | ||
| 60 | + "semi": false, // 不使用分号 | ||
| 61 | + "singleQuote": true, // 使用单引号 | ||
| 62 | + "trailingComma": "es5", // ES5 尾部逗号 | ||
| 63 | + "printWidth": 100, // 每行最大 100 字符 | ||
| 64 | + "tabWidth": 2, // 2 空格缩进 | ||
| 65 | + "useTabs": false, // 使用空格而非 tab | ||
| 66 | + "endOfLine": "lf" // LF 换行符 | ||
| 67 | +} | ||
| 68 | +``` | ||
| 69 | + | ||
| 70 | +**忽略文件**: | ||
| 71 | + | ||
| 72 | +- `node_modules/` | ||
| 73 | +- `dist/` | ||
| 74 | +- `build/` | ||
| 75 | +- `public/` | ||
| 76 | +- `coverage/` | ||
| 77 | +- `*.min.js` | ||
| 78 | +- `package-lock.json` | ||
| 79 | +- `pnpm-lock.yaml` | ||
| 80 | + | ||
| 81 | +### VS Code 配置 | ||
| 82 | + | ||
| 83 | +**文件**: `.vscode/settings.json` | ||
| 84 | + | ||
| 85 | +**自动格式化**: | ||
| 86 | + | ||
| 87 | +```json | ||
| 88 | +{ | ||
| 89 | + "editor.formatOnSave": true, | ||
| 90 | + "editor.codeActionsOnSave": { | ||
| 91 | + "source.fixAll.eslint": "explicit" | ||
| 92 | + }, | ||
| 93 | + "editor.defaultFormatter": "esbenp.prettier-vscode" | ||
| 94 | +} | ||
| 95 | +``` | ||
| 96 | + | ||
| 97 | +**推荐扩展** (`.vscode/extensions.json`): | ||
| 98 | + | ||
| 99 | +- `Vue.volar` - Vue 3 语言支持 | ||
| 100 | +- `dbaeumer.vscode-eslint` - ESLint 集成 | ||
| 101 | +- `esbenp.prettier-vscode` - Prettier 格式化 | ||
| 102 | +- `bradlc.vscode-tailwindcss` - TailwindCSS 智能提示 | ||
| 103 | +- `formulahendry.auto-rename-tag` - 自动重命名标签 | ||
| 104 | +- `christian-kohler.path-intellisense` - 路径智能提示 | ||
| 105 | + | ||
| 106 | +## 🚀 使用命令 | ||
| 107 | + | ||
| 108 | +### Lint(代码检查) | ||
| 109 | + | ||
| 110 | +```bash | ||
| 111 | +# 检查但不修复 | ||
| 112 | +pnpm lint:check | ||
| 113 | + | ||
| 114 | +# 检查并自动修复 | ||
| 115 | +pnpm lint | ||
| 116 | +``` | ||
| 117 | + | ||
| 118 | +### Format(代码格式化) | ||
| 119 | + | ||
| 120 | +```bash | ||
| 121 | +# 检查格式(不修改文件) | ||
| 122 | +pnpm format:check | ||
| 123 | + | ||
| 124 | +# 格式化代码 | ||
| 125 | +pnpm format | ||
| 126 | +``` | ||
| 127 | + | ||
| 128 | +### 组合使用 | ||
| 129 | + | ||
| 130 | +```bash | ||
| 131 | +# 先格式化,再 lint | ||
| 132 | +pnpm format && pnpm lint | ||
| 133 | + | ||
| 134 | +# 提交前检查 | ||
| 135 | +pnpm format:check && pnpm lint:check && pnpm test | ||
| 136 | +``` | ||
| 137 | + | ||
| 138 | +## 📋 ESLint 核心规则 | ||
| 139 | + | ||
| 140 | +### Vue 3 规则 | ||
| 141 | + | ||
| 142 | +| 规则 | 级别 | 说明 | | ||
| 143 | +| -------------------------------------- | ----- | ------------------ | | ||
| 144 | +| `vue/multi-word-component-names` | off | 允许单词组件名 | | ||
| 145 | +| `vue/no-v-html` | warn | 警告使用 v-html | | ||
| 146 | +| `vue/require-prop-types` | error | props 必须有类型 | | ||
| 147 | +| `vue/no-mutating-props` | error | 禁止直接修改 props | | ||
| 148 | +| `vue/component-definition-name-casing` | error | 组件名 PascalCase | | ||
| 149 | + | ||
| 150 | +### JavaScript 规则 | ||
| 151 | + | ||
| 152 | +| 规则 | 级别 | 说明 | | ||
| 153 | +| ------------------ | ----- | ----------------------------------- | | ||
| 154 | +| `no-console` | warn | 禁止 console.log(允许 warn/error) | | ||
| 155 | +| `no-debugger` | error | 禁止 debugger | | ||
| 156 | +| `no-var` | error | 禁止 var,使用 const/let | | ||
| 157 | +| `prefer-const` | error | 优先使用 const | | ||
| 158 | +| `eqeqeq` | error | 强制使用 === | | ||
| 159 | +| `no-eval` | error | 禁止 eval | | ||
| 160 | +| `no-throw-literal` | error | throw 必须是 Error 对象 | | ||
| 161 | + | ||
| 162 | +## 🔧 常见问题 | ||
| 163 | + | ||
| 164 | +### 1. ESLint 报错但不显示具体规则 | ||
| 165 | + | ||
| 166 | +**解决**: 确保 `eslint.config.js` 配置正确 | ||
| 167 | + | ||
| 168 | +### 2. Prettier 与 ESLint 冲突 | ||
| 169 | + | ||
| 170 | +**解决**: `eslint-config-prettier` 已禁用所有与 Prettier 冲突的规则 | ||
| 171 | + | ||
| 172 | +### 3. VS Code 不自动格式化 | ||
| 173 | + | ||
| 174 | +**解决**: | ||
| 175 | + | ||
| 176 | +1. 确保安装了 `Prettier - Code formatter` 扩展 | ||
| 177 | +2. 检查 `.vscode/settings.json` 中的配置 | ||
| 178 | +3. 重启 VS Code | ||
| 179 | + | ||
| 180 | +### 4. 忽略特定文件 | ||
| 181 | + | ||
| 182 | +**ESLint 9**: 在 `eslint.config.js` 的 `ignores` 中添加 | ||
| 183 | + | ||
| 184 | +**Prettier**: 在 `.prettierignore` 中添加 | ||
| 185 | + | ||
| 186 | +## 📝 提交前检查 | ||
| 187 | + | ||
| 188 | +在提交代码前,请运行: | ||
| 189 | + | ||
| 190 | +```bash | ||
| 191 | +# 1. 格式化代码 | ||
| 192 | +pnpm format | ||
| 193 | + | ||
| 194 | +# 2. 检查代码规范 | ||
| 195 | +pnpm lint:check | ||
| 196 | + | ||
| 197 | +# 3. 运行测试 | ||
| 198 | +pnpm test | ||
| 199 | + | ||
| 200 | +# 4. 检查测试覆盖率 | ||
| 201 | +pnpm test:coverage | ||
| 202 | +``` | ||
| 203 | + | ||
| 204 | +## 🎯 最佳实践 | ||
| 205 | + | ||
| 206 | +### 1. 开发流程 | ||
| 207 | + | ||
| 208 | +```bash | ||
| 209 | +# 1. 编写代码 | ||
| 210 | +# 2. 保存时自动格式化(VS Code 自动) | ||
| 211 | +# 3. 手动运行 lint 检查 | ||
| 212 | +pnpm lint:check | ||
| 213 | + | ||
| 214 | +# 4. 修复错误 | ||
| 215 | +pnpm lint | ||
| 216 | + | ||
| 217 | +# 5. 提交代码 | ||
| 218 | +git add . | ||
| 219 | +git commit -m "feat: xxx" | ||
| 220 | +``` | ||
| 221 | + | ||
| 222 | +### 2. Git Hooks(可选) | ||
| 223 | + | ||
| 224 | +使用 `husky` 和 `lint-staged` 自动化: | ||
| 225 | + | ||
| 226 | +```bash | ||
| 227 | +# 安装 | ||
| 228 | +pnpm add -D husky lint-staged | ||
| 229 | + | ||
| 230 | +# 配置 | ||
| 231 | +npx husky install | ||
| 232 | +npx husky add .husky/pre-commit "pnpm lint-staged" | ||
| 233 | +``` | ||
| 234 | + | ||
| 235 | +**`package.json`**: | ||
| 236 | + | ||
| 237 | +```json | ||
| 238 | +{ | ||
| 239 | + "lint-staged": { | ||
| 240 | + "*.{js,vue}": ["eslint --fix", "prettier --write"] | ||
| 241 | + } | ||
| 242 | +} | ||
| 243 | +``` | ||
| 244 | + | ||
| 245 | +### 3. CI/CD 集成 | ||
| 246 | + | ||
| 247 | +在 CI 中运行: | ||
| 248 | + | ||
| 249 | +```yaml | ||
| 250 | +# .github/workflows/ci.yml | ||
| 251 | +- name: Lint | ||
| 252 | + run: pnpm lint:check | ||
| 253 | + | ||
| 254 | +- name: Format check | ||
| 255 | + run: pnpm format:check | ||
| 256 | + | ||
| 257 | +- name: Test | ||
| 258 | + run: pnpm test | ||
| 259 | +``` | ||
| 260 | + | ||
| 261 | +## 📊 规则优先级 | ||
| 262 | + | ||
| 263 | +``` | ||
| 264 | +ESLint 规则 > Prettier 规则 | ||
| 265 | + | ||
| 266 | +1. Prettier 负责格式化(空格、引号、换行) | ||
| 267 | +2. ESLint 负责代码质量(未使用变量、潜在错误) | ||
| 268 | +3. eslint-config-prettier 禁用冲突的 ESLint 规则 | ||
| 269 | +``` | ||
| 270 | + | ||
| 271 | +## 🔄 迁移指南 | ||
| 272 | + | ||
| 273 | +### 从旧版 ESLint 迁移 | ||
| 274 | + | ||
| 275 | +如果你之前使用 `.eslintrc.js`: | ||
| 276 | + | ||
| 277 | +```bash | ||
| 278 | +# 1. 删除旧配置 | ||
| 279 | +rm .eslintrc.js | ||
| 280 | + | ||
| 281 | +# 2. 使用新的 eslint.config.js(已配置) | ||
| 282 | +# 3. 删除 .eslintignore(ESLint 9 不再使用) | ||
| 283 | +``` | ||
| 284 | + | ||
| 285 | +### 从旧版 Prettier 迁移 | ||
| 286 | + | ||
| 287 | +如果你之前使用 `.prettierrc.js`: | ||
| 288 | + | ||
| 289 | +```bash | ||
| 290 | +# 1. 转换为 .prettierrc(JSON 格式) | ||
| 291 | +# 2. 确保 prettier-plugin-tailwindcss 已安装 | ||
| 292 | +``` | ||
| 293 | + | ||
| 294 | +## 🚨 注意事项 | ||
| 295 | + | ||
| 296 | +1. **不要修改 `node_modules/` 中的文件** - ESLint 会忽略它们 | ||
| 297 | +2. **不要提交格式化后的构建产物** - `dist/`、`build/` 已被忽略 | ||
| 298 | +3. **团队统一配置** - 确保所有成员使用相同的配置文件 | ||
| 299 | +4. **定期更新依赖** - ESLint 和 Prettier 会定期更新 | ||
| 300 | + | ||
| 301 | +## 📚 参考资源 | ||
| 302 | + | ||
| 303 | +- [ESLint 9 文档](https://eslint.org/docs/latest/) | ||
| 304 | +- [Vue 3 官方 ESLint 插件](https://eslint.vuejs.org/) | ||
| 305 | +- [Prettier 文档](https://prettier.io/docs/en/) | ||
| 306 | +- [Prettier TailwindCSS 插件](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) | ||
| 307 | + | ||
| 308 | +## 🎉 完成 | ||
| 309 | + | ||
| 310 | +你的项目现在已经配置了完整的 ESLint 和 Prettier! | ||
| 311 | + | ||
| 312 | +**下一步**: | ||
| 313 | + | ||
| 314 | +- [ ] 添加 Husky + lint-staged(Git Hooks) | ||
| 315 | +- [ ] 添加 Vitest Coverage(测试覆盖率) | ||
| 316 | +- [ ] 添加 Playwright(E2E 测试) |
docs/HUSKY_LINT_STAGED.md
0 → 100644
| 1 | +# Husky + lint-staged 配置说明 | ||
| 2 | + | ||
| 3 | +## ✅ 配置完成 | ||
| 4 | + | ||
| 5 | +你的项目现在已经配置了 **Husky** 和 **lint-staged**,在 Git 提交前自动运行代码检查和格式化。 | ||
| 6 | + | ||
| 7 | +## 📦 已安装的包 | ||
| 8 | + | ||
| 9 | +```json | ||
| 10 | +{ | ||
| 11 | + "devDependencies": { | ||
| 12 | + "husky": "^9.1.7", | ||
| 13 | + "lint-staged": "^16.2.7" | ||
| 14 | + } | ||
| 15 | +} | ||
| 16 | +``` | ||
| 17 | + | ||
| 18 | +## 🔧 配置文件 | ||
| 19 | + | ||
| 20 | +### 1. Husky 配置 | ||
| 21 | + | ||
| 22 | +**目录**: `.husky/` | ||
| 23 | + | ||
| 24 | +**文件**: | ||
| 25 | + | ||
| 26 | +- `pre-commit` - 提交前执行的 hook | ||
| 27 | +- `_/husky.sh` - Husky 辅助脚本 | ||
| 28 | + | ||
| 29 | +**`pre-commit` 内容**: | ||
| 30 | + | ||
| 31 | +```bash | ||
| 32 | +#!/usr/bin/env sh | ||
| 33 | +. "$(dirname "$0")/_/husky.sh" | ||
| 34 | + | ||
| 35 | +npx lint-staged | ||
| 36 | +``` | ||
| 37 | + | ||
| 38 | +### 2. lint-staged 配置 | ||
| 39 | + | ||
| 40 | +**位置**: `package.json` 中的 `lint-staged` 字段 | ||
| 41 | + | ||
| 42 | +**配置**: | ||
| 43 | + | ||
| 44 | +```json | ||
| 45 | +{ | ||
| 46 | + "lint-staged": { | ||
| 47 | + "*.{js,vue}": ["eslint --fix", "prettier --write"], | ||
| 48 | + "*.{css,less,scss}": ["prettier --write"], | ||
| 49 | + "*.{json,md}": ["prettier --write"] | ||
| 50 | + } | ||
| 51 | +} | ||
| 52 | +``` | ||
| 53 | + | ||
| 54 | +## 🚀 工作流程 | ||
| 55 | + | ||
| 56 | +### 自动化流程 | ||
| 57 | + | ||
| 58 | +``` | ||
| 59 | +1. git add . | ||
| 60 | + ↓ | ||
| 61 | +2. git commit -m "xxx" | ||
| 62 | + ↓ | ||
| 63 | +3. Husky 触发 pre-commit hook | ||
| 64 | + ↓ | ||
| 65 | +4. lint-staged 检查暂存的文件 | ||
| 66 | + ↓ | ||
| 67 | +5. ESLint 自动修复问题 | ||
| 68 | + ↓ | ||
| 69 | +6. Prettier 自动格式化代码 | ||
| 70 | + ↓ | ||
| 71 | +7. 如果所有检查通过 → 提交成功 ✅ | ||
| 72 | + 如果有错误 → 提交失败,显示错误信息 ❌ | ||
| 73 | +``` | ||
| 74 | + | ||
| 75 | +### 文件类型处理 | ||
| 76 | + | ||
| 77 | +| 文件类型 | 操作 | | ||
| 78 | +| --------------------------- | ----------------------------------- | | ||
| 79 | +| `*.js`, `*.vue` | ESLint 检查并修复 + Prettier 格式化 | | ||
| 80 | +| `*.css`, `*.less`, `*.scss` | Prettier 格式化 | | ||
| 81 | +| `*.json`, `*.md` | Prettier 格式化 | | ||
| 82 | + | ||
| 83 | +## 📝 使用示例 | ||
| 84 | + | ||
| 85 | +### 正常提交 | ||
| 86 | + | ||
| 87 | +```bash | ||
| 88 | +# 1. 修改文件 | ||
| 89 | +echo "console.log('test')" >> test.js | ||
| 90 | + | ||
| 91 | +# 2. 添加到暂存区 | ||
| 92 | +git add test.js | ||
| 93 | + | ||
| 94 | +# 3. 提交(自动运行 lint-staged) | ||
| 95 | +git commit -m "test: add test file" | ||
| 96 | + | ||
| 97 | +# 输出: | ||
| 98 | +# ✔ prettier --write test.js | ||
| 99 | +# ✔ eslint --fix test.js | ||
| 100 | +# [main xxxxx] test: add test file | ||
| 101 | +``` | ||
| 102 | + | ||
| 103 | +### 提交失败(有错误) | ||
| 104 | + | ||
| 105 | +```bash | ||
| 106 | +# 1. 创建有错误的文件 | ||
| 107 | +cat > bad.js << 'EOF' | ||
| 108 | +function test() { | ||
| 109 | + var x = 1 // 缺少分号,使用了 var | ||
| 110 | + return x | ||
| 111 | +} | ||
| 112 | +EOF | ||
| 113 | + | ||
| 114 | +# 2. 尝试提交 | ||
| 115 | +git add bad.js | ||
| 116 | +git commit -m "test: bad code" | ||
| 117 | + | ||
| 118 | +# 输出: | ||
| 119 | +# ✖ eslint --fix bad.js | ||
| 120 | +# ✖ 1:7 error Unexpected var, use let or const instead no-var | ||
| 121 | +# ✖ 1:18 error Missing semicolon | ||
| 122 | +# husky - pre-commit hook exited with code 1 (error) | ||
| 123 | +``` | ||
| 124 | + | ||
| 125 | +### 跳过 Hook(不推荐) | ||
| 126 | + | ||
| 127 | +```bash | ||
| 128 | +# 使用 --no-verify 跳过 hook | ||
| 129 | +git commit --no-verify -m "xxx" | ||
| 130 | +``` | ||
| 131 | + | ||
| 132 | +## 🛠️ 调试 | ||
| 133 | + | ||
| 134 | +### 测试 lint-staged | ||
| 135 | + | ||
| 136 | +```bash | ||
| 137 | +# 不实际提交,只测试 lint-staged | ||
| 138 | +npx lint-staged | ||
| 139 | +``` | ||
| 140 | + | ||
| 141 | +### 查看已安装的 Hooks | ||
| 142 | + | ||
| 143 | +```bash | ||
| 144 | +# 查看所有 Git hooks | ||
| 145 | +ls -la .husky/ | ||
| 146 | + | ||
| 147 | +# 查看 pre-commit 内容 | ||
| 148 | +cat .husky/pre-commit | ||
| 149 | +``` | ||
| 150 | + | ||
| 151 | +### 手动运行 Hook | ||
| 152 | + | ||
| 153 | +```bash | ||
| 154 | +# 手动执行 pre-commit hook | ||
| 155 | +.husky/pre-commit | ||
| 156 | +``` | ||
| 157 | + | ||
| 158 | +## 📋 常见问题 | ||
| 159 | + | ||
| 160 | +### 1. Hook 不执行 | ||
| 161 | + | ||
| 162 | +**检查**: | ||
| 163 | + | ||
| 164 | +```bash | ||
| 165 | +# 1. 检查 .husky 目录是否存在 | ||
| 166 | +ls -la .husky/ | ||
| 167 | + | ||
| 168 | +# 2. 检查 pre-commit 是否可执行 | ||
| 169 | +ls -la .husky/pre-commit | ||
| 170 | + | ||
| 171 | +# 3. 如果不可执行,添加权限 | ||
| 172 | +chmod +x .husky/pre-commit | ||
| 173 | +``` | ||
| 174 | + | ||
| 175 | +### 2. lint-staged 找不到文件 | ||
| 176 | + | ||
| 177 | +**原因**: 文件未被 `git add` 到暂存区 | ||
| 178 | + | ||
| 179 | +**解决**: | ||
| 180 | + | ||
| 181 | +```bash | ||
| 182 | +# 确保文件已添加到暂存区 | ||
| 183 | +git add your-file.js | ||
| 184 | + | ||
| 185 | +# 检查暂存区文件 | ||
| 186 | +git status | ||
| 187 | +``` | ||
| 188 | + | ||
| 189 | +### 3. 提交速度慢 | ||
| 190 | + | ||
| 191 | +**原因**: lint-staged 检查的文件太多 | ||
| 192 | + | ||
| 193 | +**优化**: | ||
| 194 | + | ||
| 195 | +- 缩小 `package.json` 中 `lint-staged` 的匹配范围 | ||
| 196 | +- 只检查 `src/` 目录 | ||
| 197 | + | ||
| 198 | +```json | ||
| 199 | +{ | ||
| 200 | + "lint-staged": { | ||
| 201 | + "src/**/*.{js,vue}": ["eslint --fix", "prettier --write"] | ||
| 202 | + } | ||
| 203 | +} | ||
| 204 | +``` | ||
| 205 | + | ||
| 206 | +### 4. 想暂时禁用 Hook | ||
| 207 | + | ||
| 208 | +**方法 1** - 跳过单次提交: | ||
| 209 | + | ||
| 210 | +```bash | ||
| 211 | +git commit --no-verify -m "xxx" | ||
| 212 | +``` | ||
| 213 | + | ||
| 214 | +**方法 2** - 暂时删除 hook: | ||
| 215 | + | ||
| 216 | +```bash | ||
| 217 | +# 删除 hook | ||
| 218 | +rm .husky/pre-commit | ||
| 219 | + | ||
| 220 | +# 提交后恢复 | ||
| 221 | +git checkout .husky/pre-commit | ||
| 222 | +``` | ||
| 223 | + | ||
| 224 | +## 🎯 最佳实践 | ||
| 225 | + | ||
| 226 | +### 1. 提交前检查 | ||
| 227 | + | ||
| 228 | +即使有 Husky,建议提交前也手动检查: | ||
| 229 | + | ||
| 230 | +```bash | ||
| 231 | +# 1. 格式化代码 | ||
| 232 | +pnpm format | ||
| 233 | + | ||
| 234 | +# 2. Lint 检查 | ||
| 235 | +pnpm lint:check | ||
| 236 | + | ||
| 237 | +# 3. 测试 | ||
| 238 | +pnpm test | ||
| 239 | + | ||
| 240 | +# 4. 提交(Husky 会再次检查) | ||
| 241 | +git add . | ||
| 242 | +git commit -m "xxx" | ||
| 243 | +``` | ||
| 244 | + | ||
| 245 | +### 2. 团队协作 | ||
| 246 | + | ||
| 247 | +**确保团队成员都安装了依赖**: | ||
| 248 | + | ||
| 249 | +```bash | ||
| 250 | +# 克隆项目后 | ||
| 251 | +pnpm install | ||
| 252 | + | ||
| 253 | +# Husky 会自动安装(通过 prepare 脚本) | ||
| 254 | +``` | ||
| 255 | + | ||
| 256 | +**`package.json` 中的 `prepare` 脚本**: | ||
| 257 | + | ||
| 258 | +```json | ||
| 259 | +{ | ||
| 260 | + "scripts": { | ||
| 261 | + "prepare": "husky" | ||
| 262 | + } | ||
| 263 | +} | ||
| 264 | +``` | ||
| 265 | + | ||
| 266 | +这确保了每次 `pnpm install` 后都会自动安装 Husky hooks。 | ||
| 267 | + | ||
| 268 | +### 3. CI/CD 集成 | ||
| 269 | + | ||
| 270 | +在 CI 中可以跳过 Husky(因为 CI 环境不使用 Git hooks): | ||
| 271 | + | ||
| 272 | +```yaml | ||
| 273 | +# .github/workflows/ci.yml | ||
| 274 | +- name: Lint and format | ||
| 275 | + run: | | ||
| 276 | + pnpm lint:check | ||
| 277 | + pnpm format:check | ||
| 278 | +``` | ||
| 279 | + | ||
| 280 | +## 🔄 更新配置 | ||
| 281 | + | ||
| 282 | +### 添加新的 Hook | ||
| 283 | + | ||
| 284 | +```bash | ||
| 285 | +# 创建 commit-msg hook(检查提交信息) | ||
| 286 | +cat > .husky/commit-msg << 'EOF' | ||
| 287 | +#!/usr/bin/env sh | ||
| 288 | +. "$(dirname "$0")/_/husky.sh" | ||
| 289 | + | ||
| 290 | +npx commitlint --edit $1 | ||
| 291 | +EOF | ||
| 292 | + | ||
| 293 | +chmod +x .husky/commit-msg | ||
| 294 | +``` | ||
| 295 | + | ||
| 296 | +### 修改 lint-staged 配置 | ||
| 297 | + | ||
| 298 | +编辑 `package.json` 中的 `lint-staged` 字段: | ||
| 299 | + | ||
| 300 | +```json | ||
| 301 | +{ | ||
| 302 | + "lint-staged": { | ||
| 303 | + "src/**/*.{js,vue}": ["eslint --fix", "prettier --write"], | ||
| 304 | + "src/**/*.{css,less}": ["prettier --write"], | ||
| 305 | + "*.md": ["prettier --write", "markdownlint --fix"] | ||
| 306 | + } | ||
| 307 | +} | ||
| 308 | +``` | ||
| 309 | + | ||
| 310 | +### 添加测试到 Hook | ||
| 311 | + | ||
| 312 | +编辑 `.husky/pre-commit`: | ||
| 313 | + | ||
| 314 | +```bash | ||
| 315 | +#!/usr/bin/env sh | ||
| 316 | +. "$(dirname "$0")/_/husky.sh" | ||
| 317 | + | ||
| 318 | +# 运行 lint-staged | ||
| 319 | +npx lint-staged | ||
| 320 | + | ||
| 321 | +# 运行测试(可选) | ||
| 322 | +pnpm test | ||
| 323 | +``` | ||
| 324 | + | ||
| 325 | +## 📚 参考资源 | ||
| 326 | + | ||
| 327 | +- [Husky 官方文档](https://typicode.github.io/husky/) | ||
| 328 | +- [lint-staged 官方文档](https://github.com/okonet/lint-staged) | ||
| 329 | +- [Git Hooks 文档](https://git-scm.com/docs/githooks) | ||
| 330 | + | ||
| 331 | +## 🎉 总结 | ||
| 332 | + | ||
| 333 | +**优势**: | ||
| 334 | + | ||
| 335 | +- ✅ 自动化代码质量检查 | ||
| 336 | +- ✅ 保持代码风格一致 | ||
| 337 | +- ✅ 防止低质量代码进入仓库 | ||
| 338 | +- ✅ 节省手动检查时间 | ||
| 339 | +- ✅ 团队协作更高效 | ||
| 340 | + | ||
| 341 | +**注意事项**: | ||
| 342 | + | ||
| 343 | +- ⚠️ Hook 只在本地执行,不会影响已经推送的代码 | ||
| 344 | +- ⚠️ 可以使用 `--no-verify` 跳过(不推荐) | ||
| 345 | +- ⚠️ CI/CD 中应该单独运行检查(不依赖 Hook) | ||
| 346 | + | ||
| 347 | +**下一步**: | ||
| 348 | + | ||
| 349 | +- [ ] 添加 commitlint(提交信息规范) | ||
| 350 | +- [ ] 添加更多 Git hooks(commit-msg、pre-push) | ||
| 351 | +- [ ] 配置 CI/CD 中的质量检查 | ||
| 352 | + | ||
| 353 | +享受自动化的 Git 工作流!🚀 |
eslint.config.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2026-01-28 20:45:00 | ||
| 3 | + * @Description: ESLint 配置 - 使用 ESLint 9 扁平配置格式 | ||
| 4 | + */ | ||
| 5 | +import vue from 'eslint-plugin-vue' | ||
| 6 | +import prettier from 'eslint-config-prettier' | ||
| 7 | + | ||
| 8 | +export default [ | ||
| 9 | + { | ||
| 10 | + // 忽略文件 | ||
| 11 | + ignores: [ | ||
| 12 | + 'node_modules/**', | ||
| 13 | + 'dist/**', | ||
| 14 | + 'dist.*', | ||
| 15 | + 'build/**', | ||
| 16 | + '*.min.js', | ||
| 17 | + 'public/**', | ||
| 18 | + 'VITE*.md', | ||
| 19 | + '.cursor/**', | ||
| 20 | + '.history/**', | ||
| 21 | + 'coverage/**', | ||
| 22 | + '.nyc_output/**', | ||
| 23 | + 'src/auto-imports.d.ts', | ||
| 24 | + 'src/components.d.ts', | ||
| 25 | + 'mlaj/**', | ||
| 26 | + ], | ||
| 27 | + }, | ||
| 28 | + | ||
| 29 | + // JavaScript / Vue 文件配置 | ||
| 30 | + { | ||
| 31 | + files: ['**/*.{js,mjs,cjs,vue}'], | ||
| 32 | + languageOptions: { | ||
| 33 | + ecmaVersion: 'latest', | ||
| 34 | + sourceType: 'module', | ||
| 35 | + globals: { | ||
| 36 | + // 浏览器环境 | ||
| 37 | + window: 'readonly', | ||
| 38 | + document: 'readonly', | ||
| 39 | + navigator: 'readonly', | ||
| 40 | + localStorage: 'readonly', | ||
| 41 | + sessionStorage: 'readonly', | ||
| 42 | + history: 'readonly', | ||
| 43 | + location: 'readonly', | ||
| 44 | + fetch: 'readonly', | ||
| 45 | + URL: 'readonly', | ||
| 46 | + XMLHttpRequest: 'readonly', | ||
| 47 | + WebSocket: 'readonly', | ||
| 48 | + HTMLElement: 'readonly', | ||
| 49 | + customElements: 'readonly', | ||
| 50 | + | ||
| 51 | + // 微信环境 | ||
| 52 | + wx: 'readonly', | ||
| 53 | + WeixinJSBridge: 'readonly', | ||
| 54 | + | ||
| 55 | + // Node.js 环境 | ||
| 56 | + process: 'readonly', | ||
| 57 | + Buffer: 'readonly', | ||
| 58 | + __dirname: 'readonly', | ||
| 59 | + __filename: 'readonly', | ||
| 60 | + module: 'readonly', | ||
| 61 | + require: 'readonly', | ||
| 62 | + exports: 'readonly', | ||
| 63 | + | ||
| 64 | + // Vitest 测试环境 | ||
| 65 | + describe: 'readonly', | ||
| 66 | + it: 'readonly', | ||
| 67 | + test: 'readonly', | ||
| 68 | + expect: 'readonly', | ||
| 69 | + vi: 'readonly', | ||
| 70 | + beforeEach: 'readonly', | ||
| 71 | + afterEach: 'readonly', | ||
| 72 | + beforeAll: 'readonly', | ||
| 73 | + afterAll: 'readonly', | ||
| 74 | + }, | ||
| 75 | + }, | ||
| 76 | + plugins: { | ||
| 77 | + vue, | ||
| 78 | + }, | ||
| 79 | + rules: { | ||
| 80 | + // Vue 3 规则 | ||
| 81 | + 'vue/multi-word-component-names': 'off', // 允许单词组件名 | ||
| 82 | + 'vue/no-v-html': 'warn', // v-html 需谨慎使用 | ||
| 83 | + 'vue/require-default-prop': 'off', // props 不强制默认值 | ||
| 84 | + 'vue/require-prop-types': 'error', // props 必须有类型 | ||
| 85 | + 'vue/no-mutating-props': 'error', // 禁止直接修改 props | ||
| 86 | + 'vue/component-definition-name-casing': ['error', 'PascalCase'], // 组件名 PascalCase | ||
| 87 | + 'vue/component-name-in-template-casing': ['error', 'PascalCase'], // 模板中组件名 PascalCase | ||
| 88 | + 'vue/no-unused-vars': 'error', // 禁止未使用的变量 | ||
| 89 | + 'vue/padding-line-between-blocks': 'warn', // 块之间空行 | ||
| 90 | + | ||
| 91 | + // 通用 JavaScript 规则 | ||
| 92 | + 'no-console': ['warn', { allow: ['warn', 'error'] }], // 禁止 console.log(允许 warn/error) | ||
| 93 | + 'no-debugger': 'error', // 禁止 debugger | ||
| 94 | + 'no-alert': 'warn', // 警告使用 alert | ||
| 95 | + 'no-var': 'error', // 禁止 var,使用 const/let | ||
| 96 | + 'prefer-const': 'error', // 优先使用 const | ||
| 97 | + 'no-duplicate-imports': 'error', // 禁止重复导入 | ||
| 98 | + 'no-unused-vars': 'off', // Vue 已处理 | ||
| 99 | + 'no-const-assign': 'error', // 禁止重新赋值 const | ||
| 100 | + 'prefer-template': 'warn', // 优先使用模板字符串 | ||
| 101 | + 'template-curly-spacing': 'error', // 模板字符串空格 | ||
| 102 | + 'object-shorthand': ['warn', 'always'], // 对象简写 | ||
| 103 | + 'prefer-arrow-callback': 'warn', // 优先使用箭头函数 | ||
| 104 | + 'arrow-spacing': 'error', // 箭头函数空格 | ||
| 105 | + 'arrow-parens': ['warn', 'as-needed'], // 箭头函数括号 | ||
| 106 | + 'arrow-body-style': ['warn', 'as-needed'], // 箭头函数体 | ||
| 107 | + | ||
| 108 | + // 代码质量 | ||
| 109 | + eqeqeq: ['error', 'always', { null: 'ignore' }], // 强制 === | ||
| 110 | + curly: ['error', 'all'], // 强制花括号 | ||
| 111 | + 'brace-style': ['error', '1tbs', { allowSingleLine: true }], // 花括号风格 | ||
| 112 | + 'no-else-return': 'warn', // if 中 return 后不要 else | ||
| 113 | + 'no-nested-ternary': 'warn', // 禁止嵌套三元 | ||
| 114 | + 'no-unneeded-ternary': 'error', // 禁止不必要的三元 | ||
| 115 | + 'prefer-destructuring': ['warn', { array: true, object: true }], // 优先解构 | ||
| 116 | + | ||
| 117 | + // 异步 | ||
| 118 | + 'no-async-promise-executor': 'error', // 禁止 async promise executor | ||
| 119 | + 'no-await-in-loop': 'warn', // 循环中的 await 警告 | ||
| 120 | + 'require-await': 'error', // async 函数必须有 await | ||
| 121 | + 'prefer-promise-reject-errors': 'error', // Promise reject 必须有 Error | ||
| 122 | + | ||
| 123 | + // 错误处理 | ||
| 124 | + 'no-throw-literal': 'error', // throw 必须是 Error 对象 | ||
| 125 | + 'prefer-promise-reject-errors': 'error', // Promise reject 必须是 Error | ||
| 126 | + | ||
| 127 | + // 性能 | ||
| 128 | + 'no-loop-func': 'warn', // 循环中不要创建函数 | ||
| 129 | + 'no-new-func': 'error', // 禁止 new Function | ||
| 130 | + | ||
| 131 | + // 安全 | ||
| 132 | + 'no-eval': 'error', // 禁止 eval | ||
| 133 | + 'no-implied-eval': 'error', // 禁止隐式 eval | ||
| 134 | + 'no-new-object': 'error', // 禁止 new Object() | ||
| 135 | + 'no-script-url': 'error', // 禁止 javascript: | ||
| 136 | + }, | ||
| 137 | + }, | ||
| 138 | + | ||
| 139 | + // 测试文件配置 | ||
| 140 | + { | ||
| 141 | + files: ['**/*.test.js', '**/*.spec.js', 'test/**'], | ||
| 142 | + languageOptions: { | ||
| 143 | + globals: { | ||
| 144 | + describe: 'readonly', | ||
| 145 | + it: 'readonly', | ||
| 146 | + test: 'readonly', | ||
| 147 | + expect: 'readonly', | ||
| 148 | + vi: 'readonly', | ||
| 149 | + beforeEach: 'readonly', | ||
| 150 | + afterEach: 'readonly', | ||
| 151 | + beforeAll: 'readonly', | ||
| 152 | + afterAll: 'readonly', | ||
| 153 | + }, | ||
| 154 | + }, | ||
| 155 | + }, | ||
| 156 | + | ||
| 157 | + // Prettier 配置(必须最后) | ||
| 158 | + prettier, | ||
| 159 | +] |
| ... | @@ -8,6 +8,12 @@ | ... | @@ -8,6 +8,12 @@ |
| 8 | "build": ". ~/.nvm/nvm.sh && nvm use 18.19.1 && vite build", | 8 | "build": ". ~/.nvm/nvm.sh && nvm use 18.19.1 && vite build", |
| 9 | "preview": "vite preview", | 9 | "preview": "vite preview", |
| 10 | "test": "vitest run", | 10 | "test": "vitest run", |
| 11 | + "test:ui": "vitest --ui", | ||
| 12 | + "test:coverage": "vitest --coverage", | ||
| 13 | + "lint": "eslint . --fix", | ||
| 14 | + "lint:check": "eslint .", | ||
| 15 | + "format": "prettier --write \"src/**/*.{js,vue,css,less,md,json}\"", | ||
| 16 | + "format:check": "prettier --check \"src/**/*.{js,vue,css,less,md,json}\"", | ||
| 11 | "mcp:speckit": "node mcp/speckit_stdio_server.js", | 17 | "mcp:speckit": "node mcp/speckit_stdio_server.js", |
| 12 | "tar": "tar -czvpf dist.tar.gz mlaj", | 18 | "tar": "tar -czvpf dist.tar.gz mlaj", |
| 13 | "build_tar": "npm run build && npm run tar", | 19 | "build_tar": "npm run build && npm run tar", |
| ... | @@ -20,7 +26,20 @@ | ... | @@ -20,7 +26,20 @@ |
| 20 | "remove_tar": "rm -rf dist.tar.gz", | 26 | "remove_tar": "rm -rf dist.tar.gz", |
| 21 | "dev_upload": "npm run build_tar && npm run scp-dev && npm run dec-dev && npm run remove_tar", | 27 | "dev_upload": "npm run build_tar && npm run scp-dev && npm run dec-dev && npm run remove_tar", |
| 22 | "behalo_upload": "npm run build_tar && npm run scp-behalo && npm run dec-behalo && npm run remove_tar", | 28 | "behalo_upload": "npm run build_tar && npm run scp-behalo && npm run dec-behalo && npm run remove_tar", |
| 23 | - "oa_upload": "npm run build_tar && npm run scp-oa && npm run dec-oa && npm run remove_tar" | 29 | + "oa_upload": "npm run build_tar && npm run scp-oa && npm run dec-oa && npm run remove_tar", |
| 30 | + "prepare": "husky" | ||
| 31 | + }, | ||
| 32 | + "lint-staged": { | ||
| 33 | + "*.{js,vue}": [ | ||
| 34 | + "eslint --fix", | ||
| 35 | + "prettier --write" | ||
| 36 | + ], | ||
| 37 | + "*.{css,less,scss}": [ | ||
| 38 | + "prettier --write" | ||
| 39 | + ], | ||
| 40 | + "*.{json,md}": [ | ||
| 41 | + "prettier --write" | ||
| 42 | + ] | ||
| 24 | }, | 43 | }, |
| 25 | "dependencies": { | 44 | "dependencies": { |
| 26 | "@fortawesome/fontawesome-svg-core": "^6.5.1", | 45 | "@fortawesome/fontawesome-svg-core": "^6.5.1", |
| ... | @@ -56,15 +75,24 @@ | ... | @@ -56,15 +75,24 @@ |
| 56 | "weixin-js-sdk": "^1.6.5" | 75 | "weixin-js-sdk": "^1.6.5" |
| 57 | }, | 76 | }, |
| 58 | "devDependencies": { | 77 | "devDependencies": { |
| 78 | + "@playwright/test": "^1.58.0", | ||
| 59 | "@vitejs/plugin-vue": "^5.2.1", | 79 | "@vitejs/plugin-vue": "^5.2.1", |
| 60 | "@vitejs/plugin-vue-jsx": "^4.1.2", | 80 | "@vitejs/plugin-vue-jsx": "^4.1.2", |
| 81 | + "@vue/eslint-config-prettier": "^10.2.0", | ||
| 61 | "@vue/test-utils": "^2.4.6", | 82 | "@vue/test-utils": "^2.4.6", |
| 62 | "@vueuse/core": "^13.0.0", | 83 | "@vueuse/core": "^13.0.0", |
| 63 | "autoprefixer": "^10.4.19", | 84 | "autoprefixer": "^10.4.19", |
| 64 | "axios": "^1.8.4", | 85 | "axios": "^1.8.4", |
| 86 | + "eslint": "^9.39.2", | ||
| 87 | + "eslint-config-prettier": "^10.1.8", | ||
| 88 | + "eslint-plugin-vue": "^10.7.0", | ||
| 89 | + "husky": "^9.1.7", | ||
| 65 | "jsdom": "^24.1.3", | 90 | "jsdom": "^24.1.3", |
| 66 | "less": "^4.2.2", | 91 | "less": "^4.2.2", |
| 92 | + "lint-staged": "^16.2.7", | ||
| 67 | "postcss": "^8.4.35", | 93 | "postcss": "^8.4.35", |
| 94 | + "prettier": "^3.8.1", | ||
| 95 | + "prettier-plugin-tailwindcss": "^0.7.2", | ||
| 68 | "qs": "^6.14.0", | 96 | "qs": "^6.14.0", |
| 69 | "rusha": "^0.8.14", | 97 | "rusha": "^0.8.14", |
| 70 | "tailwindcss": "^3.4.1", | 98 | "tailwindcss": "^3.4.1", | ... | ... |
This diff is collapsed. Click to expand it.
-
Please register or login to post a comment