create-vue3-enterprise 1.0.0 → 1.0.20

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/dist/index.js CHANGED
@@ -1,48 +1,56 @@
1
1
  #!/usr/bin/env node
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
- import minimist from 'minimist';
6
- import prompts from 'prompts';
7
- import { red, green, cyan } from 'kolorist';
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import minimist from "minimist";
6
+ import prompts from "prompts";
7
+ import { red, green, cyan, yellow } from "kolorist";
8
8
  const argv = minimist(process.argv.slice(2), {
9
- string: ['_'],
10
- boolean: ['help', 'template', 'force'],
9
+ string: ["_"],
10
+ boolean: ["help", "template", "force"],
11
11
  alias: {
12
- h: 'help',
13
- t: 'template',
14
- f: 'force'
15
- }
12
+ h: "help",
13
+ t: "template",
14
+ f: "force",
15
+ },
16
16
  });
17
17
  const cwd = process.cwd();
18
18
  const __filename = fileURLToPath(import.meta.url);
19
19
  const __dirname = path.dirname(__filename);
20
20
  function printHelp() {
21
21
  console.log(`
22
- ${cyan('create-vue3-enterprise')}
22
+ ${cyan("create-vue3-enterprise")}
23
23
 
24
- ${green('Usage:')}
25
- npm create vue3-enterprise <project-name> [options]
24
+ ${green("用法:")}
25
+ npm create vue3-enterprise <项目名称> [选项]
26
26
 
27
- ${green('Options:')}
28
- -h, --help Show help
29
- -f, --force Force overwrite
27
+ ${green("选项:")}
28
+ -h, --help 显示帮助信息
29
+ -t, --template 指定模板(vue3-ts)
30
+ -f, --force 强制覆盖已存在的目录
30
31
 
31
- ${green('Examples:')}
32
+ ${green("示例:")}
32
33
  npm create vue3-enterprise my-project
34
+ npm create vue3-enterprise my-project --force
33
35
  `);
34
36
  }
35
37
  function isValidPackageName(projectName) {
36
38
  return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(projectName);
37
39
  }
38
40
  function toValidPackageName(projectName) {
39
- return projectName.trim().toLowerCase().replace(/\s+/g, '-').replace(/^[._]/, '').replace(/[^a-z0-9-~]+/g, '-');
41
+ return projectName
42
+ .trim()
43
+ .toLowerCase()
44
+ .replace(/\s+/g, "-")
45
+ .replace(/^[._]/, "")
46
+ .replace(/[^a-z0-9-~]+/g, "-");
40
47
  }
41
48
  function emptyDir(dir) {
42
49
  if (!fs.existsSync(dir))
43
50
  return;
44
51
  for (const file of fs.readdirSync(dir)) {
45
- fs.rmSync(path.resolve(dir, file), { recursive: true, force: true });
52
+ const filePath = path.resolve(dir, file);
53
+ fs.rmSync(filePath, { recursive: true, force: true });
46
54
  }
47
55
  }
48
56
  function copyDir(srcDir, destDir) {
@@ -51,166 +59,319 @@ function copyDir(srcDir, destDir) {
51
59
  const srcFile = path.resolve(srcDir, file);
52
60
  const destFile = path.resolve(destDir, file);
53
61
  const stat = fs.statSync(srcFile);
54
- if (stat.isDirectory())
62
+ if (stat.isDirectory()) {
55
63
  copyDir(srcFile, destFile);
56
- else
64
+ }
65
+ else {
57
66
  fs.copyFileSync(srcFile, destFile);
67
+ }
58
68
  }
59
69
  }
60
- function makePkg(targetDir, features, needRouter, needPinia) {
61
- const name = isValidPackageName(targetDir) ? targetDir : toValidPackageName(targetDir);
70
+ async function generatePackageJson(targetDir, options) {
62
71
  const pkg = {
63
- name,
64
- version: '0.0.0',
72
+ name: isValidPackageName(targetDir)
73
+ ? targetDir
74
+ : toValidPackageName(targetDir),
75
+ version: "0.0.0",
65
76
  private: true,
66
- type: 'module',
77
+ type: "module",
67
78
  scripts: {
68
- dev: 'vite',
69
- build: 'vue-tsc && vite build',
70
- preview: 'vite preview',
71
- test: 'vitest',
72
- 'test:e2e': 'playwright test',
73
- 'test:perf': 'playwright test --config=playwright.config.perf.ts',
74
- lint: 'eslint . --ext .vue,.ts,.tsx --fix',
75
- format: 'prettier --write .',
76
- 'type-check': 'vue-tsc --noEmit'
79
+ dev: "vite",
80
+ build: "vue-tsc && vite build",
81
+ preview: "vite preview",
82
+ test: "vitest",
83
+ "test:e2e": "playwright test",
84
+ "test:perf": "playwright test --config=playwright.config.perf.ts",
85
+ lint: "eslint . --ext .vue,.ts,.tsx --fix",
86
+ format: "prettier --write .",
87
+ "type-check": "vue-tsc --noEmit",
88
+ prepare: "husky install",
89
+ "lint-staged": "lint-staged",
90
+ },
91
+ dependencies: {
92
+ vue: "^3.4.0",
77
93
  },
78
- dependencies: { vue: '^3.4.0' },
79
94
  devDependencies: {
80
- '@vitejs/plugin-vue': '^5.0.0',
81
- typescript: '^5.3.0',
82
- vite: '^5.0.0',
83
- 'vue-tsc': '^1.8.0'
84
- }
95
+ "@vitejs/plugin-vue": "^5.0.0",
96
+ typescript: "^5.3.0",
97
+ vite: "^5.0.0",
98
+ "vue-tsc": "^1.8.0",
99
+ },
85
100
  };
86
- if (needRouter)
87
- pkg.dependencies['vue-router'] = '^4.2.0';
88
- if (needPinia)
89
- pkg.dependencies['pinia'] = '^2.1.0';
90
- if (features.includes('lint')) {
91
- Object.assign(pkg.devDependencies, {
92
- '@typescript-eslint/eslint-plugin': '^6.21.0',
93
- '@typescript-eslint/parser': '^6.21.0',
94
- '@vue/eslint-config-prettier': '^9.0.0',
95
- '@vue/eslint-config-typescript': '^12.0.0',
96
- eslint: '^8.57.0',
97
- 'eslint-plugin-vue': '^9.21.0',
98
- prettier: '^3.2.0'
99
- });
100
- }
101
- if (features.includes('vitest')) {
102
- Object.assign(pkg.devDependencies, {
103
- '@vue/test-utils': '^2.4.0',
104
- 'happy-dom': '^13.0.0',
105
- vitest: '^1.2.0'
106
- });
107
- }
108
- if (features.includes('playwright')) {
109
- pkg.devDependencies['@playwright/test'] = '^1.41.0';
110
- }
111
- if (features.includes('mcp')) {
112
- pkg.devDependencies['vue3-enterprise-mcp'] = '^1.0.0';
113
- }
114
- if (features.includes('ci')) {
115
- pkg.devDependencies['vue3-enterprise-ci'] = '^1.0.0';
101
+ if (options.needsRouter) {
102
+ pkg.dependencies["vue-router"] = "^4.2.0";
103
+ }
104
+ if (options.needsPinia) {
105
+ pkg.dependencies["pinia"] = "^2.1.0";
106
+ }
107
+ if (options.features?.includes("lint")) {
108
+ pkg.devDependencies["@typescript-eslint/eslint-plugin"] = "^6.21.0";
109
+ pkg.devDependencies["@typescript-eslint/parser"] = "^6.21.0";
110
+ pkg.devDependencies["@vue/eslint-config-prettier"] = "^9.0.0";
111
+ pkg.devDependencies["@vue/eslint-config-typescript"] = "^12.0.0";
112
+ pkg.devDependencies["eslint"] = "^8.57.0";
113
+ pkg.devDependencies["eslint-plugin-vue"] = "^9.21.0";
114
+ pkg.devDependencies["prettier"] = "^3.2.0";
115
+ // Git hooks
116
+ pkg.devDependencies["husky"] = "^9.0.0";
117
+ pkg.devDependencies["lint-staged"] = "^15.2.0";
118
+ pkg.devDependencies["@commitlint/cli"] = "^19.0.0";
119
+ pkg.devDependencies["@commitlint/config-conventional"] = "^19.0.0";
120
+ }
121
+ if (options.features?.includes("vitest")) {
122
+ pkg.devDependencies["@vue/test-utils"] = "^2.4.0";
123
+ pkg.devDependencies["happy-dom"] = "^13.0.0";
124
+ pkg.devDependencies["vitest"] = "^1.2.0";
125
+ }
126
+ if (options.features?.includes("playwright")) {
127
+ pkg.devDependencies["@playwright/test"] = "^1.41.0";
128
+ }
129
+ if (options.features?.includes("mcp")) {
130
+ pkg.devDependencies["vue3-enterprise-mcp"] = "^1.0.0";
131
+ }
132
+ if (options.features?.includes("ci")) {
133
+ pkg.devDependencies["vue3-enterprise-ci"] = "^1.0.0";
116
134
  }
117
135
  return pkg;
118
136
  }
119
137
  async function main() {
120
- console.log(`\n${cyan('Vue3 Enterprise Toolchain')}\n`);
138
+ console.log(`\n${cyan("Vue3 Enterprise Toolchain")}\n`);
121
139
  if (argv.help) {
122
140
  printHelp();
123
141
  return;
124
142
  }
125
143
  let targetDir = argv._[0];
126
- const response = await prompts([
144
+ const promptResult = await prompts([
127
145
  {
128
- type: targetDir ? null : 'text',
129
- name: 'name',
130
- message: 'Project name:',
131
- initial: 'vue3-enterprise-project',
132
- onState: (s) => { targetDir = String(s.value).trim() || 'vue3-enterprise-project'; }
146
+ type: targetDir ? null : "text",
147
+ name: "projectName",
148
+ message: "项目名称:",
149
+ initial: "vue3-enterprise-project",
150
+ onState: (state) => {
151
+ targetDir = String(state.value).trim() || "vue3-enterprise-project";
152
+ },
133
153
  },
134
154
  {
135
- type: 'select', name: 'template', message: 'Select template:',
155
+ type: "select",
156
+ name: "template",
157
+ message: "选择模板:",
136
158
  choices: [
137
- { title: 'Vue3 + TypeScript + Vite', value: 'vue3-ts' },
138
- { title: 'Vue3 + TS + Vite + Pinia', value: 'vue3-ts-pinia' },
139
- { title: 'Vue3 + TS + Vite + Pinia + Router', value: 'vue3-ts-full' }
140
- ]
159
+ { title: "Vue3 + TypeScript + Vite (推荐)", value: "vue3-ts" },
160
+ { title: "Vue3 + TypeScript + Vite + Pinia", value: "vue3-ts-pinia" },
161
+ {
162
+ title: "Vue3 + TypeScript + Vite + Pinia + Vue Router",
163
+ value: "vue3-ts-full",
164
+ },
165
+ ],
141
166
  },
142
167
  {
143
- type: 'multiselect', name: 'features', message: 'Select features:',
168
+ type: "multiselect",
169
+ name: "features",
170
+ message: "选择功能 (空格选择,回车确认):",
144
171
  choices: [
145
- { title: 'ESLint + Prettier', value: 'lint', selected: true },
146
- { title: 'Vitest', value: 'vitest', selected: true },
147
- { title: 'Playwright', value: 'playwright', selected: true },
148
- { title: 'Vue3 Enterprise MCP', value: 'mcp', selected: true },
149
- { title: 'CI/CD Workflow', value: 'ci', selected: true },
150
- { title: 'Performance Benchmark', value: 'perf', selected: true }
151
- ]
172
+ { title: "ESLint + Prettier", value: "lint", selected: true },
173
+ { title: "Vitest (单元测试)", value: "vitest", selected: true },
174
+ {
175
+ title: "Playwright (E2E 测试)",
176
+ value: "playwright",
177
+ selected: true,
178
+ },
179
+ { title: "Vue3 Enterprise MCP", value: "mcp", selected: true },
180
+ { title: "CI/CD 工作流", value: "ci", selected: true },
181
+ { title: "性能基准测试", value: "perf", selected: true },
182
+ ],
152
183
  },
153
184
  {
154
- type: 'confirm', name: 'router', message: 'Add Vue Router?', initial: true
185
+ type: "confirm",
186
+ name: "needsTypeScript",
187
+ message: "使用 TypeScript?",
188
+ initial: true,
155
189
  },
156
190
  {
157
- type: 'confirm', name: 'pinia', message: 'Add Pinia?', initial: true
191
+ type: "confirm",
192
+ name: "needsRouter",
193
+ message: "添加 Vue Router?",
194
+ initial: true,
158
195
  },
159
196
  {
160
- type: 'confirm', name: 'overwrite',
161
- message: targetDir && fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0
162
- ? `Dir "${targetDir}" exists. Overwrite?` : 'Overwrite existing?',
163
- initial: false
164
- }
165
- ], { onCancel: () => { console.log(red('✖ Cancelled')); process.exit(1); } });
166
- if (!response.overwrite) {
167
- console.log(red('✖ Cancelled'));
197
+ type: "confirm",
198
+ name: "needsPinia",
199
+ message: "添加 Pinia (状态管理)?",
200
+ initial: true,
201
+ },
202
+ {
203
+ type: "confirm",
204
+ name: "overwrite",
205
+ message: targetDir &&
206
+ fs.existsSync(targetDir) &&
207
+ fs.readdirSync(targetDir).length > 0
208
+ ? `目录 "${targetDir}" 已存在且不为空。是否覆盖?`
209
+ : "",
210
+ initial: false,
211
+ },
212
+ ], {
213
+ onCancel: () => {
214
+ console.log(red("✖ 操作已取消"));
215
+ process.exit(1);
216
+ },
217
+ });
218
+ if (promptResult.overwrite === false) {
219
+ console.log(red("✖ 操作已取消"));
168
220
  process.exit(1);
169
221
  }
170
222
  const root = path.join(cwd, targetDir);
171
223
  if (fs.existsSync(root)) {
172
- if (argv.force || response.overwrite)
224
+ if (argv.force || promptResult.overwrite) {
173
225
  emptyDir(root);
226
+ }
174
227
  else {
175
- console.log(red(`✖ Dir "${targetDir}" exists`));
228
+ console.log(red(`✖ 目录 "${targetDir}" 已存在`));
229
+ console.log(yellow("提示: 使用 --force 强制覆盖"));
176
230
  process.exit(1);
177
231
  }
178
232
  }
179
- else
233
+ else {
180
234
  fs.mkdirSync(root, { recursive: true });
181
- console.log(`\n${cyan('Creating project...')}\n`);
182
- copyDir(path.resolve(__dirname, '../template'), root);
183
- const pkg = makePkg(targetDir, response.features || [], response.router, response.pinia);
184
- fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify(pkg, null, 2));
185
- const desc = {
186
- lint: '- 🧹 ESLint + Prettier',
187
- vitest: '- 🧪 Vitest Unit Tests',
188
- playwright: '- 🎭 Playwright E2E',
189
- mcp: '- 🔍 Vue3 Enterprise MCP',
190
- ci: '- CI/CD',
191
- perf: '- 📊 Performance'
235
+ }
236
+ console.log(`\n${cyan("正在创建项目...")}\n`);
237
+ const templateDir = path.resolve(__dirname, "../template");
238
+ copyDir(templateDir, root);
239
+ const pkg = await generatePackageJson(targetDir, {
240
+ features: promptResult.features || [],
241
+ needsRouter: promptResult.needsRouter,
242
+ needsPinia: promptResult.needsPinia,
243
+ });
244
+ fs.writeFileSync(path.join(root, "package.json"), JSON.stringify(pkg, null, 2));
245
+ const featureDescriptions = {
246
+ lint: "- 🧹 ESLint + Prettier 代码规范 + Git Hooks 自动检查",
247
+ vitest: "- 🧪 Vitest 单元测试",
248
+ playwright: "- 🎭 Playwright E2E 测试",
249
+ mcp: "- 🔍 Vue3 Enterprise MCP 代码审查",
250
+ ci: "- ⚡ CI/CD 自动化",
251
+ perf: "- 📊 性能基准测试",
192
252
  };
193
253
  const readme = `# ${pkg.name}
194
254
 
195
- Created with Vue3 Enterprise Toolchain
255
+ 使用 Vue3 Enterprise Toolchain 创建的项目
256
+
257
+ ## 功能特性
196
258
 
197
- ## Features
198
- ${(response.features || []).map((f) => desc[f]).filter(Boolean).join('\n')}
259
+ ${(promptResult.features || [])
260
+ .map((f) => featureDescriptions[f] || "")
261
+ .filter(Boolean)
262
+ .join("\n")}
263
+
264
+ ## 快速开始
199
265
 
200
- ## Quick Start
201
266
  \`\`\`bash
267
+ # 安装依赖
202
268
  npm install
269
+
270
+ # 启动开发服务器
203
271
  npm run dev
272
+
273
+ # 运行测试
274
+ npm test
275
+
276
+ # 构建生产版本
277
+ npm run build
204
278
  \`\`\`
205
279
 
206
- ## Scripts
207
- - \`npm run dev\` - Start dev server
208
- - \`npm run build\` - Build
209
- - \`npm test\` - Run tests
280
+ ## 可用脚本
281
+
282
+ - \`npm run dev\` - 启动开发服务器
283
+ - \`npm run build\` - 构建生产版本
284
+ - \`npm run preview\` - 预览生产版本
285
+ - \`npm test\` - 运行单元测试
286
+ - \`npm run test:e2e\` - 运行 E2E 测试
287
+ - \`npm run test:perf\` - 运行性能测试
288
+ - \`npm run lint\` - 运行 ESLint
289
+ - \`npm run format\` - 运行 Prettier
290
+
291
+ ## 文档
292
+
293
+ - [Vue3 Enterprise MCP 使用指南](https://github.com/your-org/vue3-enterprise-toolchain/blob/main/docs/mcp.md)
294
+ - [CI 集成指南](https://github.com/your-org/vue3-enterprise-toolchain/blob/main/docs/ci.md)
210
295
  `;
211
- fs.writeFileSync(path.join(root, 'README.md'), readme);
212
- const gitignore = `node_modules\ndist\n*.local\n.vscode\ncoverage\ntest-results\nplaywright-report\n.perf-baseline.json`;
213
- fs.writeFileSync(path.join(root, '.gitignore'), gitignore);
214
- console.log(`${green('✔')} Created!\n cd ${cyan(targetDir)}\n npm install\n npm run dev\n`);
296
+ fs.writeFileSync(path.join(root, "README.md"), readme);
297
+ const gitignore = `# Logs
298
+ logs
299
+ *.log
300
+ npm-debug.log*
301
+ yarn-debug.log*
302
+ yarn-error.log*
303
+ pnpm-debug.log*
304
+ lerna-debug.log*
305
+
306
+ node_modules
307
+ .DS_Store
308
+ dist
309
+ dist-ssr
310
+ *.local
311
+
312
+ # Editor directories and files
313
+ .vscode/*
314
+ !.vscode/extensions.json
315
+ .idea
316
+ *.suo
317
+ *.ntvs*
318
+ *.njsproj
319
+ *.sln
320
+ *.sw?
321
+
322
+ # Testing
323
+ coverage
324
+
325
+ # Playwright
326
+ test-results/
327
+ playwright-report/
328
+ playwright/.cache/
329
+
330
+ # Performance baseline
331
+ .perf-baseline.json
332
+ `;
333
+ fs.writeFileSync(path.join(root, ".gitignore"), gitignore);
334
+ // Initialize git and husky if lint feature is enabled
335
+ if (promptResult.features?.includes("lint")) {
336
+ const { execSync } = require("child_process");
337
+ try {
338
+ execSync("git init", { cwd: root, stdio: "ignore" });
339
+ execSync("npm run prepare", { cwd: root, stdio: "ignore" });
340
+ }
341
+ catch (e) {
342
+ // Git may not be installed
343
+ }
344
+ }
345
+ console.log(`${green("✔")} 项目创建成功!\n`);
346
+ console.log(` cd ${cyan(targetDir)}`);
347
+ console.log(` npm install`);
348
+ console.log(` npm run dev\n`);
349
+ // Git hooks info
350
+ if (promptResult.features?.includes("lint")) {
351
+ console.log(`${cyan("Git Hooks 配置:")}`);
352
+ console.log(` • pre-commit: 自动检查格式和类型`);
353
+ console.log(` • commit-msg: 验证提交信息格式`);
354
+ console.log(` 提交时自动运行检查,无需手动触发\n`);
355
+ }
356
+ if (promptResult.features?.includes("mcp")) {
357
+ console.log(`${cyan("MCP Server 配置:")}`);
358
+ console.log(` 在 Claude Desktop 的 claude_desktop_config.json 中添加:\n`);
359
+ console.log(` {
360
+ "mcpServers": {
361
+ "vue3-enterprise": {
362
+ "command": "npx",
363
+ "args": ["-y", "vue3-enterprise-mcp", "${targetDir}"]
364
+ }
365
+ }
366
+ }\n`);
367
+ }
368
+ if (promptResult.features?.includes("ci")) {
369
+ console.log(`${cyan("CI 配置:")}`);
370
+ console.log(` 已配置 GitHub Actions 工作流`);
371
+ console.log(` 提交代码后会自动运行测试和性能检查\n`);
372
+ }
215
373
  }
216
- main().catch((e) => { console.error(e); process.exit(1); });
374
+ main().catch((err) => {
375
+ console.error(err);
376
+ process.exit(1);
377
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-vue3-enterprise",
3
- "version": "1.0.0",
3
+ "version": "1.0.20",
4
4
  "description": "Vue3 + TypeScript + Vite 企业级项目脚手架",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,9 +13,7 @@
13
13
  "scripts": {
14
14
  "build": "tsc",
15
15
  "dev": "tsc --watch",
16
- "prepublishOnly": "npm run build",
17
- "test": "node ./dist/index.js --help",
18
- "test:create": "node ./dist/index.js test-project --force"
16
+ "prepublishOnly": "npm run build"
19
17
  },
20
18
  "dependencies": {
21
19
  "kolorist": "^1.8.0",
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env sh
2
+ . "$(dirname -- "$0")/_/husky.sh"
3
+
4
+ # ============================================
5
+ # 📝 Commit Message Validation
6
+ # Uses Conventional Commits format
7
+ # ============================================
8
+
9
+ echo ""
10
+ echo "📝 Validating commit message..."
11
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
12
+
13
+ # Run commitlint
14
+ if ! npx commitlint --edit ${1}; then
15
+ echo ""
16
+ echo "❌ Commit message validation FAILED"
17
+ echo ""
18
+ echo "📖 Correct format:"
19
+ echo " <type>(<scope>): <subject>"
20
+ echo ""
21
+ echo " Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert"
22
+ echo ""
23
+ echo " Examples:"
24
+ echo " • feat: add user login button"
25
+ echo " • fix: resolve memory leak in component"
26
+ echo " • docs: update API documentation"
27
+ echo ""
28
+ echo " Run 'npm run commit' for interactive commit"
29
+ echo ""
30
+ exit 1
31
+ fi
32
+
33
+ echo ""
34
+ echo "✅ Commit message validated!"
35
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env sh
2
+ . "$(dirname -- "$0")/_/husky.sh"
3
+
4
+ # ============================================
5
+ # 🧹 Pre-commit Checks
6
+ # - Code formatting (Prettier)
7
+ # - Type checking (vue-tsc)
8
+ # - Linting (ESLint)
9
+ # ============================================
10
+
11
+ echo ""
12
+ echo "🔍 Running pre-commit checks..."
13
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
14
+
15
+ # Run lint-staged
16
+ if ! npx lint-staged; then
17
+ echo ""
18
+ echo "❌ Pre-commit checks FAILED"
19
+ echo ""
20
+ echo "📖 Fix suggestions:"
21
+ echo " • Run 'npm run format' to fix formatting"
22
+ echo " • Run 'npm run lint' to fix linting errors"
23
+ echo " • Fix TypeScript errors manually"
24
+ echo ""
25
+ exit 1
26
+ fi
27
+
28
+ echo ""
29
+ echo "✅ Pre-commit checks passed!"
30
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
@@ -0,0 +1,28 @@
1
+ export default {
2
+ extends: ['@commitlint/config-conventional'],
3
+ rules: {
4
+ // Type envelope: build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test
5
+ 'type-enum': [
6
+ 2,
7
+ 'always',
8
+ [
9
+ 'feat', // New feature
10
+ 'fix', // Bug fix
11
+ 'docs', // Documentation only changes
12
+ 'style', // Changes that do not affect the meaning of the code (formatting)
13
+ 'refactor', // Code change that neither fixes a bug nor adds a feature
14
+ 'perf', // Performance improvement
15
+ 'test', // Adding or correcting tests
16
+ 'build', // Changes to build system or dependencies
17
+ 'ci', // Changes to CI configuration
18
+ 'chore', // Other changes that don't modify src or test files
19
+ 'revert', // Reverts a previous commit
20
+ ],
21
+ ],
22
+ 'type-case': [2, 'always', 'lower-case'],
23
+ 'type-empty': [2, 'never'],
24
+ 'subject-empty': [2, 'never'],
25
+ 'subject-full-stop': [2, 'never', '.'],
26
+ 'header-max-length': [2, 'always', 100],
27
+ },
28
+ }
@@ -0,0 +1,8 @@
1
+ export default {
2
+ // TypeScript and Vue files - check types
3
+ '*.ts': ['vue-tsc --noEmit', 'eslint --fix'],
4
+ '*.tsx': ['vue-tsc --noEmit', 'eslint --fix'],
5
+ '*.vue': ['vue-tsc --noEmit', 'eslint --fix'],
6
+ // Format all staged files
7
+ '**/*': ['prettier --write --ignore-unknown'],
8
+ }