create-vue3-enterprise 1.0.0 → 1.0.21

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,57 @@
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 { execSync } from "node:child_process";
6
+ import minimist from "minimist";
7
+ import prompts from "prompts";
8
+ import { red, green, cyan, yellow } from "kolorist";
8
9
  const argv = minimist(process.argv.slice(2), {
9
- string: ['_'],
10
- boolean: ['help', 'template', 'force'],
10
+ string: ["_"],
11
+ boolean: ["help", "template", "force"],
11
12
  alias: {
12
- h: 'help',
13
- t: 'template',
14
- f: 'force'
15
- }
13
+ h: "help",
14
+ t: "template",
15
+ f: "force",
16
+ },
16
17
  });
17
18
  const cwd = process.cwd();
18
19
  const __filename = fileURLToPath(import.meta.url);
19
20
  const __dirname = path.dirname(__filename);
20
21
  function printHelp() {
21
22
  console.log(`
22
- ${cyan('create-vue3-enterprise')}
23
+ ${cyan("create-vue3-enterprise")}
23
24
 
24
- ${green('Usage:')}
25
- npm create vue3-enterprise <project-name> [options]
25
+ ${green("用法:")}
26
+ npm create vue3-enterprise <项目名称> [选项]
26
27
 
27
- ${green('Options:')}
28
- -h, --help Show help
29
- -f, --force Force overwrite
28
+ ${green("选项:")}
29
+ -h, --help 显示帮助信息
30
+ -t, --template 指定模板(vue3-ts)
31
+ -f, --force 强制覆盖已存在的目录
30
32
 
31
- ${green('Examples:')}
33
+ ${green("示例:")}
32
34
  npm create vue3-enterprise my-project
35
+ npm create vue3-enterprise my-project --force
33
36
  `);
34
37
  }
35
38
  function isValidPackageName(projectName) {
36
39
  return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(projectName);
37
40
  }
38
41
  function toValidPackageName(projectName) {
39
- return projectName.trim().toLowerCase().replace(/\s+/g, '-').replace(/^[._]/, '').replace(/[^a-z0-9-~]+/g, '-');
42
+ return projectName
43
+ .trim()
44
+ .toLowerCase()
45
+ .replace(/\s+/g, "-")
46
+ .replace(/^[._]/, "")
47
+ .replace(/[^a-z0-9-~]+/g, "-");
40
48
  }
41
49
  function emptyDir(dir) {
42
50
  if (!fs.existsSync(dir))
43
51
  return;
44
52
  for (const file of fs.readdirSync(dir)) {
45
- fs.rmSync(path.resolve(dir, file), { recursive: true, force: true });
53
+ const filePath = path.resolve(dir, file);
54
+ fs.rmSync(filePath, { recursive: true, force: true });
46
55
  }
47
56
  }
48
57
  function copyDir(srcDir, destDir) {
@@ -51,166 +60,318 @@ function copyDir(srcDir, destDir) {
51
60
  const srcFile = path.resolve(srcDir, file);
52
61
  const destFile = path.resolve(destDir, file);
53
62
  const stat = fs.statSync(srcFile);
54
- if (stat.isDirectory())
63
+ if (stat.isDirectory()) {
55
64
  copyDir(srcFile, destFile);
56
- else
65
+ }
66
+ else {
57
67
  fs.copyFileSync(srcFile, destFile);
68
+ }
58
69
  }
59
70
  }
60
- function makePkg(targetDir, features, needRouter, needPinia) {
61
- const name = isValidPackageName(targetDir) ? targetDir : toValidPackageName(targetDir);
71
+ async function generatePackageJson(targetDir, options) {
62
72
  const pkg = {
63
- name,
64
- version: '0.0.0',
73
+ name: isValidPackageName(targetDir)
74
+ ? targetDir
75
+ : toValidPackageName(targetDir),
76
+ version: "0.0.0",
65
77
  private: true,
66
- type: 'module',
78
+ type: "module",
67
79
  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'
80
+ dev: "vite",
81
+ build: "vue-tsc && vite build",
82
+ preview: "vite preview",
83
+ test: "vitest",
84
+ "test:e2e": "playwright test",
85
+ "test:perf": "playwright test --config=playwright.config.perf.ts",
86
+ lint: "eslint . --ext .vue,.ts,.tsx --fix",
87
+ format: "prettier --write .",
88
+ "type-check": "vue-tsc --noEmit",
89
+ prepare: "husky install",
90
+ "lint-staged": "lint-staged",
91
+ },
92
+ dependencies: {
93
+ vue: "^3.4.0",
77
94
  },
78
- dependencies: { vue: '^3.4.0' },
79
95
  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
- }
96
+ "@vitejs/plugin-vue": "^5.0.0",
97
+ typescript: "^5.3.0",
98
+ vite: "^5.0.0",
99
+ "vue-tsc": "^1.8.0",
100
+ },
85
101
  };
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';
102
+ if (options.needsRouter) {
103
+ pkg.dependencies["vue-router"] = "^4.2.0";
104
+ }
105
+ if (options.needsPinia) {
106
+ pkg.dependencies["pinia"] = "^2.1.0";
107
+ }
108
+ if (options.features?.includes("lint")) {
109
+ pkg.devDependencies["@typescript-eslint/eslint-plugin"] = "^6.21.0";
110
+ pkg.devDependencies["@typescript-eslint/parser"] = "^6.21.0";
111
+ pkg.devDependencies["@vue/eslint-config-prettier"] = "^9.0.0";
112
+ pkg.devDependencies["@vue/eslint-config-typescript"] = "^12.0.0";
113
+ pkg.devDependencies["eslint"] = "^8.57.0";
114
+ pkg.devDependencies["eslint-plugin-vue"] = "^9.21.0";
115
+ pkg.devDependencies["prettier"] = "^3.2.0";
116
+ // Git hooks
117
+ pkg.devDependencies["husky"] = "^9.0.0";
118
+ pkg.devDependencies["lint-staged"] = "^15.2.0";
119
+ pkg.devDependencies["@commitlint/cli"] = "^19.0.0";
120
+ pkg.devDependencies["@commitlint/config-conventional"] = "^19.0.0";
121
+ }
122
+ if (options.features?.includes("vitest")) {
123
+ pkg.devDependencies["@vue/test-utils"] = "^2.4.0";
124
+ pkg.devDependencies["happy-dom"] = "^13.0.0";
125
+ pkg.devDependencies["vitest"] = "^1.2.0";
126
+ }
127
+ if (options.features?.includes("playwright")) {
128
+ pkg.devDependencies["@playwright/test"] = "^1.41.0";
129
+ }
130
+ if (options.features?.includes("mcp")) {
131
+ pkg.devDependencies["vue3-enterprise-mcp"] = "^1.0.0";
132
+ }
133
+ if (options.features?.includes("ci")) {
134
+ pkg.devDependencies["vue3-enterprise-ci"] = "^1.0.0";
116
135
  }
117
136
  return pkg;
118
137
  }
119
138
  async function main() {
120
- console.log(`\n${cyan('Vue3 Enterprise Toolchain')}\n`);
139
+ console.log(`\n${cyan("Vue3 Enterprise Toolchain")}\n`);
121
140
  if (argv.help) {
122
141
  printHelp();
123
142
  return;
124
143
  }
125
144
  let targetDir = argv._[0];
126
- const response = await prompts([
145
+ const promptResult = await prompts([
127
146
  {
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'; }
147
+ type: targetDir ? null : "text",
148
+ name: "projectName",
149
+ message: "项目名称:",
150
+ initial: "vue3-enterprise-project",
151
+ onState: (state) => {
152
+ targetDir = String(state.value).trim() || "vue3-enterprise-project";
153
+ },
133
154
  },
134
155
  {
135
- type: 'select', name: 'template', message: 'Select template:',
156
+ type: "select",
157
+ name: "template",
158
+ message: "选择模板:",
136
159
  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
- ]
160
+ { title: "Vue3 + TypeScript + Vite (推荐)", value: "vue3-ts" },
161
+ { title: "Vue3 + TypeScript + Vite + Pinia", value: "vue3-ts-pinia" },
162
+ {
163
+ title: "Vue3 + TypeScript + Vite + Pinia + Vue Router",
164
+ value: "vue3-ts-full",
165
+ },
166
+ ],
141
167
  },
142
168
  {
143
- type: 'multiselect', name: 'features', message: 'Select features:',
169
+ type: "multiselect",
170
+ name: "features",
171
+ message: "选择功能 (空格选择,回车确认):",
144
172
  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
- ]
173
+ { title: "ESLint + Prettier", value: "lint", selected: true },
174
+ { title: "Vitest (单元测试)", value: "vitest", selected: true },
175
+ {
176
+ title: "Playwright (E2E 测试)",
177
+ value: "playwright",
178
+ selected: true,
179
+ },
180
+ { title: "Vue3 Enterprise MCP", value: "mcp", selected: true },
181
+ { title: "CI/CD 工作流", value: "ci", selected: true },
182
+ { title: "性能基准测试", value: "perf", selected: true },
183
+ ],
152
184
  },
153
185
  {
154
- type: 'confirm', name: 'router', message: 'Add Vue Router?', initial: true
186
+ type: "confirm",
187
+ name: "needsTypeScript",
188
+ message: "使用 TypeScript?",
189
+ initial: true,
155
190
  },
156
191
  {
157
- type: 'confirm', name: 'pinia', message: 'Add Pinia?', initial: true
192
+ type: "confirm",
193
+ name: "needsRouter",
194
+ message: "添加 Vue Router?",
195
+ initial: true,
158
196
  },
159
197
  {
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'));
198
+ type: "confirm",
199
+ name: "needsPinia",
200
+ message: "添加 Pinia (状态管理)?",
201
+ initial: true,
202
+ },
203
+ {
204
+ type: "confirm",
205
+ name: "overwrite",
206
+ message: targetDir &&
207
+ fs.existsSync(targetDir) &&
208
+ fs.readdirSync(targetDir).length > 0
209
+ ? `目录 "${targetDir}" 已存在且不为空。是否覆盖?`
210
+ : "",
211
+ initial: false,
212
+ },
213
+ ], {
214
+ onCancel: () => {
215
+ console.log(red("✖ 操作已取消"));
216
+ process.exit(1);
217
+ },
218
+ });
219
+ if (promptResult.overwrite === false) {
220
+ console.log(red("✖ 操作已取消"));
168
221
  process.exit(1);
169
222
  }
170
223
  const root = path.join(cwd, targetDir);
171
224
  if (fs.existsSync(root)) {
172
- if (argv.force || response.overwrite)
225
+ if (argv.force || promptResult.overwrite) {
173
226
  emptyDir(root);
227
+ }
174
228
  else {
175
- console.log(red(`✖ Dir "${targetDir}" exists`));
229
+ console.log(red(`✖ 目录 "${targetDir}" 已存在`));
230
+ console.log(yellow("提示: 使用 --force 强制覆盖"));
176
231
  process.exit(1);
177
232
  }
178
233
  }
179
- else
234
+ else {
180
235
  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'
236
+ }
237
+ console.log(`\n${cyan("正在创建项目...")}\n`);
238
+ const templateDir = path.resolve(__dirname, "../template");
239
+ copyDir(templateDir, root);
240
+ const pkg = await generatePackageJson(targetDir, {
241
+ features: promptResult.features || [],
242
+ needsRouter: promptResult.needsRouter,
243
+ needsPinia: promptResult.needsPinia,
244
+ });
245
+ fs.writeFileSync(path.join(root, "package.json"), JSON.stringify(pkg, null, 2));
246
+ const featureDescriptions = {
247
+ lint: "- 🧹 ESLint + Prettier 代码规范 + Git Hooks 自动检查",
248
+ vitest: "- 🧪 Vitest 单元测试",
249
+ playwright: "- 🎭 Playwright E2E 测试",
250
+ mcp: "- 🔍 Vue3 Enterprise MCP 代码审查",
251
+ ci: "- ⚡ CI/CD 自动化",
252
+ perf: "- 📊 性能基准测试",
192
253
  };
193
254
  const readme = `# ${pkg.name}
194
255
 
195
- Created with Vue3 Enterprise Toolchain
256
+ 使用 Vue3 Enterprise Toolchain 创建的项目
257
+
258
+ ## 功能特性
196
259
 
197
- ## Features
198
- ${(response.features || []).map((f) => desc[f]).filter(Boolean).join('\n')}
260
+ ${(promptResult.features || [])
261
+ .map((f) => featureDescriptions[f] || "")
262
+ .filter(Boolean)
263
+ .join("\n")}
264
+
265
+ ## 快速开始
199
266
 
200
- ## Quick Start
201
267
  \`\`\`bash
268
+ # 安装依赖
202
269
  npm install
270
+
271
+ # 启动开发服务器
203
272
  npm run dev
273
+
274
+ # 运行测试
275
+ npm test
276
+
277
+ # 构建生产版本
278
+ npm run build
204
279
  \`\`\`
205
280
 
206
- ## Scripts
207
- - \`npm run dev\` - Start dev server
208
- - \`npm run build\` - Build
209
- - \`npm test\` - Run tests
281
+ ## 可用脚本
282
+
283
+ - \`npm run dev\` - 启动开发服务器
284
+ - \`npm run build\` - 构建生产版本
285
+ - \`npm run preview\` - 预览生产版本
286
+ - \`npm test\` - 运行单元测试
287
+ - \`npm run test:e2e\` - 运行 E2E 测试
288
+ - \`npm run test:perf\` - 运行性能测试
289
+ - \`npm run lint\` - 运行 ESLint
290
+ - \`npm run format\` - 运行 Prettier
291
+
292
+ ## 文档
293
+
294
+ - [Vue3 Enterprise MCP 使用指南](https://github.com/your-org/vue3-enterprise-toolchain/blob/main/docs/mcp.md)
295
+ - [CI 集成指南](https://github.com/your-org/vue3-enterprise-toolchain/blob/main/docs/ci.md)
210
296
  `;
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`);
297
+ fs.writeFileSync(path.join(root, "README.md"), readme);
298
+ const gitignore = `# Logs
299
+ logs
300
+ *.log
301
+ npm-debug.log*
302
+ yarn-debug.log*
303
+ yarn-error.log*
304
+ pnpm-debug.log*
305
+ lerna-debug.log*
306
+
307
+ node_modules
308
+ .DS_Store
309
+ dist
310
+ dist-ssr
311
+ *.local
312
+
313
+ # Editor directories and files
314
+ .vscode/*
315
+ !.vscode/extensions.json
316
+ .idea
317
+ *.suo
318
+ *.ntvs*
319
+ *.njsproj
320
+ *.sln
321
+ *.sw?
322
+
323
+ # Testing
324
+ coverage
325
+
326
+ # Playwright
327
+ test-results/
328
+ playwright-report/
329
+ playwright/.cache/
330
+
331
+ # Performance baseline
332
+ .perf-baseline.json
333
+ `;
334
+ fs.writeFileSync(path.join(root, ".gitignore"), gitignore);
335
+ // Initialize git and husky if lint feature is enabled
336
+ if (promptResult.features?.includes("lint")) {
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.21",
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
+ }