@robot-admin/git-standards 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,997 @@
1
+ // src/cli/init.ts
2
+ import { resolve as resolve4 } from "path";
3
+ import { unlinkSync, existsSync as existsSync4, rmSync } from "fs";
4
+ import chalk from "chalk";
5
+ import ora from "ora";
6
+ import { execa as execa2 } from "execa";
7
+
8
+ // src/utils/package-manager.ts
9
+ import { existsSync } from "fs";
10
+ import { resolve } from "path";
11
+ async function detectPackageManager(cwd = process.cwd()) {
12
+ if (existsSync(resolve(cwd, "bun.lockb")) || existsSync(resolve(cwd, "bun.lock"))) {
13
+ return "bun";
14
+ }
15
+ if (existsSync(resolve(cwd, "pnpm-lock.yaml"))) {
16
+ return "pnpm";
17
+ }
18
+ if (existsSync(resolve(cwd, "yarn.lock"))) {
19
+ return "yarn";
20
+ }
21
+ if (existsSync(resolve(cwd, "package-lock.json"))) {
22
+ return "npm";
23
+ }
24
+ return "npm";
25
+ }
26
+ function getInstallCommand(pm) {
27
+ const commands = {
28
+ npm: "npm install --save-dev",
29
+ yarn: "yarn add --dev",
30
+ pnpm: "pnpm add -D",
31
+ bun: "bun add --dev"
32
+ };
33
+ return commands[pm];
34
+ }
35
+ function getExecCommand(pm) {
36
+ const commands = {
37
+ npm: "npx --no",
38
+ yarn: "yarn",
39
+ pnpm: "pnpm dlx",
40
+ bun: "bunx"
41
+ };
42
+ return commands[pm];
43
+ }
44
+ function getPackageManagerName(pm) {
45
+ const names = {
46
+ npm: "npm",
47
+ yarn: "Yarn",
48
+ pnpm: "pnpm",
49
+ bun: "Bun"
50
+ };
51
+ return names[pm];
52
+ }
53
+
54
+ // src/utils/git.ts
55
+ import { existsSync as existsSync2 } from "fs";
56
+ import { resolve as resolve2 } from "path";
57
+ import { execa } from "execa";
58
+ function isGitRepository(cwd = process.cwd()) {
59
+ return existsSync2(resolve2(cwd, ".git"));
60
+ }
61
+ async function initGitRepository(cwd = process.cwd()) {
62
+ if (!isGitRepository(cwd)) {
63
+ await execa("git", ["init"], { cwd });
64
+ }
65
+ }
66
+
67
+ // src/utils/file.ts
68
+ import { existsSync as existsSync3 } from "fs";
69
+ import { readFile, writeFile, mkdir } from "fs/promises";
70
+ import { resolve as resolve3, dirname } from "path";
71
+ async function readFileContent(filePath) {
72
+ return await readFile(filePath, "utf-8");
73
+ }
74
+ async function writeFileContent(filePath, content) {
75
+ const dir = dirname(filePath);
76
+ if (!existsSync3(dir)) {
77
+ await mkdir(dir, { recursive: true });
78
+ }
79
+ await writeFile(filePath, content, "utf-8");
80
+ }
81
+ async function readJsonFile(filePath) {
82
+ const content = await readFileContent(filePath);
83
+ return JSON.parse(content);
84
+ }
85
+ async function writeJsonFile(filePath, data, pretty = true) {
86
+ const content = pretty ? JSON.stringify(data, null, 2) + "\n" : JSON.stringify(data);
87
+ await writeFileContent(filePath, content);
88
+ }
89
+ async function updatePackageJson(updates, cwd = process.cwd()) {
90
+ const packageJsonPath = resolve3(cwd, "package.json");
91
+ const packageJson = await readJsonFile(packageJsonPath);
92
+ const updated = { ...packageJson, ...updates };
93
+ await writeJsonFile(packageJsonPath, updated);
94
+ }
95
+
96
+ // src/configs/lint-staged.ts
97
+ function createLintStagedConfig(options = {}) {
98
+ const codePattern = options.filePatterns?.code || "src/**/*.{js,jsx,ts,tsx,vue}";
99
+ const markupPattern = options.filePatterns?.markup || "*.{json,md,yml,yaml}";
100
+ const codeCommands = [];
101
+ if (options.oxlint !== false) {
102
+ codeCommands.push("oxlint --max-warnings 0 --deny-warnings");
103
+ }
104
+ if (options.eslint !== false) {
105
+ codeCommands.push("eslint --fix --no-cache");
106
+ }
107
+ if (options.prettier !== false) {
108
+ codeCommands.push("prettier --write");
109
+ }
110
+ const config = {};
111
+ if (codeCommands.length > 0) {
112
+ config[codePattern] = codeCommands;
113
+ }
114
+ if (options.prettier !== false) {
115
+ config[markupPattern] = ["prettier --write"];
116
+ }
117
+ return config;
118
+ }
119
+ function generateLintStagedConfig(options = {}) {
120
+ return createLintStagedConfig(options);
121
+ }
122
+
123
+ // src/cli/init.ts
124
+ var BRAND = "#7C3AED";
125
+ var S = {
126
+ LOGO: chalk.hex(BRAND).bold("[RS]"),
127
+ OK: chalk.green("\u2714"),
128
+ FAIL: chalk.red("\u2716"),
129
+ WARN: chalk.yellow("\u25B2"),
130
+ STEP: chalk.hex(BRAND)("\u25C6"),
131
+ ARROW: chalk.cyan("\u25B8"),
132
+ DOT: chalk.gray("\u25CF"),
133
+ INFO: chalk.blue("\u2139"),
134
+ LINE: chalk.gray("\u2500".repeat(48))
135
+ };
136
+ var PRESETS = {
137
+ minimal: {
138
+ name: "\u6781\u7B80\u6A21\u5F0F",
139
+ desc: "\u4EC5 Git \u63D0\u4EA4\u89C4\u8303 (Commitizen + Commitlint)",
140
+ features: {
141
+ eslint: false,
142
+ lintStaged: false,
143
+ prettier: false,
144
+ oxlint: false,
145
+ editorconfig: false
146
+ }
147
+ },
148
+ standard: {
149
+ name: "\u6807\u51C6\u6A21\u5F0F",
150
+ desc: "\u63D0\u4EA4\u89C4\u8303 + \u4EE3\u7801\u8D28\u91CF\u68C0\u67E5 (+ ESLint + lint-staged)",
151
+ features: {
152
+ eslint: true,
153
+ lintStaged: true,
154
+ prettier: false,
155
+ oxlint: false,
156
+ editorconfig: true
157
+ }
158
+ },
159
+ full: {
160
+ name: "\u5B8C\u6574\u6A21\u5F0F",
161
+ desc: "\u5168\u90E8\u5DE5\u5177\u94FE (+ Prettier + Oxlint + EditorConfig)",
162
+ features: {
163
+ eslint: true,
164
+ lintStaged: true,
165
+ prettier: true,
166
+ oxlint: true,
167
+ editorconfig: true
168
+ }
169
+ }
170
+ };
171
+ async function init(options = {}) {
172
+ const cwd = options.cwd || process.cwd();
173
+ printBanner();
174
+ console.log(` ${S.STEP} ${chalk.bold("\u73AF\u5883\u68C0\u6D4B")}`);
175
+ console.log();
176
+ if (!isGitRepository(cwd)) {
177
+ const spinner = ora({
178
+ text: chalk.gray("\u521D\u59CB\u5316 Git \u4ED3\u5E93..."),
179
+ prefixText: " ",
180
+ spinner: "dots"
181
+ }).start();
182
+ await initGitRepository(cwd);
183
+ spinner.succeed(chalk.white("Git \u4ED3\u5E93\u521D\u59CB\u5316\u5B8C\u6210"));
184
+ } else {
185
+ console.log(` ${S.OK} Git \u4ED3\u5E93\u5DF2\u5C31\u7EEA`);
186
+ }
187
+ const pm = await detectPackageManager(cwd);
188
+ const pmName = getPackageManagerName(pm);
189
+ console.log(` ${S.OK} \u5305\u7BA1\u7406\u5668: ${chalk.cyan.bold(pmName)}`);
190
+ console.log();
191
+ let features;
192
+ let eslintOpts = {
193
+ framework: "vue",
194
+ typescript: true,
195
+ jsdoc: false
196
+ };
197
+ if (options.ci) {
198
+ const presetId = options.preset || "standard";
199
+ if (presetId === "custom") {
200
+ features = {
201
+ eslint: true,
202
+ lintStaged: true,
203
+ prettier: options.prettier ?? true,
204
+ oxlint: options.oxlint ?? true,
205
+ editorconfig: true
206
+ };
207
+ } else {
208
+ features = { ...PRESETS[presetId].features };
209
+ }
210
+ if (options.prettier !== void 0) features.prettier = options.prettier;
211
+ if (options.oxlint !== void 0) features.oxlint = options.oxlint;
212
+ const jsdocDefault = presetId === "full" ? true : false;
213
+ eslintOpts = {
214
+ framework: options.framework || "vue",
215
+ typescript: options.typescript ?? true,
216
+ jsdoc: options.jsdoc ?? jsdocDefault
217
+ };
218
+ console.log(
219
+ ` ${S.INFO} ${chalk.gray("CI \u6A21\u5F0F")} ${chalk.white(
220
+ "\u9884\u8BBE:"
221
+ )} ${chalk.cyan(presetId)}`
222
+ );
223
+ console.log();
224
+ } else {
225
+ const result = await interactiveSetup(options);
226
+ features = result.features;
227
+ eslintOpts = result.eslintOpts;
228
+ }
229
+ printSummary(features, eslintOpts, pmName);
230
+ if (!options.ci) {
231
+ const { confirm } = await import("@inquirer/prompts");
232
+ const proceed = await confirm({
233
+ message: chalk.white("\u786E\u8BA4\u4EE5\u4E0A\u914D\u7F6E\u5E76\u5F00\u59CB\u5B89\u88C5?"),
234
+ default: true,
235
+ theme: { prefix: ` ${S.ARROW}` }
236
+ });
237
+ if (!proceed) {
238
+ console.log();
239
+ console.log(` ${S.INFO} ${chalk.gray("\u91CD\u65B0\u9009\u62E9\u914D\u7F6E...")}`);
240
+ console.log();
241
+ const result = await interactiveSetup(options);
242
+ features = result.features;
243
+ eslintOpts = result.eslintOpts;
244
+ printSummary(features, eslintOpts, pmName);
245
+ }
246
+ }
247
+ console.log();
248
+ await installDependencies(cwd, pm, features, eslintOpts);
249
+ await generateConfigFiles(cwd, features, eslintOpts);
250
+ await setupHusky(cwd, pm, features);
251
+ await addPackageScripts(cwd, pm, features);
252
+ printCompletion(pm, features);
253
+ }
254
+ function printBanner() {
255
+ console.log();
256
+ console.log(S.LINE);
257
+ console.log(
258
+ ` ${S.LOGO} ${chalk.bold("Robot Standards")} ${chalk.gray("v1.0.0")}`
259
+ );
260
+ console.log(` ${chalk.gray("\u96F6\u914D\u7F6E \xB7 \u6A21\u5757\u5316 \xB7 Git \u5DE5\u7A0B\u5316\u6807\u51C6\u5DE5\u5177\u5305")}`);
261
+ console.log(S.LINE);
262
+ console.log();
263
+ }
264
+ async function interactiveSetup(options) {
265
+ const { select, checkbox, confirm } = await import("@inquirer/prompts");
266
+ while (true) {
267
+ console.log(` ${S.STEP} ${chalk.bold("\u9009\u62E9\u6A21\u5F0F")}`);
268
+ console.log();
269
+ const presetId = await select({
270
+ message: chalk.white("\u9009\u62E9\u9884\u8BBE\u65B9\u6848"),
271
+ choices: [
272
+ {
273
+ name: `\u6781\u7B80\u6A21\u5F0F ${chalk.gray(
274
+ "\u2500\u2500 \u4EC5\u63D0\u4EA4\u89C4\u8303 (Commitizen + Commitlint)"
275
+ )}`,
276
+ value: "minimal",
277
+ description: chalk.gray("\u9002\u5408\u53EA\u9700\u89C4\u8303\u63D0\u4EA4\u4FE1\u606F\u7684\u9879\u76EE")
278
+ },
279
+ {
280
+ name: `\u6807\u51C6\u6A21\u5F0F ${chalk.gray("\u2500\u2500 \u63D0\u4EA4\u89C4\u8303 + \u4EE3\u7801\u68C0\u67E5 (+ ESLint)")}`,
281
+ value: "standard",
282
+ description: chalk.gray("\u9002\u5408\u5927\u591A\u6570\u9879\u76EE")
283
+ },
284
+ {
285
+ name: `\u5B8C\u6574\u6A21\u5F0F ${chalk.gray(
286
+ "\u2500\u2500 \u5168\u90E8\u5DE5\u5177\u94FE (+ Prettier + Oxlint)"
287
+ )} ${chalk.hex(BRAND)("\u4E3B\u9879\u76EE(Robot_Admin)")}`,
288
+ value: "full",
289
+ description: chalk.gray("\u5168\u9762\u4EE3\u7801\u8D28\u91CF\u7BA1\u63A7")
290
+ },
291
+ {
292
+ name: `\u81EA\u5B9A\u4E49 ${chalk.gray("\u2500\u2500 \u81EA\u7531\u7EC4\u5408\u9700\u8981\u7684\u5DE5\u5177\u94FE")}`,
293
+ value: "custom",
294
+ description: chalk.gray("\u7CBE\u786E\u63A7\u5236\u6BCF\u4E2A\u529F\u80FD\u6A21\u5757")
295
+ }
296
+ ],
297
+ default: "standard",
298
+ theme: { prefix: ` ${S.ARROW}` }
299
+ });
300
+ let features;
301
+ if (presetId === "custom") {
302
+ console.log();
303
+ console.log(
304
+ ` ${S.STEP} ${chalk.bold("\u9009\u62E9\u529F\u80FD")} ${chalk.gray(
305
+ "(Git \u63D0\u4EA4\u89C4\u8303\u9ED8\u8BA4\u5305\u542B)"
306
+ )}`
307
+ );
308
+ console.log();
309
+ const selected = await checkbox({
310
+ message: chalk.white("\u9009\u62E9\u9644\u52A0\u529F\u80FD (\u7A7A\u683C\u5207\u6362, \u56DE\u8F66\u786E\u8BA4)"),
311
+ choices: [
312
+ {
313
+ name: `ESLint ${chalk.gray("\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5")}`,
314
+ value: "eslint"
315
+ },
316
+ {
317
+ name: `lint-staged ${chalk.gray("\u6682\u5B58\u533A\u589E\u91CF\u68C0\u67E5")}`,
318
+ value: "lintStaged"
319
+ },
320
+ {
321
+ name: `Prettier ${chalk.gray("\u4EE3\u7801\u81EA\u52A8\u683C\u5F0F\u5316")}`,
322
+ value: "prettier"
323
+ },
324
+ {
325
+ name: `Oxlint ${chalk.gray(
326
+ "\u9AD8\u6027\u80FD Lint \u5F15\u64CE (50x faster)"
327
+ )}`,
328
+ value: "oxlint"
329
+ },
330
+ {
331
+ name: `EditorConfig ${chalk.gray("\u7F16\u8F91\u5668\u7EDF\u4E00\u914D\u7F6E")}`,
332
+ value: "editorconfig"
333
+ },
334
+ {
335
+ name: chalk.yellow("\u21A9 \u8FD4\u56DE\u4E0A\u4E00\u6B65"),
336
+ value: "__back__"
337
+ }
338
+ ],
339
+ theme: { prefix: ` ${S.ARROW}` }
340
+ });
341
+ if (selected.includes("__back__")) continue;
342
+ features = {
343
+ eslint: selected.includes("eslint"),
344
+ lintStaged: selected.includes("lintStaged"),
345
+ prettier: selected.includes("prettier"),
346
+ oxlint: selected.includes("oxlint"),
347
+ editorconfig: selected.includes("editorconfig")
348
+ };
349
+ if (features.oxlint && !features.eslint) {
350
+ console.log(
351
+ `
352
+ ${S.INFO} ${chalk.gray(
353
+ "Oxlint \u9700\u8981 ESLint \u914D\u5408\uFF0C\u5DF2\u81EA\u52A8\u542F\u7528 ESLint"
354
+ )}`
355
+ );
356
+ features.eslint = true;
357
+ }
358
+ if (features.lintStaged && !features.eslint && !features.prettier) {
359
+ console.log(
360
+ `
361
+ ${S.INFO} ${chalk.gray(
362
+ "lint-staged \u9700\u8981 ESLint \u6216 Prettier\uFF0C\u5DF2\u81EA\u52A8\u542F\u7528 ESLint"
363
+ )}`
364
+ );
365
+ features.eslint = true;
366
+ }
367
+ } else {
368
+ features = { ...PRESETS[presetId].features };
369
+ }
370
+ let eslintOpts = {
371
+ framework: "vue",
372
+ typescript: true,
373
+ jsdoc: false
374
+ };
375
+ if (features.eslint) {
376
+ console.log();
377
+ console.log(` ${S.STEP} ${chalk.bold("ESLint \u914D\u7F6E")}`);
378
+ console.log();
379
+ const framework = await select({
380
+ message: chalk.white("\u9879\u76EE\u6846\u67B6"),
381
+ choices: [
382
+ {
383
+ name: "Vue 3",
384
+ value: "vue"
385
+ },
386
+ { name: "React", value: "react" },
387
+ { name: "Vanilla JS / TS", value: "vanilla" },
388
+ {
389
+ name: chalk.yellow("\u21A9 \u8FD4\u56DE\u4E0A\u4E00\u6B65"),
390
+ value: "__back__"
391
+ }
392
+ ],
393
+ default: options.framework || "vue",
394
+ theme: { prefix: ` ${S.ARROW}` }
395
+ });
396
+ if (framework === "__back__") continue;
397
+ const typescript = await confirm({
398
+ message: chalk.white("\u4F7F\u7528 TypeScript"),
399
+ default: options.typescript ?? true,
400
+ theme: { prefix: ` ${S.ARROW}` }
401
+ });
402
+ const jsdoc = await confirm({
403
+ message: chalk.white("\u5F3A\u5236 JSDoc \u6CE8\u91CA"),
404
+ default: options.jsdoc ?? true,
405
+ theme: { prefix: ` ${S.ARROW}` }
406
+ });
407
+ eslintOpts = { framework, typescript, jsdoc };
408
+ }
409
+ return { features, eslintOpts };
410
+ }
411
+ }
412
+ function printSummary(features, eslintOpts, pmName) {
413
+ console.log();
414
+ console.log(` ${S.STEP} ${chalk.bold("\u914D\u7F6E\u6458\u8981")}`);
415
+ console.log();
416
+ console.log(
417
+ ` ${S.OK} ${chalk.white("\u6838\u5FC3")} Commitizen + Commitlint + Husky`
418
+ );
419
+ if (features.eslint) {
420
+ const fw = eslintOpts.framework === "vue" ? "Vue 3" : eslintOpts.framework === "react" ? "React" : "Vanilla";
421
+ const ts = eslintOpts.typescript ? " + TS" : "";
422
+ const jsdoc = eslintOpts.jsdoc ? " + JSDoc" : "";
423
+ console.log(
424
+ ` ${S.OK} ${chalk.white("\u68C0\u67E5")} ESLint (${fw}${ts}${jsdoc})`
425
+ );
426
+ }
427
+ if (features.lintStaged) {
428
+ console.log(` ${S.OK} ${chalk.white("\u6682\u5B58")} lint-staged`);
429
+ }
430
+ if (features.oxlint) {
431
+ console.log(
432
+ ` ${S.OK} ${chalk.white("\u52A0\u901F")} Oxlint ${chalk.gray("(50x faster)")}`
433
+ );
434
+ }
435
+ if (features.prettier) {
436
+ console.log(` ${S.OK} ${chalk.white("\u683C\u5F0F")} Prettier`);
437
+ }
438
+ if (features.editorconfig) {
439
+ console.log(` ${S.OK} ${chalk.white("\u7F16\u8F91")} EditorConfig`);
440
+ }
441
+ console.log(` ${S.DOT} ${chalk.gray("\u7BA1\u7406")} ${pmName}`);
442
+ console.log();
443
+ }
444
+ async function installDependencies(cwd, pm, features, eslintOpts) {
445
+ const spinner = ora({
446
+ text: chalk.gray("\u5206\u6790\u4F9D\u8D56..."),
447
+ prefixText: " ",
448
+ spinner: "dots"
449
+ }).start();
450
+ const deps = [
451
+ "@commitlint/cli",
452
+ "@commitlint/config-conventional",
453
+ "commitizen",
454
+ "cz-customizable",
455
+ "husky"
456
+ ];
457
+ if (features.eslint) {
458
+ deps.push("eslint");
459
+ if (eslintOpts.framework === "vue") {
460
+ deps.push("eslint-plugin-vue", "@vue/eslint-config-typescript");
461
+ }
462
+ if (eslintOpts.typescript) {
463
+ deps.push(
464
+ "@typescript-eslint/eslint-plugin",
465
+ "@typescript-eslint/parser"
466
+ );
467
+ }
468
+ if (eslintOpts.jsdoc) {
469
+ deps.push("eslint-plugin-jsdoc");
470
+ }
471
+ }
472
+ if (features.lintStaged) {
473
+ deps.push("lint-staged");
474
+ }
475
+ if (features.oxlint) {
476
+ deps.push("oxlint", "eslint-plugin-oxlint");
477
+ }
478
+ if (features.prettier) {
479
+ deps.push("prettier");
480
+ if (features.eslint && eslintOpts.framework === "vue") {
481
+ deps.push("@vue/eslint-config-prettier");
482
+ }
483
+ }
484
+ spinner.text = chalk.gray(`\u5B89\u88C5 ${deps.length} \u4E2A\u4F9D\u8D56...`);
485
+ try {
486
+ const installCmd = getInstallCommand(pm);
487
+ await execa2(
488
+ installCmd.split(" ")[0],
489
+ [...installCmd.split(" ").slice(1), ...deps],
490
+ { cwd, stdio: "pipe" }
491
+ );
492
+ spinner.succeed(
493
+ chalk.white("\u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210 ") + chalk.gray(`(${deps.length} packages)`)
494
+ );
495
+ } catch (error) {
496
+ spinner.fail(chalk.red("\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25"));
497
+ throw error;
498
+ }
499
+ }
500
+ async function generateConfigFiles(cwd, features, eslintOpts) {
501
+ const spinner = ora({
502
+ text: chalk.gray("\u751F\u6210\u914D\u7F6E\u6587\u4EF6..."),
503
+ prefixText: " ",
504
+ spinner: "dots"
505
+ }).start();
506
+ const generated = [];
507
+ try {
508
+ const czConfig = `/*
509
+ * Commitizen \u81EA\u5B9A\u4E49\u914D\u7F6E (cz-customizable)
510
+ * @generated by @robot-admin/git-standards
511
+ *
512
+ * \u76F4\u63A5\u4FEE\u6539\u6B64\u6587\u4EF6\u5373\u53EF\u81EA\u5B9A\u4E49\u63D0\u4EA4\u89C4\u8303
513
+ */
514
+ module.exports = {
515
+ scopes: [],
516
+ allowEmptyScopes: false,
517
+ allowCustomScopes: true,
518
+
519
+ types: [
520
+ { value: 'wip', name: 'wip: \u{1F6A7} \u5F00\u53D1\u4E2D' },
521
+ { value: 'feat', name: 'feat: \u{1F3AF} \u65B0\u529F\u80FD' },
522
+ { value: 'fix', name: 'fix: \u{1F41B} Bug \u4FEE\u590D' },
523
+ { value: 'perf', name: 'perf: \u26A1\uFE0F \u6027\u80FD\u4F18\u5316' },
524
+ { value: 'deps', name: 'deps: \u{1F4E6} \u4F9D\u8D56\u66F4\u65B0' },
525
+ { value: 'refactor', name: 'refactor: \u267B\uFE0F \u91CD\u6784' },
526
+ { value: 'docs', name: 'docs: \u{1F4DA} \u6587\u6863\u53D8\u66F4' },
527
+ { value: 'test', name: 'test: \u{1F50E} \u6D4B\u8BD5\u76F8\u5173' },
528
+ { value: 'style', name: 'style: \u{1F484} \u4EE3\u7801\u6837\u5F0F' },
529
+ { value: 'build', name: 'build: \u{1F9F3} \u6784\u5EFA/\u6253\u5305' },
530
+ { value: 'chore', name: 'chore: \u{1F527} \u5176\u4ED6\u6742\u9879' },
531
+ { value: 'revert', name: 'revert: \u{1F519} \u56DE\u9000' },
532
+ ],
533
+
534
+ messages: {
535
+ type: '\u8BF7\u9009\u62E9\u63D0\u4EA4\u7C7B\u578B:',
536
+ customScope: '\u8BF7\u8F93\u5165\u4FEE\u6539\u8303\u56F4(\u5FC5\u586B\uFF0C\u683C\u5F0F\u5982\uFF1A\u6A21\u5757/\u5B50\u6A21\u5757):',
537
+ subject: '\u8BF7\u7B80\u8981\u63CF\u8FF0\u63D0\u4EA4(\u5FC5\u586B\uFF0C\u4E0D\u52A0\u53E5\u53F7):',
538
+ body: '\u8BF7\u8F93\u5165\u66F4\u8BE6\u7EC6\u7684\u8BF4\u660E(\u53EF\u9009):\\n',
539
+ footer: 'Footer(\u53EF\u9009): \u4F8B\u5982 "Closes #123" \u6216 "Release-As: 1.3.1"\\n',
540
+ confirmCommit: '\u786E\u8BA4\u63D0\u4EA4\u4EE5\u4E0A\u5185\u5BB9\uFF1F(y/n/e/h)',
541
+ },
542
+
543
+ skipQuestions: ['body'],
544
+
545
+ allowBreakingChanges: ['feat', 'fix', 'refactor'],
546
+ breakingPrefix: 'BREAKING CHANGE:',
547
+
548
+ subjectLimit: 88,
549
+ }
550
+ `;
551
+ await writeFileContent(resolve4(cwd, ".cz-config.js"), czConfig);
552
+ generated.push(".cz-config.js");
553
+ const commitlintConfig = `/*
554
+ * Commitlint \u914D\u7F6E
555
+ * @generated by @robot-admin/git-standards
556
+ *
557
+ * \u76F4\u63A5\u4FEE\u6539\u6B64\u6587\u4EF6\u5373\u53EF\u81EA\u5B9A\u4E49\u63D0\u4EA4\u6821\u9A8C\u89C4\u5219
558
+ */
559
+ module.exports = {
560
+ extends: ['@commitlint/config-conventional'],
561
+ rules: {
562
+ 'type-enum': [
563
+ 2,
564
+ 'always',
565
+ [
566
+ 'wip', 'feat', 'fix', 'docs', 'style', 'refactor',
567
+ 'perf', 'test', 'chore', 'revert', 'build', 'deps',
568
+ ],
569
+ ],
570
+ 'subject-case': [0],
571
+ },
572
+ }
573
+ `;
574
+ await writeFileContent(
575
+ resolve4(cwd, "commitlint.config.js"),
576
+ commitlintConfig
577
+ );
578
+ generated.push("commitlint.config.js");
579
+ if (features.prettier) {
580
+ const prettierConfig = `/*
581
+ * Prettier \u914D\u7F6E
582
+ * @generated by @robot-admin/git-standards
583
+ *
584
+ * \u76F4\u63A5\u4FEE\u6539\u6B64\u6587\u4EF6\u5373\u53EF\u81EA\u5B9A\u4E49\u683C\u5F0F\u5316\u89C4\u5219
585
+ */
586
+ module.exports = {
587
+ $schema: 'https://json.schemastore.org/prettierrc',
588
+ semi: false,
589
+ singleQuote: true,
590
+ printWidth: 80,
591
+ tabWidth: 2,
592
+ quoteProps: 'as-needed',
593
+ trailingComma: 'es5',
594
+ bracketSpacing: true,
595
+ jsxSingleQuote: true,
596
+ arrowParens: 'avoid',
597
+ endOfLine: 'auto',
598
+ htmlWhitespaceSensitivity: 'strict',
599
+ vueIndentScriptAndStyle: true,
600
+ singleAttributePerLine: true,
601
+ }
602
+ `;
603
+ await writeFileContent(resolve4(cwd, ".prettierrc.js"), prettierConfig);
604
+ generated.push(".prettierrc.js");
605
+ }
606
+ if (features.eslint) {
607
+ const hasOxlint = features.oxlint;
608
+ const hasPrettier = features.prettier;
609
+ const hasJsdoc = eslintOpts.jsdoc;
610
+ const isVue = eslintOpts.framework === "vue";
611
+ const isTs = eslintOpts.typescript;
612
+ const importLines = [];
613
+ if (isVue) importLines.push("import pluginVue from 'eslint-plugin-vue'");
614
+ if (isVue && isTs) {
615
+ importLines.push(
616
+ "import {\n defineConfigWithVueTs,\n vueTsConfigs,\n} from '@vue/eslint-config-typescript'"
617
+ );
618
+ }
619
+ if (hasOxlint)
620
+ importLines.push("import oxlint from 'eslint-plugin-oxlint'");
621
+ if (hasPrettier && isVue) {
622
+ importLines.push(
623
+ "import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'"
624
+ );
625
+ }
626
+ if (hasJsdoc)
627
+ importLines.push("import jsdocPlugin from 'eslint-plugin-jsdoc'");
628
+ const fileExts = isTs ? "js,ts,mts,tsx,vue" : "js,jsx,vue";
629
+ const vue2DeprecationRules = isVue ? `
630
+ //! \u4E3B\u52A8\u7981\u6B62 Vue 2 \u5199\u6CD5
631
+ 'vue/no-deprecated-props-default-this': 'error',
632
+ 'vue/no-deprecated-events-api': 'error',
633
+ 'vue/no-deprecated-filter': 'error',
634
+ 'vue/no-deprecated-functional-template': 'error',
635
+ ` : "";
636
+ const vueComponentRules = isVue ? `
637
+ // Vue \u89C4\u8303
638
+ //! PascalCase \u547D\u540D\u89C4\u8303
639
+ 'vue/component-name-in-template-casing': [
640
+ 'error',
641
+ 'PascalCase',
642
+ {
643
+ registeredComponentsOnly: false,
644
+ ignores: [
645
+ 'router-view',
646
+ 'router-link',
647
+ 'transition',
648
+ 'draggable',
649
+ '/^icon-/i',
650
+ '/^C_/',
651
+ '/^c_/',
652
+ 'v-md-editor',
653
+ ],
654
+ },
655
+ ],
656
+ 'vue/multi-word-component-names': [
657
+ 'error',
658
+ {
659
+ ignores: ['index'],
660
+ },
661
+ ],
662
+ //! \u7981\u6B62\u5728\u6A21\u677F\u4E2D\u6CE8\u518C\u4F46\u672A\u4F7F\u7528\u7684\u7EC4\u4EF6
663
+ 'vue/no-unused-components': 'error',
664
+ ${vue2DeprecationRules}` : "";
665
+ const jsdocBlock = hasJsdoc ? `
666
+ //MARK: \u81EA\u5B9A\u4E49\u89C4\u5219\u7EC4\uFF08\u4F18\u5148\u7EA7\u6700\u9AD8\uFF09
667
+ {
668
+ plugins: {
669
+ jsdoc: jsdocPlugin,
670
+ },
671
+ rules: {
672
+ //! JSDoc \u6CE8\u91CA\u89C4\u5219
673
+ 'jsdoc/require-jsdoc': [
674
+ 'error',
675
+ {
676
+ require: {
677
+ FunctionDeclaration: true,
678
+ MethodDefinition: true,
679
+ ClassDeclaration: true,
680
+ ArrowFunctionExpression: false,
681
+ FunctionExpression: true,
682
+ },
683
+ contexts: [
684
+ 'FunctionDeclaration',
685
+ 'ClassDeclaration',
686
+ 'ClassProperty',
687
+ 'MethodDefinition',
688
+ 'FunctionExpression',
689
+ ],
690
+ checkConstructors: true,
691
+ checkGetters: true,
692
+ checkSetters: true,
693
+ },
694
+ ],
695
+ ` : `
696
+ // \u81EA\u5B9A\u4E49\u89C4\u5219\u7EC4
697
+ {
698
+ rules: {
699
+ `;
700
+ const fileTypeOverrides = isTs ? `
701
+ //MARK: \u6587\u4EF6\u7C7B\u578B\u8986\u76D6\u89C4\u5219
702
+
703
+ //! \u53D8\u91CF\u4F7F\u7528\u89C4\u5219
704
+ {
705
+ files: ['**/*.js'],
706
+ rules: {
707
+ 'no-unused-vars': 'error',
708
+ '@typescript-eslint/no-unused-vars': 'off',
709
+ },
710
+ },
711
+ {
712
+ files: ['**/*.{ts,mts,tsx,vue}'],
713
+ rules: {
714
+ 'no-unused-vars': 'off',
715
+ '@typescript-eslint/no-unused-vars': 'error',
716
+ },
717
+ },
718
+ ` : "";
719
+ const jsdocWhitelist = hasJsdoc ? `
720
+ //MARK: JSDoc \u767D\u540D\u5355\u8986\u76D6\u89C4\u5219
721
+ {
722
+ files: [
723
+ 'src/router/**/*.ts',
724
+ 'src/stores/**/*.ts',
725
+ 'src/views/**/components/*.vue',
726
+ ],
727
+ rules: {
728
+ 'jsdoc/require-jsdoc': 'off',
729
+ '@typescript-eslint/require-jsdoc': 'off',
730
+ },
731
+ },
732
+ ` : "";
733
+ const ignoreAssets = `
734
+ //MARK: ESLINT \u767D\u540D\u5355\u914D\u7F6E\u7EC4
735
+ {
736
+ name: 'app/ignore-assets',
737
+ ignores: [
738
+ 'src/assets/images/**/*',
739
+ '**/*.d.ts',
740
+ '**/auto-imports.d.ts',
741
+ 'src/views/**/components/*.vue',
742
+ 'scripts/**/*',
743
+ ],
744
+ },
745
+ `;
746
+ const tsRules = isTs ? `
747
+ //! \u5173\u95ED\u4E0E oxlint \u91CD\u590D\u7684 ESLint \u89C4\u5219
748
+ 'no-undef': 'off',
749
+
750
+ //! \u5F15\u53F7\u89C4\u8303
751
+ '@typescript-eslint/quotes': ['error', 'single'],${isVue ? "\n 'vue/html-quotes': ['error', 'double']," : ""}
752
+
753
+ //! TypeScript \u5B89\u5168
754
+ '@typescript-eslint/no-explicit-any': 'off',
755
+ '@typescript-eslint/ban-ts-comment': [
756
+ 'error',
757
+ {
758
+ 'ts-ignore': 'allow-with-description',
759
+ },
760
+ ],
761
+
762
+ //! \u8868\u8FBE\u5F0F\u89C4\u8303
763
+ '@typescript-eslint/no-unused-expressions': [
764
+ 'error',
765
+ {
766
+ allowShortCircuit: true,
767
+ allowTernary: false,
768
+ allowTaggedTemplates: false,
769
+ enforceForJSX: true,
770
+ },
771
+ ],
772
+ ` : "";
773
+ const useWrapper = isVue && isTs;
774
+ const wrapperStart = useWrapper ? "export default defineConfigWithVueTs(" : "export default [";
775
+ const wrapperEnd = useWrapper ? ")" : "]";
776
+ const oxlintLine = hasOxlint ? "\n ...oxlint.configs['flat/recommended'], // \u9AD8\u6027\u80FD\u57FA\u7840\u6821\u9A8C\n" : "";
777
+ const vueLine = isVue ? `
778
+ //! \u5FFD\u7565\u8F6C\u4E49\u5B57\u7B26
779
+ {
780
+ rules: {
781
+ 'no-useless-escape': 'off',
782
+ },
783
+ },
784
+
785
+ pluginVue.configs['flat/essential'], // Vue \u4E13\u7528\u89C4\u5219` : "";
786
+ const tsLine = isVue && isTs ? "\n vueTsConfigs.recommended, // TS \u4E13\u7528\u89C4\u5219" : "";
787
+ const skipLine = hasPrettier && isVue ? "\n skipFormatting" : "";
788
+ const eslintConfig = `/*
789
+ * ESLint Flat Config
790
+ * @generated by @robot-admin/git-standards
791
+ *
792
+ * \u76F4\u63A5\u4FEE\u6539\u6B64\u6587\u4EF6\u5373\u53EF\u81EA\u5B9A\u4E49 ESLint \u89C4\u5219
793
+ */
794
+ ${importLines.join("\n")}
795
+
796
+ ${wrapperStart}
797
+ //MARK: \u57FA\u7840\u914D\u7F6E\u7EC4
798
+ {
799
+ name: 'app/files-to-lint',
800
+ files: ['**/*.{${fileExts}}'],
801
+ },
802
+
803
+ {
804
+ name: 'app/files-to-ignore',
805
+ ignores: [
806
+ '**/dist/**',
807
+ '**/dist-ssr/**',
808
+ '**/coverage/**',
809
+ ],
810
+ },
811
+
812
+ //MARK: \u6838\u5FC3\u89C4\u5219\u7EC4\uFF08\u6309\u4F18\u5148\u7EA7\u6392\u5E8F\uFF09
813
+ ${oxlintLine}${vueLine}${tsLine}
814
+ ${fileTypeOverrides}
815
+ ${jsdocBlock}${tsRules}
816
+ //! \u4EE3\u7801\u590D\u6742\u5EA6
817
+ 'max-depth': ['error', 4],
818
+ complexity: ['warn', 10],
819
+
820
+ //! \u5F02\u6B65\u4EE3\u7801\u89C4\u8303
821
+ 'no-await-in-loop': 'error',
822
+ ${vueComponentRules}
823
+ //MARK: \u683C\u5F0F\u89C4\u8303
824
+ 'no-irregular-whitespace': 'error',
825
+ 'no-multi-spaces': 'error',
826
+ 'space-infix-ops': 'error',
827
+ 'array-bracket-spacing': ['error', 'never'],
828
+ 'arrow-spacing': ['error', { before: true, after: true }],
829
+ 'max-params': ['warn', 6],
830
+ 'no-eval': 'error',
831
+ 'prefer-const': 'warn',
832
+ 'no-var': 'warn',
833
+ 'prefer-destructuring': [
834
+ 1,
835
+ { object: true, array: false },
836
+ ],
837
+ 'no-duplicate-imports': 'error',
838
+ },
839
+ },
840
+ ${ignoreAssets}${jsdocWhitelist}${skipLine}
841
+ ${wrapperEnd}
842
+ `;
843
+ await writeFileContent(resolve4(cwd, "eslint.config.ts"), eslintConfig);
844
+ generated.push("eslint.config.ts");
845
+ }
846
+ if (features.editorconfig) {
847
+ const editorConfig = `# EditorConfig - \u7F16\u8F91\u5668\u7EDF\u4E00\u914D\u7F6E
848
+ # @generated by @robot-admin/git-standards
849
+ # \u53C2\u8003: https://editorconfig.org
850
+
851
+ root = true
852
+
853
+ [*]
854
+ charset = utf-8
855
+ indent_style = space
856
+ indent_size = 2
857
+ end_of_line = lf
858
+ insert_final_newline = true
859
+ trim_trailing_whitespace = true
860
+
861
+ [*.md]
862
+ trim_trailing_whitespace = false
863
+
864
+ [*.{yml,yaml}]
865
+ indent_size = 2
866
+
867
+ [Makefile]
868
+ indent_style = tab
869
+ `;
870
+ await writeFileContent(resolve4(cwd, ".editorconfig"), editorConfig);
871
+ generated.push(".editorconfig");
872
+ }
873
+ spinner.succeed(
874
+ chalk.white("\u914D\u7F6E\u6587\u4EF6\u751F\u6210\u5B8C\u6210 ") + chalk.gray(`(${generated.join(", ")})`)
875
+ );
876
+ } catch (error) {
877
+ spinner.fail(chalk.red("\u914D\u7F6E\u6587\u4EF6\u751F\u6210\u5931\u8D25"));
878
+ throw error;
879
+ }
880
+ }
881
+ async function setupHusky(cwd, pm, features) {
882
+ const spinner = ora({
883
+ text: chalk.gray("\u521D\u59CB\u5316 Husky..."),
884
+ prefixText: " ",
885
+ spinner: "dots"
886
+ }).start();
887
+ try {
888
+ const execCmd = getExecCommand(pm);
889
+ await execa2(execCmd, ["husky", "init"], { cwd, stdio: "pipe" });
890
+ const legacyDir = resolve4(cwd, ".husky/_");
891
+ if (existsSync4(legacyDir)) {
892
+ rmSync(legacyDir, { recursive: true, force: true });
893
+ }
894
+ const commitMsg = `${execCmd} --no-install commitlint --edit "$1"
895
+ `;
896
+ await writeFileContent(resolve4(cwd, ".husky/commit-msg"), commitMsg);
897
+ const hooks = ["commit-msg"];
898
+ const needsPreCommit = features.eslint || features.lintStaged || features.oxlint;
899
+ if (needsPreCommit) {
900
+ const cmds = [];
901
+ if (features.oxlint) {
902
+ cmds.push(`${execCmd} oxlint --max-warnings 0`);
903
+ }
904
+ if (features.lintStaged) {
905
+ cmds.push(`${execCmd} lint-staged`);
906
+ } else if (features.eslint) {
907
+ cmds.push(`${execCmd} eslint . --fix`);
908
+ }
909
+ await writeFileContent(
910
+ resolve4(cwd, ".husky/pre-commit"),
911
+ cmds.join("\n") + "\n"
912
+ );
913
+ hooks.push("pre-commit");
914
+ } else {
915
+ const defaultPreCommit = resolve4(cwd, ".husky/pre-commit");
916
+ if (existsSync4(defaultPreCommit)) {
917
+ unlinkSync(defaultPreCommit);
918
+ }
919
+ }
920
+ spinner.succeed(
921
+ chalk.white("Husky \u521D\u59CB\u5316\u5B8C\u6210 ") + chalk.gray(`(${hooks.join(", ")})`)
922
+ );
923
+ } catch (error) {
924
+ spinner.fail(chalk.red("Husky \u521D\u59CB\u5316\u5931\u8D25"));
925
+ throw error;
926
+ }
927
+ }
928
+ async function addPackageScripts(cwd, pm, features) {
929
+ const spinner = ora({
930
+ text: chalk.gray("\u66F4\u65B0 package.json..."),
931
+ prefixText: " ",
932
+ spinner: "dots"
933
+ }).start();
934
+ try {
935
+ const packageJsonPath = resolve4(cwd, "package.json");
936
+ const packageJson = await readJsonFile(packageJsonPath);
937
+ const scripts = packageJson.scripts || {};
938
+ scripts.cz = "git-cz";
939
+ scripts.prepare = "husky";
940
+ if (features.eslint) {
941
+ scripts.lint = features.oxlint ? "oxlint . --fix -D correctness --ignore-path .gitignore && eslint . --fix" : "eslint . --fix";
942
+ }
943
+ if (features.prettier) {
944
+ scripts.format = "prettier --write src/";
945
+ }
946
+ const czConfig = {
947
+ commitizen: { path: "node_modules/cz-customizable" }
948
+ };
949
+ const updates = { scripts, config: czConfig };
950
+ if (features.lintStaged) {
951
+ updates["lint-staged"] = generateLintStagedConfig({
952
+ eslint: features.eslint,
953
+ oxlint: features.oxlint,
954
+ prettier: features.prettier
955
+ });
956
+ }
957
+ await updatePackageJson(updates, cwd);
958
+ const parts = ["scripts"];
959
+ if (features.lintStaged) parts.push("lint-staged");
960
+ spinner.succeed(
961
+ chalk.white("package.json \u66F4\u65B0\u5B8C\u6210 ") + chalk.gray(`(${parts.join(" + ")})`)
962
+ );
963
+ } catch (error) {
964
+ spinner.fail(chalk.red("package.json \u66F4\u65B0\u5931\u8D25"));
965
+ throw error;
966
+ }
967
+ }
968
+ function printCompletion(pm, features) {
969
+ console.log();
970
+ console.log(S.LINE);
971
+ console.log(` ${S.OK} ${chalk.green.bold("\u521D\u59CB\u5316\u5B8C\u6210!")}`);
972
+ console.log(S.LINE);
973
+ console.log();
974
+ console.log(` ${chalk.bold("\u5FEB\u901F\u5F00\u59CB:")}`);
975
+ console.log();
976
+ console.log(` ${S.DOT} \u63D0\u4EA4\u4EE3\u7801 ${chalk.cyan(`${pm} run cz`)}`);
977
+ if (features.eslint) {
978
+ console.log(` ${S.DOT} \u68C0\u67E5\u4EE3\u7801 ${chalk.cyan(`${pm} run lint`)}`);
979
+ }
980
+ if (features.prettier) {
981
+ console.log(` ${S.DOT} \u683C\u5F0F\u5316 ${chalk.cyan(`${pm} run format`)}`);
982
+ }
983
+ console.log();
984
+ console.log(
985
+ ` ${S.INFO} ${chalk.gray("\u5168\u5C40\u5B89\u88C5 commitizen \u540E\u53EF\u76F4\u63A5\u4F7F\u7528 git cz \u63D0\u4EA4")}`
986
+ );
987
+ console.log(` ${S.DOT} ${chalk.gray(`npm install -g commitizen`)}`);
988
+ console.log();
989
+ console.log(
990
+ ` ${S.INFO} ${chalk.gray("\u6240\u6709\u914D\u7F6E\u6587\u4EF6\u5747\u652F\u6301\u8986\u76D6\u6269\u5C55\uFF0C\u8BE6\u89C1 README.md")}`
991
+ );
992
+ console.log();
993
+ }
994
+ export {
995
+ init
996
+ };
997
+ //# sourceMappingURL=init.js.map