@fastcar/cli 0.1.2 → 0.1.4
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/cli.js +239 -226
- package/package.json +1 -1
- package/skills/AGENTS.md +251 -0
- package/skills/fastcar-database/SKILL.md +436 -337
- package/skills/fastcar-framework/SKILL.md +577 -856
- package/skills/fastcar-rpc-microservices/SKILL.md +19 -69
- package/skills/fastcar-serverless/SKILL.md +48 -48
- package/skills/fastcar-toolkit/SKILL.md +22 -31
- package/skills/typescript-coding-style/SKILL.md +144 -0
- package/src/init.js +708 -700
- package/src/pack.js +7 -7
- package/src/skill.js +493 -364
- package/src/update.js +301 -0
- package/src/utils.js +2 -2
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.
|
|
58
|
+
await compressing.tgz.uncompress(src, dest);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
async function zipFile(src, dest) {
|
|
62
|
-
await compressing.
|
|
62
|
+
await compressing.tgz.compressDir(src, dest);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function readYaml(fp) {
|