@fastcar/cli 0.1.1 → 0.1.3

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/src/pack.js CHANGED
@@ -101,8 +101,8 @@ async function packProject(projectPath, outputPath, packageManager) {
101
101
  const version = packageJson.version || "0.0.0";
102
102
  const folderName = path.basename(cwd);
103
103
  const defaultOutputDir = outputPath || path.join(cwd, "dist");
104
- const zipFileName = `${projectName}-${version}.zip`;
105
- const zipFilePath = path.join(defaultOutputDir, zipFileName);
104
+ const tgzFileName = `${projectName}-${version}.tgz`;
105
+ const tgzFilePath = path.join(defaultOutputDir, tgzFileName);
106
106
 
107
107
  // 创建临时目录,使用项目文件夹名作为根目录名
108
108
  const tempDir = path.join(cwd, `.pack-temp-${Date.now()}`);
@@ -143,17 +143,17 @@ async function packProject(projectPath, outputPath, packageManager) {
143
143
 
144
144
  // 打包
145
145
  console.log("📦 正在生成压缩包...");
146
- if (fs.existsSync(zipFilePath)) {
147
- fs.rmSync(zipFilePath);
146
+ if (fs.existsSync(tgzFilePath)) {
147
+ fs.rmSync(tgzFilePath);
148
148
  }
149
- await utils.zipFile(rootDir, zipFilePath);
149
+ await utils.zipFile(rootDir, tgzFilePath);
150
150
 
151
151
  console.log("✅ 打包完成!");
152
- console.log(` 输出文件: ${zipFilePath}`);
152
+ console.log(` 输出文件: ${tgzFilePath}`);
153
153
  console.log(` 解压目录: ${folderName}/`);
154
154
 
155
155
  // 计算文件大小
156
- const stats = fs.statSync(zipFilePath);
156
+ const stats = fs.statSync(tgzFilePath);
157
157
  const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
158
158
  console.log(` 文件大小: ${sizeMB} MB`);
159
159
  } catch (error) {
package/src/reverse.js CHANGED
@@ -1,6 +1,8 @@
1
1
  const path = require("path");
2
2
  const fs = require("fs");
3
3
  const process = require("process");
4
+ const yaml = require("yaml");
5
+ const inquirer = require("inquirer");
4
6
 
5
7
  // 默认配置文件模板
6
8
  const defaultConfig = {
@@ -25,18 +27,70 @@ const defaultConfig = {
25
27
  ignoreCamelcase: false,
26
28
  };
27
29
 
30
+ // 支持的配置文件名
31
+ const CONFIG_FILES = {
32
+ json: "reverse.config.json",
33
+ yml: "reverse.config.yml",
34
+ };
35
+
36
+ // 查找已存在的配置文件
37
+ function findExistingConfig(cwd) {
38
+ for (const ext of ["yml", "json"]) {
39
+ const configPath = path.join(cwd, CONFIG_FILES[ext]);
40
+ if (fs.existsSync(configPath)) {
41
+ return { ext, configPath };
42
+ }
43
+ }
44
+ return null;
45
+ }
46
+
47
+ // 读取配置文件
48
+ function readConfig(configPath, ext) {
49
+ const content = fs.readFileSync(configPath, "utf-8");
50
+ if (ext === "yml") {
51
+ return yaml.parse(content);
52
+ }
53
+ return JSON.parse(content);
54
+ }
55
+
56
+ // 写入配置文件
57
+ function writeConfig(configPath, ext, config) {
58
+ if (ext === "yml") {
59
+ fs.writeFileSync(configPath, yaml.stringify(config));
60
+ } else {
61
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
62
+ }
63
+ }
64
+
28
65
  // 生成默认配置文件
29
- function initReverseConfig() {
66
+ async function initReverseConfig() {
30
67
  const cwd = process.cwd();
31
- const configPath = path.join(cwd, "reverse.config.json");
32
68
 
33
- // 检查是否已存在
34
- if (fs.existsSync(configPath)) {
35
- console.log("⚠️ reverse.config.json 配置文件已存在");
69
+ // 检查是否已存在任何格式的配置文件
70
+ const existing = findExistingConfig(cwd);
71
+ if (existing) {
72
+ console.log(`⚠️ ${path.basename(existing.configPath)} 配置文件已存在`);
36
73
  console.log(" 如需重新生成,请先删除现有文件");
37
74
  return;
38
75
  }
39
76
 
77
+ // 交互式选择配置格式
78
+ const answer = await inquirer.prompt([
79
+ {
80
+ type: "list",
81
+ name: "format",
82
+ message: "选择配置文件格式:",
83
+ choices: [
84
+ { name: "YAML (reverse.config.yml)", value: "yml" },
85
+ { name: "JSON (reverse.config.json)", value: "json" },
86
+ ],
87
+ default: "yml",
88
+ },
89
+ ]);
90
+
91
+ const ext = answer.format;
92
+ const configPath = path.join(cwd, CONFIG_FILES[ext]);
93
+
40
94
  // 填充默认路径
41
95
  const config = {
42
96
  ...defaultConfig,
@@ -45,8 +99,8 @@ function initReverseConfig() {
45
99
  };
46
100
 
47
101
  try {
48
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
49
- console.log("✅ 已生成 reverse.config.json 配置文件");
102
+ writeConfig(configPath, ext, config);
103
+ console.log(`✅ 已生成 ${CONFIG_FILES[ext]} 配置文件`);
50
104
  console.log("📁 文件路径:", configPath);
51
105
  console.log("\n💡 请根据需要修改以下配置:");
52
106
  console.log(" • tables: 要逆向生成的表名数组");
@@ -72,11 +126,31 @@ async function reverseGenerate(args = []) {
72
126
  }
73
127
 
74
128
  // 读取配置文件
75
- const configPath = path.join(cwd, "reverse.config.json");
76
- if (!fs.existsSync(configPath)) {
77
- console.log("❌ 未找到 reverse.config.json 配置文件");
129
+ const existing = findExistingConfig(cwd);
130
+ if (!existing) {
131
+ console.log("❌ 未找到 reverse.config.yml 或 reverse.config.json 配置文件");
78
132
  console.log(" 请在项目根目录创建该文件,格式如下:");
79
133
  console.log(`
134
+ YAML 格式 (reverse.config.yml):
135
+ tables: [test]
136
+ modelDir: ${cwd.replace(/\\/g, "/")}/src/models
137
+ mapperDir: ${cwd.replace(/\\/g, "/")}/src/mappers
138
+ dbConfig:
139
+ host: localhost
140
+ port: 3306
141
+ user: root
142
+ password: password
143
+ database: test_db
144
+ style:
145
+ tabWidth: 4
146
+ printWidth: 200
147
+ trailingComma: es5
148
+ useTabs: true
149
+ parser: typescript
150
+ endOfLine: crlf
151
+ ignoreCamelcase: false
152
+
153
+ JSON 格式 (reverse.config.json):
80
154
  {
81
155
  "tables": ["test"],
82
156
  "modelDir": "${cwd.replace(/\\/g, "/")}/src/models",
@@ -104,9 +178,9 @@ async function reverseGenerate(args = []) {
104
178
 
105
179
  let config;
106
180
  try {
107
- config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
181
+ config = readConfig(existing.configPath, existing.ext);
108
182
  } catch (error) {
109
- console.log("❌ 配置文件解析失败:", error.message);
183
+ console.log(`❌ 配置文件 ${path.basename(existing.configPath)} 解析失败:`, error.message);
110
184
  return;
111
185
  }
112
186
 
package/src/update.js CHANGED
@@ -0,0 +1,301 @@
1
+ const process = require("process");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const { execSync } = require("child_process");
5
+ const compressing = require("compressing");
6
+ const utils = require("./utils");
7
+ const templates = require("./templates.json");
8
+
9
+ /**
10
+ * 从 npm 下载模板包并仅同步 template/target 文件夹
11
+ * @param {string} packageName - 模板包名(如 @fastcar/template-cos)
12
+ * @param {string} targetDir - 目标目录(当前项目目录)
13
+ */
14
+ async function updateTemplateTarget(packageName, targetDir) {
15
+ console.log(`📦 正在更新模板 ${packageName}...`);
16
+ console.log(`📂 目标目录: ${targetDir}`);
17
+
18
+ // 使用 npm pack 下载包
19
+ const tempDir = path.join(process.cwd(), `.fastcar-update-temp-${Date.now()}`);
20
+ fs.mkdirSync(tempDir, { recursive: true });
21
+ console.log(`📂 临时目录: ${tempDir}`);
22
+
23
+ try {
24
+ // 下载 tarball
25
+ try {
26
+ console.log(`⬇️ 执行: npm pack ${packageName}...`);
27
+ execSync(`npm pack ${packageName} --pack-destination "${tempDir}"`, {
28
+ stdio: "pipe",
29
+ cwd: tempDir,
30
+ });
31
+ console.log(`✅ npm pack 执行成功`);
32
+ } catch (packError) {
33
+ const errorMsg = packError.message || "";
34
+ if (errorMsg.includes("E404") || errorMsg.includes("not found")) {
35
+ throw new Error(
36
+ `模板包 "${packageName}" 不存在\n` +
37
+ `💡 可能的原因:\n` +
38
+ ` 1. 包名拼写错误\n` +
39
+ ` 2. 该模板尚未发布到 npm\n` +
40
+ ` 3. 你没有该私有包的访问权限\n` +
41
+ `💡 解决方案:\n` +
42
+ ` - 检查模板名称是否正确\n` +
43
+ ` - 访问 https://www.npmjs.com/package/${packageName} 确认包是否存在`,
44
+ );
45
+ } else if (
46
+ errorMsg.includes("network") ||
47
+ errorMsg.includes("ECONNREFUSED")
48
+ ) {
49
+ throw new Error(
50
+ `网络连接失败,无法下载模板包 "${packageName}"\n` +
51
+ `💡 可能的原因:\n` +
52
+ ` 1. 网络连接问题\n` +
53
+ ` 2. npm registry 无法访问\n` +
54
+ ` 3. 代理设置问题\n` +
55
+ `💡 解决方案:\n` +
56
+ ` - 检查网络连接\n` +
57
+ ` - 尝试切换 npm 镜像源:npm config set registry https://registry.npmmirror.com\n` +
58
+ ` - 检查代理设置:npm config get proxy`,
59
+ );
60
+ } else {
61
+ throw new Error(
62
+ `下载模板包 "${packageName}" 失败\n` +
63
+ `📋 错误详情:${packError.message}\n` +
64
+ `💡 尝试重新执行命令,或手动检查 npm 是否正常工作`,
65
+ );
66
+ }
67
+ }
68
+
69
+ // 找到下载的 tarball 文件
70
+ console.log(`📂 读取临时目录内容...`);
71
+ const files = fs.readdirSync(tempDir);
72
+ console.log(`📄 找到文件: ${files.join(", ")}`);
73
+
74
+ const tarball = files.find((f) => f.endsWith(".tgz"));
75
+ console.log(`📦 tarball 文件: ${tarball}`);
76
+
77
+ if (!tarball) {
78
+ throw new Error(
79
+ `无法找到下载的模板包文件\n` +
80
+ `💡 可能的原因:npm pack 命令执行异常\n` +
81
+ `💡 解决方案:\n` +
82
+ ` - 检查 npm 版本:npm --version\n` +
83
+ ` - 尝试手动下载:npm pack ${packageName}`,
84
+ );
85
+ }
86
+
87
+ const tarballPath = path.join(tempDir, tarball);
88
+
89
+ // 解压 tarball
90
+ try {
91
+ const extractDir = path.join(tempDir, "extracted");
92
+ fs.mkdirSync(extractDir, { recursive: true });
93
+
94
+ await compressing.tgz.uncompress(tarballPath, extractDir);
95
+ } catch (extractError) {
96
+ throw new Error(
97
+ `解压模板包失败\n` +
98
+ `📋 错误详情:${extractError.message}\n` +
99
+ `💡 解决方案:\n` +
100
+ ` - 检查 compressing 库是否正常工作\n` +
101
+ ` - 尝试手动解压:tar -xzf ${tarballPath}`,
102
+ );
103
+ }
104
+
105
+ // npm pack 解压后会得到 package 目录
106
+ const packageDir = path.join(tempDir, "extracted", "package");
107
+ console.log(`📂 检查 package 目录: ${packageDir}`);
108
+ console.log(`📂 目录是否存在: ${fs.existsSync(packageDir)}`);
109
+
110
+ // 列出 extracted 目录内容以便调试
111
+ const extractDir = path.join(tempDir, "extracted");
112
+ if (fs.existsSync(extractDir)) {
113
+ const extractedFiles = fs.readdirSync(extractDir);
114
+ console.log(`📄 extracted 目录内容: ${extractedFiles.join(", ")}`);
115
+ }
116
+
117
+ if (!fs.existsSync(packageDir)) {
118
+ throw new Error(
119
+ `模板包结构不正确,缺少 package 目录\n` +
120
+ `💡 可能的原因:模板包打包格式不正确\n` +
121
+ `💡 解决方案:联系模板维护者检查包结构`,
122
+ );
123
+ }
124
+
125
+ // 检查 template/target 目录
126
+ const templateTargetDir = path.join(packageDir, "template", "target");
127
+ console.log(`📂 检查 template/target 目录: ${templateTargetDir}`);
128
+ console.log(`📂 template/target 目录是否存在: ${fs.existsSync(templateTargetDir)}`);
129
+
130
+ if (!fs.existsSync(templateTargetDir)) {
131
+ throw new Error(
132
+ `模板包中不存在 template/target 目录\n` +
133
+ `💡 可能的原因:该模板不支持 target 同步更新\n` +
134
+ `💡 解决方案:联系模板维护者确认包结构`,
135
+ );
136
+ }
137
+
138
+ // 检查源目录是否有内容
139
+ const sourceFiles = fs.readdirSync(templateTargetDir);
140
+ console.log(`📄 源目录文件数: ${sourceFiles.length}`);
141
+ if (sourceFiles.length === 0) {
142
+ throw new Error(
143
+ `template/target 目录为空\n` +
144
+ `💡 可能原因:模板包没有正确打包`,
145
+ );
146
+ }
147
+
148
+ // 目标 target 目录
149
+ const destTargetDir = path.join(targetDir, "target");
150
+ console.log(`📂 目标 target 目录: ${destTargetDir}`);
151
+
152
+ // 如果目标 target 目录已存在,先备份
153
+ if (fs.existsSync(destTargetDir)) {
154
+ const backupDir = path.join(targetDir, `target-backup-${Date.now()}`);
155
+ console.log(`📦 备份现有 target 目录到: ${backupDir}`);
156
+ utils.copyDirectory(destTargetDir, backupDir);
157
+
158
+ // 删除旧的 target 目录
159
+ console.log(`🗑️ 删除旧的 target 目录...`);
160
+ utils.delDirEctory(destTargetDir);
161
+ }
162
+
163
+ // 复制 template/target 到目标 target 目录
164
+ console.log("📋 复制 template/target 文件...");
165
+ console.log(` 从: ${templateTargetDir}`);
166
+ console.log(` 到: ${destTargetDir}`);
167
+
168
+ const copyResult = utils.copyDirectory(templateTargetDir, destTargetDir);
169
+ if (copyResult === false) {
170
+ throw new Error(
171
+ `复制 target 文件失败\n` +
172
+ `💡 可能原因:\n` +
173
+ ` 1. 源目录不存在或为空\n` +
174
+ ` 2. 目标目录没有写入权限\n` +
175
+ ` 3. 磁盘空间不足`,
176
+ );
177
+ }
178
+
179
+ // 检查目标目录内容
180
+ if (fs.existsSync(destTargetDir)) {
181
+ const targetFiles = fs.readdirSync(destTargetDir);
182
+ console.log(`📄 目标目录内容: ${targetFiles.join(", ") || "(空)"}`);
183
+
184
+ if (targetFiles.length === 0) {
185
+ throw new Error(
186
+ `目标目录为空,复制可能失败\n` + `💡 请检查模板包内容是否正确`,
187
+ );
188
+ }
189
+ } else {
190
+ throw new Error(
191
+ `目标目录创建失败: ${destTargetDir}\n` + `💡 请检查是否有写入权限`,
192
+ );
193
+ }
194
+
195
+ console.log(`✅ 模板 ${packageName} 的 target 目录更新完成`);
196
+
197
+ // 清理临时目录
198
+ utils.delDirEctory(tempDir);
199
+
200
+ return true;
201
+ } catch (error) {
202
+ // 清理临时目录
203
+ if (fs.existsSync(tempDir)) {
204
+ utils.delDirEctory(tempDir);
205
+ }
206
+ throw error;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * 更新 @fastcar/template-cos 模块的 target 文件夹
212
+ * @param {string[]} args - 命令行参数
213
+ */
214
+ async function updateCosTemplate(args = []) {
215
+ try {
216
+ const currDir = process.cwd();
217
+ const templateName = "cos";
218
+ const templateConfig = templates[templateName];
219
+
220
+ if (!templateConfig) {
221
+ console.error("\n" + "=".repeat(50));
222
+ console.error("❌ 模板配置不存在");
223
+ console.error("=".repeat(50));
224
+ console.error(`\n📋 模板名称:${templateName}`);
225
+ console.error("\n💡 可用的模板列表:");
226
+ Object.keys(templates).forEach((key) => {
227
+ console.error(` • ${key} - ${templates[key].description}`);
228
+ });
229
+ console.error("=".repeat(50) + "\n");
230
+ process.exit(1);
231
+ }
232
+
233
+ console.log(
234
+ `\n🚀 更新模板: ${templateConfig.name} - ${templateConfig.description}\n`,
235
+ );
236
+
237
+ // 检查当前目录是否有 package.json(确认是项目目录)
238
+ const packageJsonPath = path.join(currDir, "package.json");
239
+ if (!fs.existsSync(packageJsonPath)) {
240
+ console.warn("⚠️ 当前目录没有 package.json,请确认已在项目根目录执行此命令");
241
+ const inquirer = require("inquirer");
242
+ const answer = await inquirer.prompt([
243
+ {
244
+ type: "confirm",
245
+ name: "continue",
246
+ message: "是否继续更新?",
247
+ default: false,
248
+ },
249
+ ]);
250
+ if (!answer.continue) {
251
+ console.log("❌ 已取消更新");
252
+ return;
253
+ }
254
+ }
255
+
256
+ // 执行更新
257
+ await updateTemplateTarget(templateConfig.package, currDir);
258
+
259
+ console.log("\n✨ target 目录更新完成!");
260
+ console.log(`📁 项目路径: ${currDir}`);
261
+ console.log(`📦 使用模板: ${templateConfig.package}`);
262
+ console.log();
263
+ } catch (error) {
264
+ console.error("\n" + "=".repeat(50));
265
+ console.error("❌ 更新失败");
266
+ console.error("=".repeat(50));
267
+
268
+ if (error.message) {
269
+ console.error("\n📋 错误信息:");
270
+ console.error(error.message);
271
+ }
272
+
273
+ if (error.stderr) {
274
+ console.error("\n📋 详细日志:");
275
+ console.error(error.stderr.toString());
276
+ }
277
+
278
+ if (error.code) {
279
+ console.error(`\n📋 错误代码:${error.code}`);
280
+ }
281
+
282
+ console.error("\n" + "-".repeat(50));
283
+ console.error("💡 如果问题持续存在,请尝试以下操作:");
284
+ console.error(" 1. 检查网络连接是否正常");
285
+ console.error(
286
+ " 2. 更新 fastcar-cli 到最新版本:npm install -g @fastcar/cli",
287
+ );
288
+ console.error(" 3. 清除 npm 缓存:npm cache clean --force");
289
+ console.error(
290
+ " 4. 访问 https://github.com/williamDazhangyu/fastcar-cli/issues 提交问题",
291
+ );
292
+ console.error("-".repeat(50) + "\n");
293
+
294
+ process.exit(1);
295
+ }
296
+ }
297
+
298
+ module.exports = {
299
+ updateCosTemplate,
300
+ updateTemplateTarget,
301
+ };
package/src/utils.js CHANGED
@@ -55,11 +55,11 @@ function delDirEctory(src) {
55
55
  }
56
56
 
57
57
  async function unzipFile(src, dest) {
58
- await compressing.zip.uncompress(src, dest);
58
+ await compressing.tgz.uncompress(src, dest);
59
59
  }
60
60
 
61
61
  async function zipFile(src, dest) {
62
- await compressing.zip.compressDir(src, dest);
62
+ await compressing.tgz.compressDir(src, dest);
63
63
  }
64
64
 
65
65
  function readYaml(fp) {