@robot-admin/git-standards 1.0.0 → 1.0.2
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/README.md +320 -27
- package/dist/cli/doctor.cjs.map +1 -1
- package/dist/cli/doctor.js +1 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/init.cjs +10 -6
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +12 -8
- package/dist/cli/init.js.map +1 -1
- package/dist/index.cjs +12 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +13 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @robot-admin/git-standards
|
|
2
2
|
|
|
3
|
-
零配置 · 模块化 · Git 工程化标准工具包
|
|
3
|
+
> **v1.0.1** · 零配置 · 模块化 · Git 工程化标准工具包
|
|
4
4
|
|
|
5
5
|
集成 Commitizen + Commitlint + Husky + ESLint + Prettier + Oxlint + lint-staged,支持按需选配。
|
|
6
6
|
|
|
@@ -236,6 +236,243 @@ module.exports = {
|
|
|
236
236
|
| `.editorconfig` | - | ✔ | ✔ | 编辑器统一配置 |
|
|
237
237
|
| `.prettierrc.js` | - | - | ✔ | 代码格式化配置 |
|
|
238
238
|
|
|
239
|
+
### 各文件内容详解
|
|
240
|
+
|
|
241
|
+
#### `.cz-config.js` — Commitizen 交互式提交配置
|
|
242
|
+
|
|
243
|
+
定义了 `git cz` 交互式提交的类型、消息模板和作用域。
|
|
244
|
+
|
|
245
|
+
**生成内容**:
|
|
246
|
+
|
|
247
|
+
```js
|
|
248
|
+
module.exports = {
|
|
249
|
+
scopes: [], // 默认空,可自定义添加预设 scope
|
|
250
|
+
allowEmptyScopes: false, // 不允许空 scope
|
|
251
|
+
allowCustomScopes: true, // 允许自由输入 scope
|
|
252
|
+
|
|
253
|
+
types: [
|
|
254
|
+
{ value: "wip", name: "wip: 🚧 开发中" },
|
|
255
|
+
{ value: "feat", name: "feat: 🎯 新功能" },
|
|
256
|
+
{ value: "fix", name: "fix: 🐛 Bug 修复" },
|
|
257
|
+
{ value: "perf", name: "perf: ⚡️ 性能优化" },
|
|
258
|
+
{ value: "deps", name: "deps: 📦 依赖更新" },
|
|
259
|
+
{ value: "refactor", name: "refactor: ♻️ 重构" },
|
|
260
|
+
{ value: "docs", name: "docs: 📚 文档变更" },
|
|
261
|
+
{ value: "test", name: "test: 🔎 测试相关" },
|
|
262
|
+
{ value: "style", name: "style: 💄 代码样式" },
|
|
263
|
+
{ value: "build", name: "build: 🧳 构建/打包" },
|
|
264
|
+
{ value: "chore", name: "chore: 🔧 其他杂项" },
|
|
265
|
+
{ value: "revert", name: "revert: 🔙 回退" },
|
|
266
|
+
],
|
|
267
|
+
|
|
268
|
+
messages: {
|
|
269
|
+
type: "请选择提交类型:",
|
|
270
|
+
customScope: "请输入修改范围(必填,格式如:模块/子模块):",
|
|
271
|
+
subject: "请简要描述提交(必填,不加句号):",
|
|
272
|
+
body: "请输入更详细的说明(可选):\n",
|
|
273
|
+
footer: 'Footer(可选): 例如 "Closes #123" 或 "Release-As: 1.3.1"\n',
|
|
274
|
+
confirmCommit: "确认提交以上内容?(y/n/e/h)",
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
skipQuestions: ["body"],
|
|
278
|
+
allowBreakingChanges: ["feat", "fix", "refactor"],
|
|
279
|
+
breakingPrefix: "BREAKING CHANGE:",
|
|
280
|
+
subjectLimit: 88,
|
|
281
|
+
};
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
#### `commitlint.config.js` — 提交信息校验规则
|
|
287
|
+
|
|
288
|
+
校验 `git commit` 消息是否符合约定式提交规范。
|
|
289
|
+
|
|
290
|
+
**生成内容**:
|
|
291
|
+
|
|
292
|
+
```js
|
|
293
|
+
module.exports = {
|
|
294
|
+
extends: ["@commitlint/config-conventional"],
|
|
295
|
+
rules: {
|
|
296
|
+
"type-enum": [
|
|
297
|
+
2,
|
|
298
|
+
"always",
|
|
299
|
+
[
|
|
300
|
+
"wip",
|
|
301
|
+
"feat",
|
|
302
|
+
"fix",
|
|
303
|
+
"perf",
|
|
304
|
+
"deps",
|
|
305
|
+
"refactor",
|
|
306
|
+
"docs",
|
|
307
|
+
"test",
|
|
308
|
+
"style",
|
|
309
|
+
"build",
|
|
310
|
+
"chore",
|
|
311
|
+
"revert",
|
|
312
|
+
],
|
|
313
|
+
],
|
|
314
|
+
"type-case": [2, "always", "lower-case"],
|
|
315
|
+
"subject-empty": [2, "never"],
|
|
316
|
+
"type-empty": [2, "never"],
|
|
317
|
+
"subject-full-stop": [0, "never"],
|
|
318
|
+
"header-max-length": [2, "always", 88],
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
#### `.husky/commit-msg` — 提交信息校验 Hook
|
|
326
|
+
|
|
327
|
+
Git commit 时自动触发,校验提交信息格式是否合规。
|
|
328
|
+
|
|
329
|
+
**生成内容**:
|
|
330
|
+
|
|
331
|
+
```shell
|
|
332
|
+
bunx --no-install commitlint --edit "$1"
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
> 执行命令因包管理器不同而异(`bunx` / `npx` / `pnpm exec`)
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
#### `.husky/pre-commit` — 代码质量检查 Hook
|
|
340
|
+
|
|
341
|
+
Git commit 前自动触发,根据启用功能动态生成。
|
|
342
|
+
|
|
343
|
+
**极简模式**: 无此文件
|
|
344
|
+
|
|
345
|
+
**标准模式**(ESLint + lint-staged):
|
|
346
|
+
|
|
347
|
+
```shell
|
|
348
|
+
bunx lint-staged
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**完整模式**(Oxlint + lint-staged):
|
|
352
|
+
|
|
353
|
+
```shell
|
|
354
|
+
bunx oxlint --max-warnings 0
|
|
355
|
+
bunx lint-staged
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
> 文件会自动设置可执行权限(`chmod 755`),确保在 Git Bash / WSL 环境下正常运行。
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
#### `eslint.config.ts` — ESLint Flat Config
|
|
363
|
+
|
|
364
|
+
根据框架(Vue/React/Vanilla)、TypeScript、JSDoc 选项动态生成。
|
|
365
|
+
|
|
366
|
+
**Vue 3 + TypeScript 示例**:
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
import pluginVue from "eslint-plugin-vue";
|
|
370
|
+
import vueTsConfigs from "@vue/eslint-config-typescript";
|
|
371
|
+
import skipFormatting from "@vue/eslint-config-prettier/skip-formatting";
|
|
372
|
+
import oxlint from "eslint-plugin-oxlint"; // 完整模式
|
|
373
|
+
import jsdocPlugin from "eslint-plugin-jsdoc"; // 启用 JSDoc 时
|
|
374
|
+
import { defineConfigWithVueTs } from "@vue/eslint-config-typescript";
|
|
375
|
+
|
|
376
|
+
export default defineConfigWithVueTs(
|
|
377
|
+
{ name: "app/files-to-lint", files: ["**/*.{ts,mts,tsx,vue}"] },
|
|
378
|
+
{ name: "app/files-to-ignore", ignores: ["**/dist/**", "**/coverage/**"] },
|
|
379
|
+
|
|
380
|
+
...oxlint.configs["flat/recommended"], // Oxlint 基础规则
|
|
381
|
+
pluginVue.configs["flat/essential"], // Vue 规则
|
|
382
|
+
vueTsConfigs.recommended, // TS 规则
|
|
383
|
+
|
|
384
|
+
{
|
|
385
|
+
rules: {
|
|
386
|
+
"no-undef": "off",
|
|
387
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
388
|
+
"vue/multi-word-component-names": ["error", { ignores: ["index"] }],
|
|
389
|
+
// ... 更多规则
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
skipFormatting,
|
|
393
|
+
);
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**核心规则覆盖**: 每种框架预设都包含合理的默认规则,支持直接修改。
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
#### `.editorconfig` — 编辑器统一配置
|
|
401
|
+
|
|
402
|
+
统一团队成员的编辑器缩进、换行等基础格式。
|
|
403
|
+
|
|
404
|
+
**生成内容**:
|
|
405
|
+
|
|
406
|
+
```ini
|
|
407
|
+
root = true
|
|
408
|
+
|
|
409
|
+
[*]
|
|
410
|
+
charset = utf-8
|
|
411
|
+
indent_style = space
|
|
412
|
+
indent_size = 2
|
|
413
|
+
end_of_line = lf
|
|
414
|
+
insert_final_newline = true
|
|
415
|
+
trim_trailing_whitespace = true
|
|
416
|
+
|
|
417
|
+
[*.md]
|
|
418
|
+
trim_trailing_whitespace = false
|
|
419
|
+
|
|
420
|
+
[*.{yml,yaml}]
|
|
421
|
+
indent_size = 2
|
|
422
|
+
|
|
423
|
+
[Makefile]
|
|
424
|
+
indent_style = tab
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
#### `.prettierrc.js` — Prettier 格式化配置
|
|
430
|
+
|
|
431
|
+
代码自动格式化规则,仅完整模式生成。
|
|
432
|
+
|
|
433
|
+
**生成内容**:
|
|
434
|
+
|
|
435
|
+
```js
|
|
436
|
+
module.exports = {
|
|
437
|
+
singleQuote: true,
|
|
438
|
+
semi: false,
|
|
439
|
+
printWidth: 80,
|
|
440
|
+
trailingComma: "all",
|
|
441
|
+
arrowParens: "avoid",
|
|
442
|
+
endOfLine: "auto",
|
|
443
|
+
};
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
### package.json 自动变更
|
|
449
|
+
|
|
450
|
+
init 同时更新 `package.json` 中的以下字段:
|
|
451
|
+
|
|
452
|
+
| 字段 | 极简 | 标准 | 完整 | 内容 |
|
|
453
|
+
| ------------------- | :--: | :--: | :--: | ------------------------------ |
|
|
454
|
+
| `scripts.cz` | ✔ | ✔ | ✔ | `"git-cz"` |
|
|
455
|
+
| `scripts.prepare` | ✔ | ✔ | ✔ | `"husky"` |
|
|
456
|
+
| `scripts.lint` | - | ✔ | ✔ | `"eslint . --fix"` 或含 oxlint |
|
|
457
|
+
| `scripts.format` | - | - | ✔ | `"prettier --write src/"` |
|
|
458
|
+
| `config.commitizen` | ✔ | ✔ | ✔ | cz-customizable 路径 |
|
|
459
|
+
| `lint-staged` | - | ✔ | ✔ | 暂存区检查规则 |
|
|
460
|
+
|
|
461
|
+
**lint-staged 配置示例**(完整模式):
|
|
462
|
+
|
|
463
|
+
```jsonc
|
|
464
|
+
{
|
|
465
|
+
"lint-staged": {
|
|
466
|
+
"src/**/*.{js,jsx,ts,tsx,vue}": [
|
|
467
|
+
"oxlint --max-warnings 0 --deny-warnings",
|
|
468
|
+
"eslint --fix --no-cache",
|
|
469
|
+
"prettier --write"
|
|
470
|
+
],
|
|
471
|
+
"*.{json,md,yml,yaml}": ["prettier --write"]
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
239
476
|
## Git 提交完整流程
|
|
240
477
|
|
|
241
478
|
```
|
|
@@ -261,32 +498,6 @@ git commit(由 commitizen 触发)
|
|
|
261
498
|
提交成功 ✅
|
|
262
499
|
```
|
|
263
500
|
|
|
264
|
-
## package.json 变更
|
|
265
|
-
|
|
266
|
-
init 会自动更新 `package.json`:
|
|
267
|
-
|
|
268
|
-
```jsonc
|
|
269
|
-
{
|
|
270
|
-
"scripts": {
|
|
271
|
-
"cz": "git-cz", // 始终添加
|
|
272
|
-
"prepare": "husky", // 始终添加
|
|
273
|
-
"lint": "oxlint ... && eslint ...", // 仅标准/完整模式
|
|
274
|
-
"format": "prettier --write src/" // 仅完整模式
|
|
275
|
-
},
|
|
276
|
-
"config": {
|
|
277
|
-
"commitizen": { "path": "node_modules/cz-customizable" }
|
|
278
|
-
},
|
|
279
|
-
"lint-staged": {
|
|
280
|
-
// 仅标准/完整模式
|
|
281
|
-
"src/**/*.{js,jsx,ts,tsx,vue}": [
|
|
282
|
-
"oxlint --max-warnings 0 --deny-warnings",
|
|
283
|
-
"eslint --fix --no-cache",
|
|
284
|
-
"prettier --write"
|
|
285
|
-
]
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
```
|
|
289
|
-
|
|
290
501
|
## Doctor 诊断
|
|
291
502
|
|
|
292
503
|
检查当前项目的 Git 标准化配置状态:
|
|
@@ -319,6 +530,88 @@ node node_modules/@robot-admin/git-standards/bin/robot-standards.js doctor
|
|
|
319
530
|
○ EditorConfig
|
|
320
531
|
```
|
|
321
532
|
|
|
533
|
+
## 故障排除 (Troubleshooting)
|
|
534
|
+
|
|
535
|
+
### Husky Hooks 未执行
|
|
536
|
+
|
|
537
|
+
**问题现象**: 提交代码时没有触发 lint 检查,直接提交成功
|
|
538
|
+
|
|
539
|
+
**可能原因**: Git Bash / WSL 环境下,hook 文件缺少执行权限
|
|
540
|
+
|
|
541
|
+
**解决方案**:
|
|
542
|
+
|
|
543
|
+
```bash
|
|
544
|
+
# 检查 hook 文件权限
|
|
545
|
+
ls -la .husky/pre-commit .husky/commit-msg
|
|
546
|
+
|
|
547
|
+
# 如果没有 x 权限(应该显示 -rwxr-xr-x 而非 -rw-r--r--),执行:
|
|
548
|
+
chmod +x .husky/pre-commit .husky/commit-msg
|
|
549
|
+
|
|
550
|
+
# 验证修复
|
|
551
|
+
ls -la .husky/
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
**预防措施**: 初始化后立即检查权限
|
|
555
|
+
|
|
556
|
+
```bash
|
|
557
|
+
# 在 init 完成后执行
|
|
558
|
+
chmod +x .husky/*
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### Commitizen 提示 "git-cz command not found"
|
|
562
|
+
|
|
563
|
+
**解决方案**:
|
|
564
|
+
|
|
565
|
+
```bash
|
|
566
|
+
# 方式一:使用 npm scripts(推荐)
|
|
567
|
+
npm run cz
|
|
568
|
+
bun run cz
|
|
569
|
+
|
|
570
|
+
# 方式二:全局安装
|
|
571
|
+
npm install -g commitizen
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### lint-staged 报错 "Cannot find module"
|
|
575
|
+
|
|
576
|
+
**原因**: 依赖安装不完整
|
|
577
|
+
|
|
578
|
+
**解决方案**:
|
|
579
|
+
|
|
580
|
+
```bash
|
|
581
|
+
# 重新安装依赖
|
|
582
|
+
rm -rf node_modules
|
|
583
|
+
npm install
|
|
584
|
+
|
|
585
|
+
# 或使用 doctor 检查缺失的依赖
|
|
586
|
+
node node_modules/@robot-admin/git-standards/bin/robot-standards.js doctor
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### ESLint Flat Config 不生效
|
|
590
|
+
|
|
591
|
+
**原因**: 项目中同时存在旧版 `.eslintrc.*` 文件
|
|
592
|
+
|
|
593
|
+
**解决方案**:
|
|
594
|
+
|
|
595
|
+
```bash
|
|
596
|
+
# 删除旧配置文件(保留 eslint.config.ts)
|
|
597
|
+
rm .eslintrc.js .eslintrc.json .eslintrc.yml
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### Windows 下 Husky 不工作
|
|
601
|
+
|
|
602
|
+
**原因**: Git 配置的 core.hooksPath 可能被覆盖
|
|
603
|
+
|
|
604
|
+
**解决方案**:
|
|
605
|
+
|
|
606
|
+
```bash
|
|
607
|
+
# 检查 Git 配置
|
|
608
|
+
git config core.hooksPath
|
|
609
|
+
|
|
610
|
+
# 如果不是 .husky,重置它
|
|
611
|
+
git config --unset core.hooksPath
|
|
612
|
+
npm run prepare # 重新初始化 husky
|
|
613
|
+
```
|
|
614
|
+
|
|
322
615
|
## License
|
|
323
616
|
|
|
324
617
|
MIT
|
package/dist/cli/doctor.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/doctor.ts","../../src/utils/file.ts","../../src/utils/git.ts"],"sourcesContent":["/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Doctor 命令 - 智能诊断 Git 标准化配置\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { resolve } from \"node:path\";\r\nimport chalk from \"chalk\";\r\nimport { fileExists, readJsonFile } from \"../utils/file\";\r\nimport { isGitRepository } from \"../utils/git\";\r\n\r\n// ─── 品牌 & 符号 ──────────────────────────────────────────────────\r\nconst BRAND = \"#7C3AED\";\r\nconst S = {\r\n LOGO: chalk.hex(BRAND).bold(\"[RS]\"),\r\n OK: chalk.green(\"✔\"),\r\n FAIL: chalk.red(\"✖\"),\r\n WARN: chalk.yellow(\"▲\"),\r\n SKIP: chalk.gray(\"○\"),\r\n LINE: chalk.gray(\"─\".repeat(48)),\r\n};\r\n\r\nexport interface DoctorOptions {\r\n cwd?: string;\r\n}\r\n\r\ninterface CheckResult {\r\n name: string;\r\n status: \"pass\" | \"fail\" | \"skip\";\r\n message?: string;\r\n}\r\n\r\n/**\r\n * Doctor 命令主函数\r\n * 根据已安装的功能智能检测,未安装的功能只做提示不标记为失败\r\n */\r\nexport async function doctor(options: DoctorOptions = {}) {\r\n const cwd = options.cwd || process.cwd();\r\n\r\n // ── Banner ──\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${S.LOGO} ${chalk.bold(\"Robot Standards\")} ${chalk.gray(\"Doctor\")}`,\r\n );\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n const checks: CheckResult[] = [];\r\n\r\n // ── 1. 基础环境 ──\r\n const isGit = isGitRepository(cwd);\r\n checks.push({\r\n name: \"Git 仓库\",\r\n status: isGit ? \"pass\" : \"fail\",\r\n message: isGit ? undefined : \"未初始化 Git 仓库,运行 git init\",\r\n });\r\n\r\n // ── 2. 读取 package.json 探测已安装功能 ──\r\n let packageJson: any = {};\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const hasPackageJson = fileExists(packageJsonPath);\r\n\r\n if (hasPackageJson) {\r\n try {\r\n packageJson = await readJsonFile(packageJsonPath);\r\n } catch {\r\n // ignore\r\n }\r\n } else {\r\n checks.push({\r\n name: \"package.json\",\r\n status: \"fail\",\r\n message: \"缺少 package.json\",\r\n });\r\n }\r\n\r\n const allDeps = {\r\n ...packageJson.dependencies,\r\n ...packageJson.devDependencies,\r\n };\r\n\r\n // 检测已安装的功能模块\r\n const hasCommitizen = !!allDeps[\"commitizen\"];\r\n const hasCommitlint = !!allDeps[\"@commitlint/cli\"];\r\n const hasHusky = !!allDeps[\"husky\"];\r\n const hasEslint = !!allDeps[\"eslint\"];\r\n const hasLintStaged = !!allDeps[\"lint-staged\"];\r\n const hasPrettier = !!allDeps[\"prettier\"];\r\n\r\n // ── 3. 核心功能检查(始终检查) ──\r\n console.log(` ${chalk.bold(\"核心功能\")}`);\r\n console.log();\r\n\r\n // Husky\r\n if (hasHusky) {\r\n const huskyDir = fileExists(resolve(cwd, \".husky\"));\r\n checks.push({\r\n name: \"Husky 目录\",\r\n status: huskyDir ? \"pass\" : \"fail\",\r\n message: huskyDir ? undefined : \"缺少 .husky 目录\",\r\n });\r\n\r\n const commitMsg = fileExists(resolve(cwd, \".husky/commit-msg\"));\r\n checks.push({\r\n name: \"commit-msg hook\",\r\n status: commitMsg ? \"pass\" : \"fail\",\r\n message: commitMsg ? undefined : \"缺少 commit-msg hook\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Husky\",\r\n status: \"fail\",\r\n message: \"未安装 husky\",\r\n });\r\n }\r\n\r\n // Commitlint\r\n if (hasCommitlint) {\r\n const hasConfig =\r\n fileExists(resolve(cwd, \"commitlint.config.cjs\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.js\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.ts\"));\r\n checks.push({\r\n name: \"Commitlint 配置\",\r\n status: hasConfig ? \"pass\" : \"fail\",\r\n message: hasConfig ? undefined : \"缺少 commitlint.config.*\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitlint\",\r\n status: \"fail\",\r\n message: \"未安装 @commitlint/cli\",\r\n });\r\n }\r\n\r\n // Commitizen\r\n if (hasCommitizen) {\r\n const hasCzConfig =\r\n fileExists(resolve(cwd, \".cz-config.cjs\")) ||\r\n fileExists(resolve(cwd, \".cz-config.js\"));\r\n checks.push({\r\n name: \"Commitizen 配置\",\r\n status: hasCzConfig ? \"pass\" : \"fail\",\r\n message: hasCzConfig ? undefined : \"缺少 .cz-config.*\",\r\n });\r\n\r\n const hasCommitizenPath = !!packageJson.config?.commitizen;\r\n checks.push({\r\n name: \"commitizen path 配置\",\r\n status: hasCommitizenPath ? \"pass\" : \"fail\",\r\n message: hasCommitizenPath\r\n ? undefined\r\n : \"package.json 中缺少 config.commitizen\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitizen\",\r\n status: \"fail\",\r\n message: \"未安装 commitizen\",\r\n });\r\n }\r\n\r\n // cz 脚本\r\n const hasCzScript = !!packageJson.scripts?.cz;\r\n checks.push({\r\n name: \"cz 脚本\",\r\n status: hasCzScript ? \"pass\" : \"fail\",\r\n message: hasCzScript ? undefined : \"缺少 cz 脚本 (bun run cz)\",\r\n });\r\n\r\n // ── 输出核心检查结果 ──\r\n const coreChecks = checks.slice();\r\n for (const check of coreChecks) {\r\n printCheck(check);\r\n }\r\n\r\n // ── 4. 附加功能检查(仅检测已安装的) ──\r\n const extraChecks: CheckResult[] = [];\r\n\r\n if (hasEslint) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码检查\")}`);\r\n console.log();\r\n\r\n const hasEslintConfig =\r\n fileExists(resolve(cwd, \"eslint.config.ts\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.js\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.mjs\"));\r\n const eslintCheck: CheckResult = {\r\n name: \"ESLint 配置\",\r\n status: hasEslintConfig ? \"pass\" : \"fail\",\r\n message: hasEslintConfig ? undefined : \"缺少 eslint.config.*\",\r\n };\r\n extraChecks.push(eslintCheck);\r\n printCheck(eslintCheck);\r\n\r\n if (hasHusky) {\r\n const preCommit = fileExists(resolve(cwd, \".husky/pre-commit\"));\r\n const preCommitCheck: CheckResult = {\r\n name: \"pre-commit hook\",\r\n status: preCommit ? \"pass\" : \"fail\",\r\n message: preCommit ? undefined : \"缺少 pre-commit hook\",\r\n };\r\n extraChecks.push(preCommitCheck);\r\n printCheck(preCommitCheck);\r\n }\r\n }\r\n\r\n if (hasLintStaged) {\r\n const lintStagedConfig = !!packageJson[\"lint-staged\"];\r\n const lintStagedCheck: CheckResult = {\r\n name: \"lint-staged 配置\",\r\n status: lintStagedConfig ? \"pass\" : \"fail\",\r\n message: lintStagedConfig\r\n ? undefined\r\n : \"package.json 中缺少 lint-staged 配置\",\r\n };\r\n extraChecks.push(lintStagedCheck);\r\n printCheck(lintStagedCheck);\r\n }\r\n\r\n if (hasPrettier) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码格式化\")}`);\r\n console.log();\r\n\r\n const hasPrettierConfig =\r\n fileExists(resolve(cwd, \".prettierrc.cjs\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.js\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.json\")) ||\r\n fileExists(resolve(cwd, \".prettierrc\"));\r\n const prettierCheck: CheckResult = {\r\n name: \"Prettier 配置\",\r\n status: hasPrettierConfig ? \"pass\" : \"fail\",\r\n message: hasPrettierConfig ? undefined : \"缺少 .prettierrc.*\",\r\n };\r\n extraChecks.push(prettierCheck);\r\n printCheck(prettierCheck);\r\n }\r\n\r\n // EditorConfig(不依赖 package.json,直接检测文件)\r\n const hasEditorConfig = fileExists(resolve(cwd, \".editorconfig\"));\r\n if (hasEditorConfig) {\r\n // 只在存在时标记 pass,不存在不报错(属于可选功能)\r\n }\r\n\r\n // ── 5. 未安装功能提示 ──\r\n const notInstalled: string[] = [];\r\n if (!hasEslint) notInstalled.push(\"ESLint\");\r\n if (!hasLintStaged) notInstalled.push(\"lint-staged\");\r\n if (!hasPrettier) notInstalled.push(\"Prettier\");\r\n if (!hasEditorConfig) notInstalled.push(\"EditorConfig\");\r\n\r\n if (notInstalled.length > 0) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"未启用的功能\")}`);\r\n console.log();\r\n for (const name of notInstalled) {\r\n console.log(` ${S.SKIP} ${chalk.gray(name)}`);\r\n }\r\n }\r\n\r\n // ── 6. 汇总 ──\r\n const allChecks = [...coreChecks, ...extraChecks];\r\n const passedCount = allChecks.filter((c) => c.status === \"pass\").length;\r\n const failedCount = allChecks.filter((c) => c.status === \"fail\").length;\r\n\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${chalk.bold(\"总计:\")} ${chalk.green(`${passedCount} 通过`)} ${\r\n failedCount > 0 ? chalk.red(`${failedCount} 失败`) : chalk.gray(\"0 失败\")\r\n }`,\r\n );\r\n\r\n if (failedCount > 0) {\r\n console.log();\r\n console.log(\r\n ` ${S.WARN} ${chalk.yellow(\"运行\")} ${chalk.cyan(\r\n \"robot-standards init\",\r\n )} ${chalk.yellow(\"修复问题\")}`,\r\n );\r\n } else {\r\n console.log();\r\n console.log(` ${S.OK} ${chalk.green(\"所有检查通过!\")}`);\r\n }\r\n\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n return failedCount === 0;\r\n}\r\n\r\nfunction printCheck(check: CheckResult) {\r\n const icon =\r\n check.status === \"pass\" ? S.OK : check.status === \"fail\" ? S.FAIL : S.SKIP;\r\n console.log(` ${icon} ${check.name}`);\r\n if (check.message && check.status === \"fail\") {\r\n console.log(` ${chalk.gray(check.message)}`);\r\n }\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: 文件操作工具\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\r\nimport { resolve, dirname } from \"node:path\";\r\n\r\n/**\r\n * 检查文件是否存在\r\n */\r\nexport function fileExists(filePath: string): boolean {\r\n return existsSync(filePath);\r\n}\r\n\r\n/**\r\n * 读取文件内容\r\n */\r\nexport async function readFileContent(filePath: string): Promise<string> {\r\n return await readFile(filePath, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 写入文件内容\r\n */\r\nexport async function writeFileContent(\r\n filePath: string,\r\n content: string,\r\n): Promise<void> {\r\n const dir = dirname(filePath);\r\n if (!existsSync(dir)) {\r\n await mkdir(dir, { recursive: true });\r\n }\r\n await writeFile(filePath, content, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 读取并解析 JSON 文件\r\n */\r\nexport async function readJsonFile<T = any>(filePath: string): Promise<T> {\r\n const content = await readFileContent(filePath);\r\n return JSON.parse(content) as T;\r\n}\r\n\r\n/**\r\n * 写入 JSON 文件\r\n */\r\nexport async function writeJsonFile(\r\n filePath: string,\r\n data: any,\r\n pretty: boolean = true,\r\n): Promise<void> {\r\n const content = pretty\r\n ? JSON.stringify(data, null, 2) + \"\\n\"\r\n : JSON.stringify(data);\r\n await writeFileContent(filePath, content);\r\n}\r\n\r\n/**\r\n * 更新 package.json\r\n */\r\nexport async function updatePackageJson(\r\n updates: Record<string, any>,\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const packageJson = await readJsonFile(packageJsonPath);\r\n const updated = { ...packageJson, ...updates };\r\n await writeJsonFile(packageJsonPath, updated);\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Git 工具函数\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { resolve } from \"node:path\";\r\nimport { execa } from \"execa\";\r\n\r\n/**\r\n * 检查是否在 Git 仓库中\r\n */\r\nexport function isGitRepository(cwd: string = process.cwd()): boolean {\r\n return existsSync(resolve(cwd, \".git\"));\r\n}\r\n\r\n/**\r\n * 初始化 Git 仓库\r\n */\r\nexport async function initGitRepository(\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n if (!isGitRepository(cwd)) {\r\n await execa(\"git\", [\"init\"], { cwd });\r\n }\r\n}\r\n"],"mappings":"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","names":["import_node_path","import_node_fs","import_node_path","chalk"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/doctor.ts","../../src/utils/file.ts","../../src/utils/git.ts"],"sourcesContent":["/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Doctor 命令 - 智能诊断 Git 标准化配置\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { resolve } from \"node:path\";\r\nimport chalk from \"chalk\";\r\nimport { fileExists, readJsonFile } from \"../utils/file\";\r\nimport { isGitRepository } from \"../utils/git\";\r\n\r\n// ─── 品牌 & 符号 ──────────────────────────────────────────────────\r\nconst BRAND = \"#7C3AED\";\r\nconst S = {\r\n LOGO: chalk.hex(BRAND).bold(\"[RS]\"),\r\n OK: chalk.green(\"✔\"),\r\n FAIL: chalk.red(\"✖\"),\r\n WARN: chalk.yellow(\"▲\"),\r\n SKIP: chalk.gray(\"○\"),\r\n LINE: chalk.gray(\"─\".repeat(48)),\r\n};\r\n\r\nexport interface DoctorOptions {\r\n cwd?: string;\r\n}\r\n\r\ninterface CheckResult {\r\n name: string;\r\n status: \"pass\" | \"fail\" | \"skip\";\r\n message?: string;\r\n}\r\n\r\n/**\r\n * Doctor 命令主函数\r\n * 根据已安装的功能智能检测,未安装的功能只做提示不标记为失败\r\n */\r\nexport async function doctor(options: DoctorOptions = {}) {\r\n const cwd = options.cwd || process.cwd();\r\n\r\n // ── Banner ──\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${S.LOGO} ${chalk.bold(\"Robot Standards\")} ${chalk.gray(\"Doctor\")}`,\r\n );\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n const checks: CheckResult[] = [];\r\n\r\n // ── 1. 基础环境 ──\r\n const isGit = isGitRepository(cwd);\r\n checks.push({\r\n name: \"Git 仓库\",\r\n status: isGit ? \"pass\" : \"fail\",\r\n message: isGit ? undefined : \"未初始化 Git 仓库,运行 git init\",\r\n });\r\n\r\n // ── 2. 读取 package.json 探测已安装功能 ──\r\n let packageJson: any = {};\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const hasPackageJson = fileExists(packageJsonPath);\r\n\r\n if (hasPackageJson) {\r\n try {\r\n packageJson = await readJsonFile(packageJsonPath);\r\n } catch {\r\n // ignore\r\n }\r\n } else {\r\n checks.push({\r\n name: \"package.json\",\r\n status: \"fail\",\r\n message: \"缺少 package.json\",\r\n });\r\n }\r\n\r\n const allDeps = {\r\n ...packageJson.dependencies,\r\n ...packageJson.devDependencies,\r\n };\r\n\r\n // 检测已安装的功能模块\r\n const hasCommitizen = !!allDeps[\"commitizen\"];\r\n const hasCommitlint = !!allDeps[\"@commitlint/cli\"];\r\n const hasHusky = !!allDeps[\"husky\"];\r\n const hasEslint = !!allDeps[\"eslint\"];\r\n const hasLintStaged = !!allDeps[\"lint-staged\"];\r\n const hasPrettier = !!allDeps[\"prettier\"];\r\n\r\n // ── 3. 核心功能检查(始终检查) ──\r\n console.log(` ${chalk.bold(\"核心功能\")}`);\r\n console.log();\r\n\r\n // Husky\r\n if (hasHusky) {\r\n const huskyDir = fileExists(resolve(cwd, \".husky\"));\r\n checks.push({\r\n name: \"Husky 目录\",\r\n status: huskyDir ? \"pass\" : \"fail\",\r\n message: huskyDir ? undefined : \"缺少 .husky 目录\",\r\n });\r\n\r\n const commitMsg = fileExists(resolve(cwd, \".husky/commit-msg\"));\r\n checks.push({\r\n name: \"commit-msg hook\",\r\n status: commitMsg ? \"pass\" : \"fail\",\r\n message: commitMsg ? undefined : \"缺少 commit-msg hook\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Husky\",\r\n status: \"fail\",\r\n message: \"未安装 husky\",\r\n });\r\n }\r\n\r\n // Commitlint\r\n if (hasCommitlint) {\r\n const hasConfig =\r\n fileExists(resolve(cwd, \"commitlint.config.cjs\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.js\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.ts\"));\r\n checks.push({\r\n name: \"Commitlint 配置\",\r\n status: hasConfig ? \"pass\" : \"fail\",\r\n message: hasConfig ? undefined : \"缺少 commitlint.config.*\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitlint\",\r\n status: \"fail\",\r\n message: \"未安装 @commitlint/cli\",\r\n });\r\n }\r\n\r\n // Commitizen\r\n if (hasCommitizen) {\r\n const hasCzConfig =\r\n fileExists(resolve(cwd, \".cz-config.cjs\")) ||\r\n fileExists(resolve(cwd, \".cz-config.js\"));\r\n checks.push({\r\n name: \"Commitizen 配置\",\r\n status: hasCzConfig ? \"pass\" : \"fail\",\r\n message: hasCzConfig ? undefined : \"缺少 .cz-config.*\",\r\n });\r\n\r\n const hasCommitizenPath = !!packageJson.config?.commitizen;\r\n checks.push({\r\n name: \"commitizen path 配置\",\r\n status: hasCommitizenPath ? \"pass\" : \"fail\",\r\n message: hasCommitizenPath\r\n ? undefined\r\n : \"package.json 中缺少 config.commitizen\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitizen\",\r\n status: \"fail\",\r\n message: \"未安装 commitizen\",\r\n });\r\n }\r\n\r\n // cz 脚本\r\n const hasCzScript = !!packageJson.scripts?.cz;\r\n checks.push({\r\n name: \"cz 脚本\",\r\n status: hasCzScript ? \"pass\" : \"fail\",\r\n message: hasCzScript ? undefined : \"缺少 cz 脚本 (bun run cz)\",\r\n });\r\n\r\n // ── 输出核心检查结果 ──\r\n const coreChecks = checks.slice();\r\n for (const check of coreChecks) {\r\n printCheck(check);\r\n }\r\n\r\n // ── 4. 附加功能检查(仅检测已安装的) ──\r\n const extraChecks: CheckResult[] = [];\r\n\r\n if (hasEslint) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码检查\")}`);\r\n console.log();\r\n\r\n const hasEslintConfig =\r\n fileExists(resolve(cwd, \"eslint.config.ts\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.js\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.mjs\"));\r\n const eslintCheck: CheckResult = {\r\n name: \"ESLint 配置\",\r\n status: hasEslintConfig ? \"pass\" : \"fail\",\r\n message: hasEslintConfig ? undefined : \"缺少 eslint.config.*\",\r\n };\r\n extraChecks.push(eslintCheck);\r\n printCheck(eslintCheck);\r\n\r\n if (hasHusky) {\r\n const preCommit = fileExists(resolve(cwd, \".husky/pre-commit\"));\r\n const preCommitCheck: CheckResult = {\r\n name: \"pre-commit hook\",\r\n status: preCommit ? \"pass\" : \"fail\",\r\n message: preCommit ? undefined : \"缺少 pre-commit hook\",\r\n };\r\n extraChecks.push(preCommitCheck);\r\n printCheck(preCommitCheck);\r\n }\r\n }\r\n\r\n if (hasLintStaged) {\r\n const lintStagedConfig = !!packageJson[\"lint-staged\"];\r\n const lintStagedCheck: CheckResult = {\r\n name: \"lint-staged 配置\",\r\n status: lintStagedConfig ? \"pass\" : \"fail\",\r\n message: lintStagedConfig\r\n ? undefined\r\n : \"package.json 中缺少 lint-staged 配置\",\r\n };\r\n extraChecks.push(lintStagedCheck);\r\n printCheck(lintStagedCheck);\r\n }\r\n\r\n if (hasPrettier) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码格式化\")}`);\r\n console.log();\r\n\r\n const hasPrettierConfig =\r\n fileExists(resolve(cwd, \".prettierrc.cjs\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.js\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.json\")) ||\r\n fileExists(resolve(cwd, \".prettierrc\"));\r\n const prettierCheck: CheckResult = {\r\n name: \"Prettier 配置\",\r\n status: hasPrettierConfig ? \"pass\" : \"fail\",\r\n message: hasPrettierConfig ? undefined : \"缺少 .prettierrc.*\",\r\n };\r\n extraChecks.push(prettierCheck);\r\n printCheck(prettierCheck);\r\n }\r\n\r\n // EditorConfig(不依赖 package.json,直接检测文件)\r\n const hasEditorConfig = fileExists(resolve(cwd, \".editorconfig\"));\r\n if (hasEditorConfig) {\r\n // 只在存在时标记 pass,不存在不报错(属于可选功能)\r\n }\r\n\r\n // ── 5. 未安装功能提示 ──\r\n const notInstalled: string[] = [];\r\n if (!hasEslint) notInstalled.push(\"ESLint\");\r\n if (!hasLintStaged) notInstalled.push(\"lint-staged\");\r\n if (!hasPrettier) notInstalled.push(\"Prettier\");\r\n if (!hasEditorConfig) notInstalled.push(\"EditorConfig\");\r\n\r\n if (notInstalled.length > 0) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"未启用的功能\")}`);\r\n console.log();\r\n for (const name of notInstalled) {\r\n console.log(` ${S.SKIP} ${chalk.gray(name)}`);\r\n }\r\n }\r\n\r\n // ── 6. 汇总 ──\r\n const allChecks = [...coreChecks, ...extraChecks];\r\n const passedCount = allChecks.filter((c) => c.status === \"pass\").length;\r\n const failedCount = allChecks.filter((c) => c.status === \"fail\").length;\r\n\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${chalk.bold(\"总计:\")} ${chalk.green(`${passedCount} 通过`)} ${\r\n failedCount > 0 ? chalk.red(`${failedCount} 失败`) : chalk.gray(\"0 失败\")\r\n }`,\r\n );\r\n\r\n if (failedCount > 0) {\r\n console.log();\r\n console.log(\r\n ` ${S.WARN} ${chalk.yellow(\"运行\")} ${chalk.cyan(\r\n \"robot-standards init\",\r\n )} ${chalk.yellow(\"修复问题\")}`,\r\n );\r\n } else {\r\n console.log();\r\n console.log(` ${S.OK} ${chalk.green(\"所有检查通过!\")}`);\r\n }\r\n\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n return failedCount === 0;\r\n}\r\n\r\nfunction printCheck(check: CheckResult) {\r\n const icon =\r\n check.status === \"pass\" ? S.OK : check.status === \"fail\" ? S.FAIL : S.SKIP;\r\n console.log(` ${icon} ${check.name}`);\r\n if (check.message && check.status === \"fail\") {\r\n console.log(` ${chalk.gray(check.message)}`);\r\n }\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: 文件操作工具\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync, chmodSync } from \"node:fs\";\r\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\r\nimport { resolve, dirname } from \"node:path\";\r\n\r\n/**\r\n * 检查文件是否存在\r\n */\r\nexport function fileExists(filePath: string): boolean {\r\n return existsSync(filePath);\r\n}\r\n\r\n/**\r\n * 读取文件内容\r\n */\r\nexport async function readFileContent(filePath: string): Promise<string> {\r\n return await readFile(filePath, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 写入文件内容\r\n */\r\nexport async function writeFileContent(\r\n filePath: string,\r\n content: string,\r\n): Promise<void> {\r\n const dir = dirname(filePath);\r\n if (!existsSync(dir)) {\r\n await mkdir(dir, { recursive: true });\r\n }\r\n await writeFile(filePath, content, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 写入可执行文件(自动设置 chmod 755 执行权限)\r\n * 用于 Husky Hook 等需要执行权限的文件\r\n */\r\nexport async function writeExecutableFile(\r\n filePath: string,\r\n content: string,\r\n): Promise<void> {\r\n await writeFileContent(filePath, content);\r\n try {\r\n chmodSync(filePath, 0o755);\r\n } catch {\r\n // Windows 环境下 chmod 可能不生效,忽略错误\r\n }\r\n}\r\n\r\n/**\r\n * 读取并解析 JSON 文件\r\n */\r\nexport async function readJsonFile<T = any>(filePath: string): Promise<T> {\r\n const content = await readFileContent(filePath);\r\n return JSON.parse(content) as T;\r\n}\r\n\r\n/**\r\n * 写入 JSON 文件\r\n */\r\nexport async function writeJsonFile(\r\n filePath: string,\r\n data: any,\r\n pretty: boolean = true,\r\n): Promise<void> {\r\n const content = pretty\r\n ? JSON.stringify(data, null, 2) + \"\\n\"\r\n : JSON.stringify(data);\r\n await writeFileContent(filePath, content);\r\n}\r\n\r\n/**\r\n * 更新 package.json\r\n */\r\nexport async function updatePackageJson(\r\n updates: Record<string, any>,\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const packageJson = await readJsonFile(packageJsonPath);\r\n const updated = { ...packageJson, ...updates };\r\n await writeJsonFile(packageJsonPath, updated);\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Git 工具函数\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { resolve } from \"node:path\";\r\nimport { execa } from \"execa\";\r\n\r\n/**\r\n * 检查是否在 Git 仓库中\r\n */\r\nexport function isGitRepository(cwd: string = process.cwd()): boolean {\r\n return existsSync(resolve(cwd, \".git\"));\r\n}\r\n\r\n/**\r\n * 初始化 Git 仓库\r\n */\r\nexport async function initGitRepository(\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n if (!isGitRepository(cwd)) {\r\n await execa(\"git\", [\"init\"], { cwd });\r\n }\r\n}\r\n"],"mappings":"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","names":["import_node_path","import_node_fs","import_node_path","chalk"]}
|
package/dist/cli/doctor.js
CHANGED
|
@@ -3,7 +3,7 @@ import { resolve as resolve3 } from "path";
|
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
|
|
5
5
|
// src/utils/file.ts
|
|
6
|
-
import { existsSync } from "fs";
|
|
6
|
+
import { existsSync, chmodSync } from "fs";
|
|
7
7
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
8
8
|
import { resolve, dirname } from "path";
|
|
9
9
|
function fileExists(filePath) {
|
package/dist/cli/doctor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/doctor.ts","../../src/utils/file.ts","../../src/utils/git.ts"],"sourcesContent":["/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Doctor 命令 - 智能诊断 Git 标准化配置\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { resolve } from \"node:path\";\r\nimport chalk from \"chalk\";\r\nimport { fileExists, readJsonFile } from \"../utils/file\";\r\nimport { isGitRepository } from \"../utils/git\";\r\n\r\n// ─── 品牌 & 符号 ──────────────────────────────────────────────────\r\nconst BRAND = \"#7C3AED\";\r\nconst S = {\r\n LOGO: chalk.hex(BRAND).bold(\"[RS]\"),\r\n OK: chalk.green(\"✔\"),\r\n FAIL: chalk.red(\"✖\"),\r\n WARN: chalk.yellow(\"▲\"),\r\n SKIP: chalk.gray(\"○\"),\r\n LINE: chalk.gray(\"─\".repeat(48)),\r\n};\r\n\r\nexport interface DoctorOptions {\r\n cwd?: string;\r\n}\r\n\r\ninterface CheckResult {\r\n name: string;\r\n status: \"pass\" | \"fail\" | \"skip\";\r\n message?: string;\r\n}\r\n\r\n/**\r\n * Doctor 命令主函数\r\n * 根据已安装的功能智能检测,未安装的功能只做提示不标记为失败\r\n */\r\nexport async function doctor(options: DoctorOptions = {}) {\r\n const cwd = options.cwd || process.cwd();\r\n\r\n // ── Banner ──\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${S.LOGO} ${chalk.bold(\"Robot Standards\")} ${chalk.gray(\"Doctor\")}`,\r\n );\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n const checks: CheckResult[] = [];\r\n\r\n // ── 1. 基础环境 ──\r\n const isGit = isGitRepository(cwd);\r\n checks.push({\r\n name: \"Git 仓库\",\r\n status: isGit ? \"pass\" : \"fail\",\r\n message: isGit ? undefined : \"未初始化 Git 仓库,运行 git init\",\r\n });\r\n\r\n // ── 2. 读取 package.json 探测已安装功能 ──\r\n let packageJson: any = {};\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const hasPackageJson = fileExists(packageJsonPath);\r\n\r\n if (hasPackageJson) {\r\n try {\r\n packageJson = await readJsonFile(packageJsonPath);\r\n } catch {\r\n // ignore\r\n }\r\n } else {\r\n checks.push({\r\n name: \"package.json\",\r\n status: \"fail\",\r\n message: \"缺少 package.json\",\r\n });\r\n }\r\n\r\n const allDeps = {\r\n ...packageJson.dependencies,\r\n ...packageJson.devDependencies,\r\n };\r\n\r\n // 检测已安装的功能模块\r\n const hasCommitizen = !!allDeps[\"commitizen\"];\r\n const hasCommitlint = !!allDeps[\"@commitlint/cli\"];\r\n const hasHusky = !!allDeps[\"husky\"];\r\n const hasEslint = !!allDeps[\"eslint\"];\r\n const hasLintStaged = !!allDeps[\"lint-staged\"];\r\n const hasPrettier = !!allDeps[\"prettier\"];\r\n\r\n // ── 3. 核心功能检查(始终检查) ──\r\n console.log(` ${chalk.bold(\"核心功能\")}`);\r\n console.log();\r\n\r\n // Husky\r\n if (hasHusky) {\r\n const huskyDir = fileExists(resolve(cwd, \".husky\"));\r\n checks.push({\r\n name: \"Husky 目录\",\r\n status: huskyDir ? \"pass\" : \"fail\",\r\n message: huskyDir ? undefined : \"缺少 .husky 目录\",\r\n });\r\n\r\n const commitMsg = fileExists(resolve(cwd, \".husky/commit-msg\"));\r\n checks.push({\r\n name: \"commit-msg hook\",\r\n status: commitMsg ? \"pass\" : \"fail\",\r\n message: commitMsg ? undefined : \"缺少 commit-msg hook\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Husky\",\r\n status: \"fail\",\r\n message: \"未安装 husky\",\r\n });\r\n }\r\n\r\n // Commitlint\r\n if (hasCommitlint) {\r\n const hasConfig =\r\n fileExists(resolve(cwd, \"commitlint.config.cjs\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.js\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.ts\"));\r\n checks.push({\r\n name: \"Commitlint 配置\",\r\n status: hasConfig ? \"pass\" : \"fail\",\r\n message: hasConfig ? undefined : \"缺少 commitlint.config.*\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitlint\",\r\n status: \"fail\",\r\n message: \"未安装 @commitlint/cli\",\r\n });\r\n }\r\n\r\n // Commitizen\r\n if (hasCommitizen) {\r\n const hasCzConfig =\r\n fileExists(resolve(cwd, \".cz-config.cjs\")) ||\r\n fileExists(resolve(cwd, \".cz-config.js\"));\r\n checks.push({\r\n name: \"Commitizen 配置\",\r\n status: hasCzConfig ? \"pass\" : \"fail\",\r\n message: hasCzConfig ? undefined : \"缺少 .cz-config.*\",\r\n });\r\n\r\n const hasCommitizenPath = !!packageJson.config?.commitizen;\r\n checks.push({\r\n name: \"commitizen path 配置\",\r\n status: hasCommitizenPath ? \"pass\" : \"fail\",\r\n message: hasCommitizenPath\r\n ? undefined\r\n : \"package.json 中缺少 config.commitizen\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitizen\",\r\n status: \"fail\",\r\n message: \"未安装 commitizen\",\r\n });\r\n }\r\n\r\n // cz 脚本\r\n const hasCzScript = !!packageJson.scripts?.cz;\r\n checks.push({\r\n name: \"cz 脚本\",\r\n status: hasCzScript ? \"pass\" : \"fail\",\r\n message: hasCzScript ? undefined : \"缺少 cz 脚本 (bun run cz)\",\r\n });\r\n\r\n // ── 输出核心检查结果 ──\r\n const coreChecks = checks.slice();\r\n for (const check of coreChecks) {\r\n printCheck(check);\r\n }\r\n\r\n // ── 4. 附加功能检查(仅检测已安装的) ──\r\n const extraChecks: CheckResult[] = [];\r\n\r\n if (hasEslint) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码检查\")}`);\r\n console.log();\r\n\r\n const hasEslintConfig =\r\n fileExists(resolve(cwd, \"eslint.config.ts\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.js\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.mjs\"));\r\n const eslintCheck: CheckResult = {\r\n name: \"ESLint 配置\",\r\n status: hasEslintConfig ? \"pass\" : \"fail\",\r\n message: hasEslintConfig ? undefined : \"缺少 eslint.config.*\",\r\n };\r\n extraChecks.push(eslintCheck);\r\n printCheck(eslintCheck);\r\n\r\n if (hasHusky) {\r\n const preCommit = fileExists(resolve(cwd, \".husky/pre-commit\"));\r\n const preCommitCheck: CheckResult = {\r\n name: \"pre-commit hook\",\r\n status: preCommit ? \"pass\" : \"fail\",\r\n message: preCommit ? undefined : \"缺少 pre-commit hook\",\r\n };\r\n extraChecks.push(preCommitCheck);\r\n printCheck(preCommitCheck);\r\n }\r\n }\r\n\r\n if (hasLintStaged) {\r\n const lintStagedConfig = !!packageJson[\"lint-staged\"];\r\n const lintStagedCheck: CheckResult = {\r\n name: \"lint-staged 配置\",\r\n status: lintStagedConfig ? \"pass\" : \"fail\",\r\n message: lintStagedConfig\r\n ? undefined\r\n : \"package.json 中缺少 lint-staged 配置\",\r\n };\r\n extraChecks.push(lintStagedCheck);\r\n printCheck(lintStagedCheck);\r\n }\r\n\r\n if (hasPrettier) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码格式化\")}`);\r\n console.log();\r\n\r\n const hasPrettierConfig =\r\n fileExists(resolve(cwd, \".prettierrc.cjs\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.js\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.json\")) ||\r\n fileExists(resolve(cwd, \".prettierrc\"));\r\n const prettierCheck: CheckResult = {\r\n name: \"Prettier 配置\",\r\n status: hasPrettierConfig ? \"pass\" : \"fail\",\r\n message: hasPrettierConfig ? undefined : \"缺少 .prettierrc.*\",\r\n };\r\n extraChecks.push(prettierCheck);\r\n printCheck(prettierCheck);\r\n }\r\n\r\n // EditorConfig(不依赖 package.json,直接检测文件)\r\n const hasEditorConfig = fileExists(resolve(cwd, \".editorconfig\"));\r\n if (hasEditorConfig) {\r\n // 只在存在时标记 pass,不存在不报错(属于可选功能)\r\n }\r\n\r\n // ── 5. 未安装功能提示 ──\r\n const notInstalled: string[] = [];\r\n if (!hasEslint) notInstalled.push(\"ESLint\");\r\n if (!hasLintStaged) notInstalled.push(\"lint-staged\");\r\n if (!hasPrettier) notInstalled.push(\"Prettier\");\r\n if (!hasEditorConfig) notInstalled.push(\"EditorConfig\");\r\n\r\n if (notInstalled.length > 0) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"未启用的功能\")}`);\r\n console.log();\r\n for (const name of notInstalled) {\r\n console.log(` ${S.SKIP} ${chalk.gray(name)}`);\r\n }\r\n }\r\n\r\n // ── 6. 汇总 ──\r\n const allChecks = [...coreChecks, ...extraChecks];\r\n const passedCount = allChecks.filter((c) => c.status === \"pass\").length;\r\n const failedCount = allChecks.filter((c) => c.status === \"fail\").length;\r\n\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${chalk.bold(\"总计:\")} ${chalk.green(`${passedCount} 通过`)} ${\r\n failedCount > 0 ? chalk.red(`${failedCount} 失败`) : chalk.gray(\"0 失败\")\r\n }`,\r\n );\r\n\r\n if (failedCount > 0) {\r\n console.log();\r\n console.log(\r\n ` ${S.WARN} ${chalk.yellow(\"运行\")} ${chalk.cyan(\r\n \"robot-standards init\",\r\n )} ${chalk.yellow(\"修复问题\")}`,\r\n );\r\n } else {\r\n console.log();\r\n console.log(` ${S.OK} ${chalk.green(\"所有检查通过!\")}`);\r\n }\r\n\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n return failedCount === 0;\r\n}\r\n\r\nfunction printCheck(check: CheckResult) {\r\n const icon =\r\n check.status === \"pass\" ? S.OK : check.status === \"fail\" ? S.FAIL : S.SKIP;\r\n console.log(` ${icon} ${check.name}`);\r\n if (check.message && check.status === \"fail\") {\r\n console.log(` ${chalk.gray(check.message)}`);\r\n }\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: 文件操作工具\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\r\nimport { resolve, dirname } from \"node:path\";\r\n\r\n/**\r\n * 检查文件是否存在\r\n */\r\nexport function fileExists(filePath: string): boolean {\r\n return existsSync(filePath);\r\n}\r\n\r\n/**\r\n * 读取文件内容\r\n */\r\nexport async function readFileContent(filePath: string): Promise<string> {\r\n return await readFile(filePath, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 写入文件内容\r\n */\r\nexport async function writeFileContent(\r\n filePath: string,\r\n content: string,\r\n): Promise<void> {\r\n const dir = dirname(filePath);\r\n if (!existsSync(dir)) {\r\n await mkdir(dir, { recursive: true });\r\n }\r\n await writeFile(filePath, content, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 读取并解析 JSON 文件\r\n */\r\nexport async function readJsonFile<T = any>(filePath: string): Promise<T> {\r\n const content = await readFileContent(filePath);\r\n return JSON.parse(content) as T;\r\n}\r\n\r\n/**\r\n * 写入 JSON 文件\r\n */\r\nexport async function writeJsonFile(\r\n filePath: string,\r\n data: any,\r\n pretty: boolean = true,\r\n): Promise<void> {\r\n const content = pretty\r\n ? JSON.stringify(data, null, 2) + \"\\n\"\r\n : JSON.stringify(data);\r\n await writeFileContent(filePath, content);\r\n}\r\n\r\n/**\r\n * 更新 package.json\r\n */\r\nexport async function updatePackageJson(\r\n updates: Record<string, any>,\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const packageJson = await readJsonFile(packageJsonPath);\r\n const updated = { ...packageJson, ...updates };\r\n await writeJsonFile(packageJsonPath, updated);\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Git 工具函数\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { resolve } from \"node:path\";\r\nimport { execa } from \"execa\";\r\n\r\n/**\r\n * 检查是否在 Git 仓库中\r\n */\r\nexport function isGitRepository(cwd: string = process.cwd()): boolean {\r\n return existsSync(resolve(cwd, \".git\"));\r\n}\r\n\r\n/**\r\n * 初始化 Git 仓库\r\n */\r\nexport async function initGitRepository(\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n if (!isGitRepository(cwd)) {\r\n await execa(\"git\", [\"init\"], { cwd });\r\n }\r\n}\r\n"],"mappings":";AAOA,SAAS,WAAAA,gBAAe;AACxB,OAAO,WAAW;;;ACDlB,SAAS,kBAAkB;AAC3B,SAAS,UAAU,WAAW,aAAa;AAC3C,SAAS,SAAS,eAAe;AAK1B,SAAS,WAAW,UAA2B;AACpD,SAAO,WAAW,QAAQ;AAC5B;AAKA,eAAsB,gBAAgB,UAAmC;AACvE,SAAO,MAAM,SAAS,UAAU,OAAO;AACzC;AAmBA,eAAsB,aAAsB,UAA8B;AACxE,QAAM,UAAU,MAAM,gBAAgB,QAAQ;AAC9C,SAAO,KAAK,MAAM,OAAO;AAC3B;;;ACtCA,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,WAAAC,gBAAe;AACxB,SAAS,aAAa;AAKf,SAAS,gBAAgB,MAAc,QAAQ,IAAI,GAAY;AACpE,SAAOD,YAAWC,SAAQ,KAAK,MAAM,CAAC;AACxC;;;AFHA,IAAM,QAAQ;AACd,IAAM,IAAI;AAAA,EACR,MAAM,MAAM,IAAI,KAAK,EAAE,KAAK,MAAM;AAAA,EAClC,IAAI,MAAM,MAAM,QAAG;AAAA,EACnB,MAAM,MAAM,IAAI,QAAG;AAAA,EACnB,MAAM,MAAM,OAAO,QAAG;AAAA,EACtB,MAAM,MAAM,KAAK,QAAG;AAAA,EACpB,MAAM,MAAM,KAAK,SAAI,OAAO,EAAE,CAAC;AACjC;AAgBA,eAAsB,OAAO,UAAyB,CAAC,GAAG;AACxD,QAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;AAGvC,UAAQ,IAAI;AACZ,UAAQ,IAAI,EAAE,IAAI;AAClB,UAAQ;AAAA,IACN,KAAK,EAAE,IAAI,KAAK,MAAM,KAAK,iBAAiB,CAAC,KAAK,MAAM,KAAK,QAAQ,CAAC;AAAA,EACxE;AACA,UAAQ,IAAI,EAAE,IAAI;AAClB,UAAQ,IAAI;AAEZ,QAAM,SAAwB,CAAC;AAG/B,QAAM,QAAQ,gBAAgB,GAAG;AACjC,SAAO,KAAK;AAAA,IACV,MAAM;AAAA,IACN,QAAQ,QAAQ,SAAS;AAAA,IACzB,SAAS,QAAQ,SAAY;AAAA,EAC/B,CAAC;AAGD,MAAI,cAAmB,CAAC;AACxB,QAAM,kBAAkBC,SAAQ,KAAK,cAAc;AACnD,QAAM,iBAAiB,WAAW,eAAe;AAEjD,MAAI,gBAAgB;AAClB,QAAI;AACF,oBAAc,MAAM,aAAa,eAAe;AAAA,IAClD,QAAQ;AAAA,IAER;AAAA,EACF,OAAO;AACL,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,QAAM,UAAU;AAAA,IACd,GAAG,YAAY;AAAA,IACf,GAAG,YAAY;AAAA,EACjB;AAGA,QAAM,gBAAgB,CAAC,CAAC,QAAQ,YAAY;AAC5C,QAAM,gBAAgB,CAAC,CAAC,QAAQ,iBAAiB;AACjD,QAAM,WAAW,CAAC,CAAC,QAAQ,OAAO;AAClC,QAAM,YAAY,CAAC,CAAC,QAAQ,QAAQ;AACpC,QAAM,gBAAgB,CAAC,CAAC,QAAQ,aAAa;AAC7C,QAAM,cAAc,CAAC,CAAC,QAAQ,UAAU;AAGxC,UAAQ,IAAI,KAAK,MAAM,KAAK,0BAAM,CAAC,EAAE;AACrC,UAAQ,IAAI;AAGZ,MAAI,UAAU;AACZ,UAAM,WAAW,WAAWA,SAAQ,KAAK,QAAQ,CAAC;AAClD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ,WAAW,SAAS;AAAA,MAC5B,SAAS,WAAW,SAAY;AAAA,IAClC,CAAC;AAED,UAAM,YAAY,WAAWA,SAAQ,KAAK,mBAAmB,CAAC;AAC9D,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ,YAAY,SAAS;AAAA,MAC7B,SAAS,YAAY,SAAY;AAAA,IACnC,CAAC;AAAA,EACH,OAAO;AACL,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,MAAI,eAAe;AACjB,UAAM,YACJ,WAAWA,SAAQ,KAAK,uBAAuB,CAAC,KAChD,WAAWA,SAAQ,KAAK,sBAAsB,CAAC,KAC/C,WAAWA,SAAQ,KAAK,sBAAsB,CAAC;AACjD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ,YAAY,SAAS;AAAA,MAC7B,SAAS,YAAY,SAAY;AAAA,IACnC,CAAC;AAAA,EACH,OAAO;AACL,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,MAAI,eAAe;AACjB,UAAM,cACJ,WAAWA,SAAQ,KAAK,gBAAgB,CAAC,KACzC,WAAWA,SAAQ,KAAK,eAAe,CAAC;AAC1C,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ,cAAc,SAAS;AAAA,MAC/B,SAAS,cAAc,SAAY;AAAA,IACrC,CAAC;AAED,UAAM,oBAAoB,CAAC,CAAC,YAAY,QAAQ;AAChD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ,oBAAoB,SAAS;AAAA,MACrC,SAAS,oBACL,SACA;AAAA,IACN,CAAC;AAAA,EACH,OAAO;AACL,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,QAAM,cAAc,CAAC,CAAC,YAAY,SAAS;AAC3C,SAAO,KAAK;AAAA,IACV,MAAM;AAAA,IACN,QAAQ,cAAc,SAAS;AAAA,IAC/B,SAAS,cAAc,SAAY;AAAA,EACrC,CAAC;AAGD,QAAM,aAAa,OAAO,MAAM;AAChC,aAAW,SAAS,YAAY;AAC9B,eAAW,KAAK;AAAA,EAClB;AAGA,QAAM,cAA6B,CAAC;AAEpC,MAAI,WAAW;AACb,YAAQ,IAAI;AACZ,YAAQ,IAAI,KAAK,MAAM,KAAK,0BAAM,CAAC,EAAE;AACrC,YAAQ,IAAI;AAEZ,UAAM,kBACJ,WAAWA,SAAQ,KAAK,kBAAkB,CAAC,KAC3C,WAAWA,SAAQ,KAAK,kBAAkB,CAAC,KAC3C,WAAWA,SAAQ,KAAK,mBAAmB,CAAC;AAC9C,UAAM,cAA2B;AAAA,MAC/B,MAAM;AAAA,MACN,QAAQ,kBAAkB,SAAS;AAAA,MACnC,SAAS,kBAAkB,SAAY;AAAA,IACzC;AACA,gBAAY,KAAK,WAAW;AAC5B,eAAW,WAAW;AAEtB,QAAI,UAAU;AACZ,YAAM,YAAY,WAAWA,SAAQ,KAAK,mBAAmB,CAAC;AAC9D,YAAM,iBAA8B;AAAA,QAClC,MAAM;AAAA,QACN,QAAQ,YAAY,SAAS;AAAA,QAC7B,SAAS,YAAY,SAAY;AAAA,MACnC;AACA,kBAAY,KAAK,cAAc;AAC/B,iBAAW,cAAc;AAAA,IAC3B;AAAA,EACF;AAEA,MAAI,eAAe;AACjB,UAAM,mBAAmB,CAAC,CAAC,YAAY,aAAa;AACpD,UAAM,kBAA+B;AAAA,MACnC,MAAM;AAAA,MACN,QAAQ,mBAAmB,SAAS;AAAA,MACpC,SAAS,mBACL,SACA;AAAA,IACN;AACA,gBAAY,KAAK,eAAe;AAChC,eAAW,eAAe;AAAA,EAC5B;AAEA,MAAI,aAAa;AACf,YAAQ,IAAI;AACZ,YAAQ,IAAI,KAAK,MAAM,KAAK,gCAAO,CAAC,EAAE;AACtC,YAAQ,IAAI;AAEZ,UAAM,oBACJ,WAAWA,SAAQ,KAAK,iBAAiB,CAAC,KAC1C,WAAWA,SAAQ,KAAK,gBAAgB,CAAC,KACzC,WAAWA,SAAQ,KAAK,kBAAkB,CAAC,KAC3C,WAAWA,SAAQ,KAAK,aAAa,CAAC;AACxC,UAAM,gBAA6B;AAAA,MACjC,MAAM;AAAA,MACN,QAAQ,oBAAoB,SAAS;AAAA,MACrC,SAAS,oBAAoB,SAAY;AAAA,IAC3C;AACA,gBAAY,KAAK,aAAa;AAC9B,eAAW,aAAa;AAAA,EAC1B;AAGA,QAAM,kBAAkB,WAAWA,SAAQ,KAAK,eAAe,CAAC;AAChE,MAAI,iBAAiB;AAAA,EAErB;AAGA,QAAM,eAAyB,CAAC;AAChC,MAAI,CAAC,UAAW,cAAa,KAAK,QAAQ;AAC1C,MAAI,CAAC,cAAe,cAAa,KAAK,aAAa;AACnD,MAAI,CAAC,YAAa,cAAa,KAAK,UAAU;AAC9C,MAAI,CAAC,gBAAiB,cAAa,KAAK,cAAc;AAEtD,MAAI,aAAa,SAAS,GAAG;AAC3B,YAAQ,IAAI;AACZ,YAAQ,IAAI,KAAK,MAAM,KAAK,sCAAQ,CAAC,EAAE;AACvC,YAAQ,IAAI;AACZ,eAAW,QAAQ,cAAc;AAC/B,cAAQ,IAAI,KAAK,EAAE,IAAI,IAAI,MAAM,KAAK,IAAI,CAAC,EAAE;AAAA,IAC/C;AAAA,EACF;AAGA,QAAM,YAAY,CAAC,GAAG,YAAY,GAAG,WAAW;AAChD,QAAM,cAAc,UAAU,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,EAAE;AACjE,QAAM,cAAc,UAAU,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,EAAE;AAEjE,UAAQ,IAAI;AACZ,UAAQ,IAAI,EAAE,IAAI;AAClB,UAAQ;AAAA,IACN,KAAK,MAAM,KAAK,eAAK,CAAC,IAAI,MAAM,MAAM,GAAG,WAAW,eAAK,CAAC,KACxD,cAAc,IAAI,MAAM,IAAI,GAAG,WAAW,eAAK,IAAI,MAAM,KAAK,gBAAM,CACtE;AAAA,EACF;AAEA,MAAI,cAAc,GAAG;AACnB,YAAQ,IAAI;AACZ,YAAQ;AAAA,MACN,KAAK,EAAE,IAAI,IAAI,MAAM,OAAO,cAAI,CAAC,IAAI,MAAM;AAAA,QACzC;AAAA,MACF,CAAC,IAAI,MAAM,OAAO,0BAAM,CAAC;AAAA,IAC3B;AAAA,EACF,OAAO;AACL,YAAQ,IAAI;AACZ,YAAQ,IAAI,KAAK,EAAE,EAAE,IAAI,MAAM,MAAM,uCAAS,CAAC,EAAE;AAAA,EACnD;AAEA,UAAQ,IAAI,EAAE,IAAI;AAClB,UAAQ,IAAI;AAEZ,SAAO,gBAAgB;AACzB;AAEA,SAAS,WAAW,OAAoB;AACtC,QAAM,OACJ,MAAM,WAAW,SAAS,EAAE,KAAK,MAAM,WAAW,SAAS,EAAE,OAAO,EAAE;AACxE,UAAQ,IAAI,KAAK,IAAI,IAAI,MAAM,IAAI,EAAE;AACrC,MAAI,MAAM,WAAW,MAAM,WAAW,QAAQ;AAC5C,YAAQ,IAAI,OAAO,MAAM,KAAK,MAAM,OAAO,CAAC,EAAE;AAAA,EAChD;AACF;","names":["resolve","existsSync","resolve","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/doctor.ts","../../src/utils/file.ts","../../src/utils/git.ts"],"sourcesContent":["/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Doctor 命令 - 智能诊断 Git 标准化配置\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { resolve } from \"node:path\";\r\nimport chalk from \"chalk\";\r\nimport { fileExists, readJsonFile } from \"../utils/file\";\r\nimport { isGitRepository } from \"../utils/git\";\r\n\r\n// ─── 品牌 & 符号 ──────────────────────────────────────────────────\r\nconst BRAND = \"#7C3AED\";\r\nconst S = {\r\n LOGO: chalk.hex(BRAND).bold(\"[RS]\"),\r\n OK: chalk.green(\"✔\"),\r\n FAIL: chalk.red(\"✖\"),\r\n WARN: chalk.yellow(\"▲\"),\r\n SKIP: chalk.gray(\"○\"),\r\n LINE: chalk.gray(\"─\".repeat(48)),\r\n};\r\n\r\nexport interface DoctorOptions {\r\n cwd?: string;\r\n}\r\n\r\ninterface CheckResult {\r\n name: string;\r\n status: \"pass\" | \"fail\" | \"skip\";\r\n message?: string;\r\n}\r\n\r\n/**\r\n * Doctor 命令主函数\r\n * 根据已安装的功能智能检测,未安装的功能只做提示不标记为失败\r\n */\r\nexport async function doctor(options: DoctorOptions = {}) {\r\n const cwd = options.cwd || process.cwd();\r\n\r\n // ── Banner ──\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${S.LOGO} ${chalk.bold(\"Robot Standards\")} ${chalk.gray(\"Doctor\")}`,\r\n );\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n const checks: CheckResult[] = [];\r\n\r\n // ── 1. 基础环境 ──\r\n const isGit = isGitRepository(cwd);\r\n checks.push({\r\n name: \"Git 仓库\",\r\n status: isGit ? \"pass\" : \"fail\",\r\n message: isGit ? undefined : \"未初始化 Git 仓库,运行 git init\",\r\n });\r\n\r\n // ── 2. 读取 package.json 探测已安装功能 ──\r\n let packageJson: any = {};\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const hasPackageJson = fileExists(packageJsonPath);\r\n\r\n if (hasPackageJson) {\r\n try {\r\n packageJson = await readJsonFile(packageJsonPath);\r\n } catch {\r\n // ignore\r\n }\r\n } else {\r\n checks.push({\r\n name: \"package.json\",\r\n status: \"fail\",\r\n message: \"缺少 package.json\",\r\n });\r\n }\r\n\r\n const allDeps = {\r\n ...packageJson.dependencies,\r\n ...packageJson.devDependencies,\r\n };\r\n\r\n // 检测已安装的功能模块\r\n const hasCommitizen = !!allDeps[\"commitizen\"];\r\n const hasCommitlint = !!allDeps[\"@commitlint/cli\"];\r\n const hasHusky = !!allDeps[\"husky\"];\r\n const hasEslint = !!allDeps[\"eslint\"];\r\n const hasLintStaged = !!allDeps[\"lint-staged\"];\r\n const hasPrettier = !!allDeps[\"prettier\"];\r\n\r\n // ── 3. 核心功能检查(始终检查) ──\r\n console.log(` ${chalk.bold(\"核心功能\")}`);\r\n console.log();\r\n\r\n // Husky\r\n if (hasHusky) {\r\n const huskyDir = fileExists(resolve(cwd, \".husky\"));\r\n checks.push({\r\n name: \"Husky 目录\",\r\n status: huskyDir ? \"pass\" : \"fail\",\r\n message: huskyDir ? undefined : \"缺少 .husky 目录\",\r\n });\r\n\r\n const commitMsg = fileExists(resolve(cwd, \".husky/commit-msg\"));\r\n checks.push({\r\n name: \"commit-msg hook\",\r\n status: commitMsg ? \"pass\" : \"fail\",\r\n message: commitMsg ? undefined : \"缺少 commit-msg hook\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Husky\",\r\n status: \"fail\",\r\n message: \"未安装 husky\",\r\n });\r\n }\r\n\r\n // Commitlint\r\n if (hasCommitlint) {\r\n const hasConfig =\r\n fileExists(resolve(cwd, \"commitlint.config.cjs\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.js\")) ||\r\n fileExists(resolve(cwd, \"commitlint.config.ts\"));\r\n checks.push({\r\n name: \"Commitlint 配置\",\r\n status: hasConfig ? \"pass\" : \"fail\",\r\n message: hasConfig ? undefined : \"缺少 commitlint.config.*\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitlint\",\r\n status: \"fail\",\r\n message: \"未安装 @commitlint/cli\",\r\n });\r\n }\r\n\r\n // Commitizen\r\n if (hasCommitizen) {\r\n const hasCzConfig =\r\n fileExists(resolve(cwd, \".cz-config.cjs\")) ||\r\n fileExists(resolve(cwd, \".cz-config.js\"));\r\n checks.push({\r\n name: \"Commitizen 配置\",\r\n status: hasCzConfig ? \"pass\" : \"fail\",\r\n message: hasCzConfig ? undefined : \"缺少 .cz-config.*\",\r\n });\r\n\r\n const hasCommitizenPath = !!packageJson.config?.commitizen;\r\n checks.push({\r\n name: \"commitizen path 配置\",\r\n status: hasCommitizenPath ? \"pass\" : \"fail\",\r\n message: hasCommitizenPath\r\n ? undefined\r\n : \"package.json 中缺少 config.commitizen\",\r\n });\r\n } else {\r\n checks.push({\r\n name: \"Commitizen\",\r\n status: \"fail\",\r\n message: \"未安装 commitizen\",\r\n });\r\n }\r\n\r\n // cz 脚本\r\n const hasCzScript = !!packageJson.scripts?.cz;\r\n checks.push({\r\n name: \"cz 脚本\",\r\n status: hasCzScript ? \"pass\" : \"fail\",\r\n message: hasCzScript ? undefined : \"缺少 cz 脚本 (bun run cz)\",\r\n });\r\n\r\n // ── 输出核心检查结果 ──\r\n const coreChecks = checks.slice();\r\n for (const check of coreChecks) {\r\n printCheck(check);\r\n }\r\n\r\n // ── 4. 附加功能检查(仅检测已安装的) ──\r\n const extraChecks: CheckResult[] = [];\r\n\r\n if (hasEslint) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码检查\")}`);\r\n console.log();\r\n\r\n const hasEslintConfig =\r\n fileExists(resolve(cwd, \"eslint.config.ts\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.js\")) ||\r\n fileExists(resolve(cwd, \"eslint.config.mjs\"));\r\n const eslintCheck: CheckResult = {\r\n name: \"ESLint 配置\",\r\n status: hasEslintConfig ? \"pass\" : \"fail\",\r\n message: hasEslintConfig ? undefined : \"缺少 eslint.config.*\",\r\n };\r\n extraChecks.push(eslintCheck);\r\n printCheck(eslintCheck);\r\n\r\n if (hasHusky) {\r\n const preCommit = fileExists(resolve(cwd, \".husky/pre-commit\"));\r\n const preCommitCheck: CheckResult = {\r\n name: \"pre-commit hook\",\r\n status: preCommit ? \"pass\" : \"fail\",\r\n message: preCommit ? undefined : \"缺少 pre-commit hook\",\r\n };\r\n extraChecks.push(preCommitCheck);\r\n printCheck(preCommitCheck);\r\n }\r\n }\r\n\r\n if (hasLintStaged) {\r\n const lintStagedConfig = !!packageJson[\"lint-staged\"];\r\n const lintStagedCheck: CheckResult = {\r\n name: \"lint-staged 配置\",\r\n status: lintStagedConfig ? \"pass\" : \"fail\",\r\n message: lintStagedConfig\r\n ? undefined\r\n : \"package.json 中缺少 lint-staged 配置\",\r\n };\r\n extraChecks.push(lintStagedCheck);\r\n printCheck(lintStagedCheck);\r\n }\r\n\r\n if (hasPrettier) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"代码格式化\")}`);\r\n console.log();\r\n\r\n const hasPrettierConfig =\r\n fileExists(resolve(cwd, \".prettierrc.cjs\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.js\")) ||\r\n fileExists(resolve(cwd, \".prettierrc.json\")) ||\r\n fileExists(resolve(cwd, \".prettierrc\"));\r\n const prettierCheck: CheckResult = {\r\n name: \"Prettier 配置\",\r\n status: hasPrettierConfig ? \"pass\" : \"fail\",\r\n message: hasPrettierConfig ? undefined : \"缺少 .prettierrc.*\",\r\n };\r\n extraChecks.push(prettierCheck);\r\n printCheck(prettierCheck);\r\n }\r\n\r\n // EditorConfig(不依赖 package.json,直接检测文件)\r\n const hasEditorConfig = fileExists(resolve(cwd, \".editorconfig\"));\r\n if (hasEditorConfig) {\r\n // 只在存在时标记 pass,不存在不报错(属于可选功能)\r\n }\r\n\r\n // ── 5. 未安装功能提示 ──\r\n const notInstalled: string[] = [];\r\n if (!hasEslint) notInstalled.push(\"ESLint\");\r\n if (!hasLintStaged) notInstalled.push(\"lint-staged\");\r\n if (!hasPrettier) notInstalled.push(\"Prettier\");\r\n if (!hasEditorConfig) notInstalled.push(\"EditorConfig\");\r\n\r\n if (notInstalled.length > 0) {\r\n console.log();\r\n console.log(` ${chalk.bold(\"未启用的功能\")}`);\r\n console.log();\r\n for (const name of notInstalled) {\r\n console.log(` ${S.SKIP} ${chalk.gray(name)}`);\r\n }\r\n }\r\n\r\n // ── 6. 汇总 ──\r\n const allChecks = [...coreChecks, ...extraChecks];\r\n const passedCount = allChecks.filter((c) => c.status === \"pass\").length;\r\n const failedCount = allChecks.filter((c) => c.status === \"fail\").length;\r\n\r\n console.log();\r\n console.log(S.LINE);\r\n console.log(\r\n ` ${chalk.bold(\"总计:\")} ${chalk.green(`${passedCount} 通过`)} ${\r\n failedCount > 0 ? chalk.red(`${failedCount} 失败`) : chalk.gray(\"0 失败\")\r\n }`,\r\n );\r\n\r\n if (failedCount > 0) {\r\n console.log();\r\n console.log(\r\n ` ${S.WARN} ${chalk.yellow(\"运行\")} ${chalk.cyan(\r\n \"robot-standards init\",\r\n )} ${chalk.yellow(\"修复问题\")}`,\r\n );\r\n } else {\r\n console.log();\r\n console.log(` ${S.OK} ${chalk.green(\"所有检查通过!\")}`);\r\n }\r\n\r\n console.log(S.LINE);\r\n console.log();\r\n\r\n return failedCount === 0;\r\n}\r\n\r\nfunction printCheck(check: CheckResult) {\r\n const icon =\r\n check.status === \"pass\" ? S.OK : check.status === \"fail\" ? S.FAIL : S.SKIP;\r\n console.log(` ${icon} ${check.name}`);\r\n if (check.message && check.status === \"fail\") {\r\n console.log(` ${chalk.gray(check.message)}`);\r\n }\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: 文件操作工具\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync, chmodSync } from \"node:fs\";\r\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\r\nimport { resolve, dirname } from \"node:path\";\r\n\r\n/**\r\n * 检查文件是否存在\r\n */\r\nexport function fileExists(filePath: string): boolean {\r\n return existsSync(filePath);\r\n}\r\n\r\n/**\r\n * 读取文件内容\r\n */\r\nexport async function readFileContent(filePath: string): Promise<string> {\r\n return await readFile(filePath, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 写入文件内容\r\n */\r\nexport async function writeFileContent(\r\n filePath: string,\r\n content: string,\r\n): Promise<void> {\r\n const dir = dirname(filePath);\r\n if (!existsSync(dir)) {\r\n await mkdir(dir, { recursive: true });\r\n }\r\n await writeFile(filePath, content, \"utf-8\");\r\n}\r\n\r\n/**\r\n * 写入可执行文件(自动设置 chmod 755 执行权限)\r\n * 用于 Husky Hook 等需要执行权限的文件\r\n */\r\nexport async function writeExecutableFile(\r\n filePath: string,\r\n content: string,\r\n): Promise<void> {\r\n await writeFileContent(filePath, content);\r\n try {\r\n chmodSync(filePath, 0o755);\r\n } catch {\r\n // Windows 环境下 chmod 可能不生效,忽略错误\r\n }\r\n}\r\n\r\n/**\r\n * 读取并解析 JSON 文件\r\n */\r\nexport async function readJsonFile<T = any>(filePath: string): Promise<T> {\r\n const content = await readFileContent(filePath);\r\n return JSON.parse(content) as T;\r\n}\r\n\r\n/**\r\n * 写入 JSON 文件\r\n */\r\nexport async function writeJsonFile(\r\n filePath: string,\r\n data: any,\r\n pretty: boolean = true,\r\n): Promise<void> {\r\n const content = pretty\r\n ? JSON.stringify(data, null, 2) + \"\\n\"\r\n : JSON.stringify(data);\r\n await writeFileContent(filePath, content);\r\n}\r\n\r\n/**\r\n * 更新 package.json\r\n */\r\nexport async function updatePackageJson(\r\n updates: Record<string, any>,\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n const packageJsonPath = resolve(cwd, \"package.json\");\r\n const packageJson = await readJsonFile(packageJsonPath);\r\n const updated = { ...packageJson, ...updates };\r\n await writeJsonFile(packageJsonPath, updated);\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-13\r\n * @Description: Git 工具函数\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { resolve } from \"node:path\";\r\nimport { execa } from \"execa\";\r\n\r\n/**\r\n * 检查是否在 Git 仓库中\r\n */\r\nexport function isGitRepository(cwd: string = process.cwd()): boolean {\r\n return existsSync(resolve(cwd, \".git\"));\r\n}\r\n\r\n/**\r\n * 初始化 Git 仓库\r\n */\r\nexport async function initGitRepository(\r\n cwd: string = process.cwd(),\r\n): Promise<void> {\r\n if (!isGitRepository(cwd)) {\r\n await execa(\"git\", [\"init\"], { cwd });\r\n }\r\n}\r\n"],"mappings":"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","names":["resolve","existsSync","resolve","resolve"]}
|
package/dist/cli/init.cjs
CHANGED
|
@@ -112,6 +112,13 @@ async function writeFileContent(filePath, content) {
|
|
|
112
112
|
}
|
|
113
113
|
await (0, import_promises.writeFile)(filePath, content, "utf-8");
|
|
114
114
|
}
|
|
115
|
+
async function writeExecutableFile(filePath, content) {
|
|
116
|
+
await writeFileContent(filePath, content);
|
|
117
|
+
try {
|
|
118
|
+
(0, import_node_fs3.chmodSync)(filePath, 493);
|
|
119
|
+
} catch {
|
|
120
|
+
}
|
|
121
|
+
}
|
|
115
122
|
async function readJsonFile(filePath) {
|
|
116
123
|
const content = await readFileContent(filePath);
|
|
117
124
|
return JSON.parse(content);
|
|
@@ -601,6 +608,7 @@ module.exports = {
|
|
|
601
608
|
'perf', 'test', 'chore', 'revert', 'build', 'deps',
|
|
602
609
|
],
|
|
603
610
|
],
|
|
611
|
+
'scope-empty': [2, 'never'],
|
|
604
612
|
'subject-case': [0],
|
|
605
613
|
},
|
|
606
614
|
}
|
|
@@ -921,13 +929,9 @@ async function setupHusky(cwd, pm, features) {
|
|
|
921
929
|
try {
|
|
922
930
|
const execCmd = getExecCommand(pm);
|
|
923
931
|
await (0, import_execa2.execa)(execCmd, ["husky", "init"], { cwd, stdio: "pipe" });
|
|
924
|
-
const legacyDir = (0, import_node_path4.resolve)(cwd, ".husky/_");
|
|
925
|
-
if ((0, import_node_fs4.existsSync)(legacyDir)) {
|
|
926
|
-
(0, import_node_fs4.rmSync)(legacyDir, { recursive: true, force: true });
|
|
927
|
-
}
|
|
928
932
|
const commitMsg = `${execCmd} --no-install commitlint --edit "$1"
|
|
929
933
|
`;
|
|
930
|
-
await
|
|
934
|
+
await writeExecutableFile((0, import_node_path4.resolve)(cwd, ".husky/commit-msg"), commitMsg);
|
|
931
935
|
const hooks = ["commit-msg"];
|
|
932
936
|
const needsPreCommit = features.eslint || features.lintStaged || features.oxlint;
|
|
933
937
|
if (needsPreCommit) {
|
|
@@ -940,7 +944,7 @@ async function setupHusky(cwd, pm, features) {
|
|
|
940
944
|
} else if (features.eslint) {
|
|
941
945
|
cmds.push(`${execCmd} eslint . --fix`);
|
|
942
946
|
}
|
|
943
|
-
await
|
|
947
|
+
await writeExecutableFile(
|
|
944
948
|
(0, import_node_path4.resolve)(cwd, ".husky/pre-commit"),
|
|
945
949
|
cmds.join("\n") + "\n"
|
|
946
950
|
);
|