feima-shortcuts 0.1.4 → 0.1.5-beta.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.
package/bin/feima.js CHANGED
@@ -12,7 +12,7 @@ const run = () => {
12
12
  type: "list",
13
13
  name: "type",
14
14
  message: "请选择模版类型",
15
- choices: ["api", "template"],
15
+ choices: ["api", "template", "generate"],
16
16
  },
17
17
  // 页面模版创建方式
18
18
  {
@@ -64,6 +64,17 @@ const run = () => {
64
64
  return ["new"].includes(answers.template);
65
65
  },
66
66
  },
67
+
68
+ // 组件生成方式
69
+ {
70
+ type: "input",
71
+ message: "输入组件名称 (例如: button, input, card):",
72
+ name: "component-name",
73
+ default: "button", // 默认值
74
+ when: function (answers) {
75
+ return ["generate"].includes(answers.type);
76
+ },
77
+ },
67
78
  ])
68
79
  .then((answers) => {
69
80
  feima.run(answers);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feima-shortcuts",
3
- "version": "0.1.4",
3
+ "version": "0.1.5-beta.0",
4
4
  "description": "快捷指令",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -23,4 +23,4 @@
23
23
  "uuid": "^9.0.0"
24
24
  },
25
25
  "packageManager": "pnpm@10.6.3+sha512.bb45e34d50a9a76e858a95837301bfb6bd6d35aea2c5d52094fa497a467c43f5c440103ce2511e9e0a2f89c3d6071baac3358fc68ac6fb75e2ceb3d2736065e6"
26
- }
26
+ }
package/src/generate.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const restfulApi = require("./scripts/restful-api");
2
2
  const template = require("./scripts/template");
3
+ const generate = require("./scripts/generate");
3
4
 
4
5
  exports.run = function (answers) {
5
6
  if (answers.type == 'api') {
@@ -8,4 +9,7 @@ exports.run = function (answers) {
8
9
  if (answers.type == 'template') {
9
10
  template.run(answers)
10
11
  }
12
+ if (answers.type == 'generate') {
13
+ generate.run(answers)
14
+ }
11
15
  };
@@ -0,0 +1,250 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * 生成新的Vue组件
6
+ * @param {Object} answers - 用户输入的答案
7
+ */
8
+ function generateComponent(answers) {
9
+ let componentName = answers['component-name'];
10
+
11
+ // 确保组件名以F开头且首字母大写
12
+ if (!componentName.startsWith('F')) {
13
+ componentName = 'F' + componentName.charAt(0).toUpperCase() + componentName.slice(1);
14
+ }
15
+
16
+ // 移除开头的F,然后转换为kebab-case
17
+ const nameWithoutF = componentName.replace(/^F/, '');
18
+ const kebabName = nameWithoutF.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
19
+ const folderName = `f-${kebabName}`;
20
+
21
+ // 创建组件目录结构
22
+ const componentDir = path.join(__dirname, '../../../src/', folderName);
23
+ const srcDir = path.join(componentDir, 'src');
24
+ const testsDir = path.join(componentDir, '__tests__');
25
+
26
+ // 创建目录
27
+ if (!fs.existsSync(componentDir)) {
28
+ fs.mkdirSync(componentDir, { recursive: true });
29
+ }
30
+ if (!fs.existsSync(srcDir)) {
31
+ fs.mkdirSync(srcDir, { recursive: true });
32
+ }
33
+ if (!fs.existsSync(testsDir)) {
34
+ fs.mkdirSync(testsDir, { recursive: true });
35
+ }
36
+
37
+ // 生成Vue组件文件
38
+ const vueTemplate = generateVueTemplate(componentName);
39
+ fs.writeFileSync(path.join(srcDir, `${componentName}.vue`), vueTemplate);
40
+
41
+ // 生成index.ts文件
42
+ const indexTemplate = generateIndexTemplate(componentName);
43
+ fs.writeFileSync(path.join(componentDir, 'index.ts'), indexTemplate);
44
+
45
+ // 生成测试文件
46
+ const testTemplate = generateTestTemplate(componentName);
47
+ fs.writeFileSync(path.join(testsDir, `${componentName}.spec.ts`), testTemplate);
48
+
49
+ // 更新主入口文件
50
+ updateMainIndex(componentName);
51
+
52
+ // 更新解析器
53
+ updateResolver(componentName);
54
+
55
+ console.log(`✅ 组件 ${componentName} 生成成功!`);
56
+ console.log(`📁 组件目录: ${folderName}`);
57
+ console.log(`🔧 已自动更新 index.ts 和 resolver.ts`);
58
+ }
59
+
60
+ /**
61
+ * 生成Vue组件模板
62
+ */
63
+ function generateVueTemplate(componentName) {
64
+ return `<template>
65
+ <div class="feima-${componentName.toLowerCase()}">
66
+ <!-- ${componentName} 组件内容 -->
67
+ <slot></slot>
68
+ </div>
69
+ </template>
70
+
71
+ <script setup lang="ts">
72
+ defineOptions({ name: "${componentName}" });
73
+
74
+ // 组件属性定义
75
+ interface Props {
76
+ // 在这里定义组件属性
77
+ }
78
+
79
+ // 组件事件定义
80
+ interface Emits {
81
+ // 在这里定义组件事件
82
+ }
83
+
84
+ const props = withDefaults(defineProps<Props>(), {
85
+ // 默认属性值
86
+ });
87
+
88
+ const emit = defineEmits<Emits>();
89
+ </script>
90
+
91
+ <style scoped>
92
+ .feima-${componentName.toLowerCase()} {
93
+ /* 组件样式 */
94
+ }
95
+ </style>
96
+ `;
97
+ }
98
+
99
+ /**
100
+ * 生成index.ts模板
101
+ */
102
+ function generateIndexTemplate(componentName) {
103
+ return `import type { App } from "vue";
104
+ import ${componentName}Vue from "./src/${componentName}.vue";
105
+
106
+ export const ${componentName} = ${componentName}Vue;
107
+
108
+ export default {
109
+ install(app: App) {
110
+ app.component("${componentName}", ${componentName});
111
+ },
112
+ };
113
+ `;
114
+ }
115
+
116
+ /**
117
+ * 生成测试文件模板
118
+ */
119
+ function generateTestTemplate(componentName) {
120
+ return `import { describe, it, expect } from 'vitest';
121
+ import { mount } from '@vue/test-utils';
122
+ import ${componentName} from '../src/${componentName}.vue';
123
+
124
+ describe('${componentName}', () => {
125
+ it('renders properly', () => {
126
+ const wrapper = mount(${componentName});
127
+ expect(wrapper.exists()).toBe(true);
128
+ });
129
+ });
130
+ `;
131
+ }
132
+
133
+ /**
134
+ * 更新主入口文件
135
+ */
136
+ function updateMainIndex(componentName) {
137
+ const indexPath = path.join(__dirname, '../../../src/index.ts');
138
+
139
+ if (!fs.existsSync(indexPath)) {
140
+ console.log('⚠️ index.ts 文件不存在,跳过更新');
141
+ return;
142
+ }
143
+
144
+ let content = fs.readFileSync(indexPath, 'utf8');
145
+
146
+ // 添加导入语句
147
+ const nameWithoutF = componentName.replace(/^F/, '');
148
+ const kebabName = nameWithoutF.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
149
+ const folderName = `f-${kebabName}`;
150
+ const importLine = `import ${componentName}Installer, { ${componentName} } from "./${folderName}";`;
151
+
152
+ // 查找现有的导入语句位置,保持原有格式
153
+ const importMatch = content.match(/(import.*from "\.\/.*";\s*)+/);
154
+ if (importMatch) {
155
+ // 在最后一个导入语句后添加新的导入,保持换行格式
156
+ const lastImport = importMatch[0].trim();
157
+ const hasNewlineAfter = content.match(importMatch[0] + '\n');
158
+ const newlineAfter = hasNewlineAfter ? '\n' : '\n';
159
+ content = content.replace(importMatch[0], importMatch[0] + importLine + newlineAfter);
160
+ } else {
161
+ // 如果没有找到导入语句,在文件开头添加
162
+ content = content.replace(/^/, importLine + '\n');
163
+ }
164
+
165
+ // 添加到安装器数组,保持原有缩进格式
166
+ const installerMatch = content.match(/const installers = \[([\s\S]*?)\];/);
167
+ if (installerMatch) {
168
+ // 解析现有的安装器列表,保持每行的格式
169
+ const installerContent = installerMatch[1];
170
+ const lines = installerContent.split('\n').map(line => line.trim()).filter(line => line);
171
+
172
+ // 清理每行末尾的逗号
173
+ const cleanLines = lines.map(line => line.replace(/,$/, ''));
174
+
175
+ // 添加新的安装器,保持格式
176
+ cleanLines.push(`${componentName}Installer`);
177
+
178
+ // 重新构建数组,每行都有正确的缩进
179
+ const formattedInstallers = cleanLines.map(line => ` ${line}`).join(',\n');
180
+ content = content.replace(installerMatch[0], `const installers = [\n${formattedInstallers}\n];`);
181
+ }
182
+
183
+ // 添加到导出列表,保持原有格式
184
+ const exportMatch = content.match(/export \{ ([^}]+) \};/);
185
+ if (exportMatch) {
186
+ let currentExports = exportMatch[1].trim();
187
+
188
+ // 清理可能存在的多余逗号
189
+ currentExports = currentExports.replace(/,\s*,+/g, ','); // 移除连续逗号
190
+ currentExports = currentExports.replace(/,\s*$/, ''); // 移除末尾逗号
191
+ currentExports = currentExports.replace(/^\s*,/, ''); // 移除开头逗号
192
+
193
+ // 检查是否已经有内容,如果有则添加逗号,否则直接添加
194
+ const separator = currentExports ? ', ' : '';
195
+ const newExports = currentExports + separator + componentName;
196
+ content = content.replace(exportMatch[0], `export { ${newExports} };`);
197
+ }
198
+
199
+ fs.writeFileSync(indexPath, content);
200
+ console.log(`📝 已更新 index.ts`);
201
+ }
202
+
203
+ /**
204
+ * 更新解析器文件
205
+ */
206
+ function updateResolver(componentName) {
207
+ const resolverPath = path.join(__dirname, '../../../src/resolver.ts');
208
+
209
+ if (!fs.existsSync(resolverPath)) {
210
+ console.log('⚠️ resolver.ts 文件不存在,跳过更新');
211
+ return;
212
+ }
213
+
214
+ let content = fs.readFileSync(resolverPath, 'utf8');
215
+
216
+ // 添加到支持的组件列表,保持原有缩进格式
217
+ const supportedMatch = content.match(/const supportedComponents = \[([\s\S]*?)\];/);
218
+ if (supportedMatch) {
219
+ // 解析现有的组件列表,保持每行的格式
220
+ const componentContent = supportedMatch[1];
221
+ const lines = componentContent.split('\n').map(line => line.trim()).filter(line => line);
222
+
223
+ // 清理每行末尾的逗号
224
+ const cleanLines = lines.map(line => line.replace(/,$/, ''));
225
+
226
+ // 检查是否已经存在该组件,避免重复添加
227
+ const existingComponents = cleanLines.map(line => line.replace(/['"]/g, '').trim());
228
+ if (!existingComponents.includes(componentName)) {
229
+ // 添加新的组件,保持格式
230
+ cleanLines.push(`"${componentName}"`);
231
+
232
+ // 重新构建数组,每行都有正确的缩进
233
+ const formattedComponents = cleanLines.map(line => ` ${line}`).join(',\n');
234
+ content = content.replace(supportedMatch[0], `const supportedComponents = [\n${formattedComponents}\n];`);
235
+ } else {
236
+ console.log(`⚠️ 组件 ${componentName} 已存在于 supportedComponents 中,跳过添加`);
237
+ }
238
+ }
239
+
240
+ fs.writeFileSync(resolverPath, content);
241
+ console.log(`📝 已更新 resolver.ts`);
242
+ }
243
+
244
+ exports.run = function (answers) {
245
+ if (answers['component-name']) {
246
+ generateComponent(answers);
247
+ } else {
248
+ console.log('❌ 请提供组件名称');
249
+ }
250
+ };