@agile-team/robot-cli 1.0.4 → 1.0.6
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/index.js +13 -7
- package/lib/create.js +27 -15
- package/lib/download.js +48 -10
- package/lib/templates.js +3 -5
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
4
4
|
import { dirname, join, resolve } from 'path';
|
|
5
5
|
import { existsSync } from 'fs';
|
|
6
6
|
|
|
@@ -61,6 +61,12 @@ async function loadModules() {
|
|
|
61
61
|
try {
|
|
62
62
|
const libPath = resolveLibPath();
|
|
63
63
|
|
|
64
|
+
// 将路径转换为 file:// URL 格式(Windows 兼容)
|
|
65
|
+
const createUrl = pathToFileURL(join(libPath, 'create.js')).href;
|
|
66
|
+
const cacheUrl = pathToFileURL(join(libPath, 'cache.js')).href;
|
|
67
|
+
const templatesUrl = pathToFileURL(join(libPath, 'templates.js')).href;
|
|
68
|
+
const utilsUrl = pathToFileURL(join(libPath, 'utils.js')).href;
|
|
69
|
+
|
|
64
70
|
// 动态导入所有需要的模块
|
|
65
71
|
const [
|
|
66
72
|
{ Command },
|
|
@@ -76,10 +82,10 @@ async function loadModules() {
|
|
|
76
82
|
import('chalk'),
|
|
77
83
|
import('boxen'),
|
|
78
84
|
import('inquirer'),
|
|
79
|
-
import(
|
|
80
|
-
import(
|
|
81
|
-
import(
|
|
82
|
-
import(
|
|
85
|
+
import(createUrl),
|
|
86
|
+
import(cacheUrl),
|
|
87
|
+
import(templatesUrl),
|
|
88
|
+
import(utilsUrl)
|
|
83
89
|
]);
|
|
84
90
|
|
|
85
91
|
return {
|
|
@@ -153,7 +159,7 @@ async function main() {
|
|
|
153
159
|
|
|
154
160
|
const titleBox = boxen(
|
|
155
161
|
logo + '\n\n' +
|
|
156
|
-
' 🤖 Robot 项目脚手架工具 v1.0.
|
|
162
|
+
' 🤖 Robot 项目脚手架工具 v1.0.4\n' +
|
|
157
163
|
' 兼容 npm/yarn/pnpm/bun',
|
|
158
164
|
{
|
|
159
165
|
padding: { top: 1, bottom: 1, left: 2, right: 2 },
|
|
@@ -227,7 +233,7 @@ async function main() {
|
|
|
227
233
|
program
|
|
228
234
|
.name('robot')
|
|
229
235
|
.description('🤖 Robot 项目脚手架工具 - @agile-team/robot-cli')
|
|
230
|
-
.version('1.0.
|
|
236
|
+
.version('1.0.4')
|
|
231
237
|
.hook('preAction', () => {
|
|
232
238
|
showWelcome();
|
|
233
239
|
});
|
package/lib/create.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// lib/create.js
|
|
1
|
+
// lib/create.js - 增强版本,改进进度显示
|
|
2
2
|
import fs from "fs-extra";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import chalk from "chalk";
|
|
@@ -104,7 +104,7 @@ function generateDefaultProjectName(template) {
|
|
|
104
104
|
const timestamp = Date.now().toString().slice(-4);
|
|
105
105
|
|
|
106
106
|
// 移除版本后缀 (-full, -lite)
|
|
107
|
-
const baseName = templateKey.replace(/-(full|lite)$/, "");
|
|
107
|
+
const baseName = templateKey.replace(/-(full|lite|base)$/, "");
|
|
108
108
|
|
|
109
109
|
return `my-${baseName}-${timestamp}`;
|
|
110
110
|
}
|
|
@@ -671,7 +671,7 @@ async function confirmCreation(projectName, template, projectConfig) {
|
|
|
671
671
|
}
|
|
672
672
|
|
|
673
673
|
/**
|
|
674
|
-
* 执行创建流程
|
|
674
|
+
* 执行创建流程 - 增强版本,改进进度显示
|
|
675
675
|
*/
|
|
676
676
|
async function executeCreation(projectName, template, projectConfig) {
|
|
677
677
|
// 调试信息 - 确保参数正确
|
|
@@ -683,13 +683,20 @@ async function executeCreation(projectName, template, projectConfig) {
|
|
|
683
683
|
throw new Error(`模板数据无效: ${JSON.stringify(template)}`);
|
|
684
684
|
}
|
|
685
685
|
|
|
686
|
-
|
|
686
|
+
// 使用更详细的 spinner 配置
|
|
687
|
+
const spinner = ora({
|
|
688
|
+
text: "🚀 准备创建项目...",
|
|
689
|
+
spinner: 'dots',
|
|
690
|
+
color: 'cyan'
|
|
691
|
+
}).start();
|
|
687
692
|
|
|
688
693
|
try {
|
|
689
694
|
// 1. 检查目录是否存在
|
|
695
|
+
spinner.text = "📁 检查项目目录...";
|
|
690
696
|
const projectPath = path.resolve(projectName);
|
|
691
697
|
if (fs.existsSync(projectPath)) {
|
|
692
|
-
spinner.
|
|
698
|
+
spinner.stop();
|
|
699
|
+
console.log(chalk.yellow("⚠️ 项目目录已存在"));
|
|
693
700
|
|
|
694
701
|
const { overwrite } = await inquirer.prompt([
|
|
695
702
|
{
|
|
@@ -705,18 +712,19 @@ async function executeCreation(projectName, template, projectConfig) {
|
|
|
705
712
|
process.exit(0);
|
|
706
713
|
}
|
|
707
714
|
|
|
715
|
+
spinner.start("🗑️ 清理现有目录...");
|
|
708
716
|
await fs.remove(projectPath);
|
|
709
|
-
spinner.
|
|
717
|
+
spinner.text = "📁 准备创建新目录...";
|
|
710
718
|
}
|
|
711
719
|
|
|
712
|
-
// 2. 下载模板
|
|
713
|
-
spinner.text = "
|
|
720
|
+
// 2. 下载模板 - download.js 会自动更新 spinner 状态
|
|
721
|
+
spinner.text = "🌐 开始下载模板...";
|
|
714
722
|
let templatePath;
|
|
715
723
|
|
|
716
724
|
try {
|
|
717
725
|
templatePath = await downloadTemplate(template, {
|
|
718
726
|
useCache: projectConfig.useCache,
|
|
719
|
-
spinner,
|
|
727
|
+
spinner, // 传递 spinner,让 download 函数更新进度
|
|
720
728
|
});
|
|
721
729
|
|
|
722
730
|
// 验证模板路径
|
|
@@ -737,16 +745,17 @@ async function executeCreation(projectName, template, projectConfig) {
|
|
|
737
745
|
console.log(chalk.dim(" 1. 检查网络连接"));
|
|
738
746
|
console.log(chalk.dim(" 2. 使用 robot cache --clear 清除缓存"));
|
|
739
747
|
console.log(chalk.dim(" 3. 重试: robot create --no-cache"));
|
|
748
|
+
console.log(chalk.dim(" 4. 检查仓库地址是否正确"));
|
|
740
749
|
console.log();
|
|
741
750
|
throw error;
|
|
742
751
|
}
|
|
743
752
|
|
|
744
753
|
// 3. 复制模板文件
|
|
745
|
-
spinner.text = "复制项目文件...";
|
|
754
|
+
spinner.text = "📋 复制项目文件...";
|
|
746
755
|
await copyTemplate(templatePath, projectPath);
|
|
747
756
|
|
|
748
757
|
// 4. 处理项目配置
|
|
749
|
-
spinner.text = "处理项目配置...";
|
|
758
|
+
spinner.text = "⚙️ 处理项目配置...";
|
|
750
759
|
await processProjectConfig(
|
|
751
760
|
projectPath,
|
|
752
761
|
projectName,
|
|
@@ -756,13 +765,13 @@ async function executeCreation(projectName, template, projectConfig) {
|
|
|
756
765
|
|
|
757
766
|
// 5. 初始化Git仓库
|
|
758
767
|
if (projectConfig.initGit) {
|
|
759
|
-
spinner.text = "初始化 Git 仓库...";
|
|
768
|
+
spinner.text = "📝 初始化 Git 仓库...";
|
|
760
769
|
await initializeGitRepository(projectPath);
|
|
761
770
|
}
|
|
762
771
|
|
|
763
772
|
// 6. 安装依赖
|
|
764
773
|
if (projectConfig.installDeps) {
|
|
765
|
-
spinner.text =
|
|
774
|
+
spinner.text = `📦 使用 ${projectConfig.packageManager} 安装依赖...`;
|
|
766
775
|
await installDependencies(
|
|
767
776
|
projectPath,
|
|
768
777
|
spinner,
|
|
@@ -771,7 +780,7 @@ async function executeCreation(projectName, template, projectConfig) {
|
|
|
771
780
|
}
|
|
772
781
|
|
|
773
782
|
// 7. 创建成功
|
|
774
|
-
spinner.succeed("项目创建成功!");
|
|
783
|
+
spinner.succeed(chalk.green("🎉 项目创建成功!"));
|
|
775
784
|
|
|
776
785
|
console.log();
|
|
777
786
|
console.log(chalk.green("🎉 项目创建完成!"));
|
|
@@ -808,7 +817,10 @@ async function executeCreation(projectName, template, projectConfig) {
|
|
|
808
817
|
console.log();
|
|
809
818
|
|
|
810
819
|
// 显示项目统计
|
|
820
|
+
spinner.start("📊 统计项目信息...");
|
|
811
821
|
const stats = await generateProjectStats(projectPath);
|
|
822
|
+
spinner.stop();
|
|
823
|
+
|
|
812
824
|
if (stats) {
|
|
813
825
|
printProjectStats(stats);
|
|
814
826
|
console.log();
|
|
@@ -943,4 +955,4 @@ function getStartCommand(template) {
|
|
|
943
955
|
};
|
|
944
956
|
|
|
945
957
|
return startCommands[template.key] || "bun run dev";
|
|
946
|
-
}
|
|
958
|
+
}
|
package/lib/download.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// lib/download.js -
|
|
1
|
+
// lib/download.js - 增强版本,添加详细进度显示
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import os from 'os';
|
|
@@ -44,6 +44,9 @@ export async function downloadTemplate(template, options = {}) {
|
|
|
44
44
|
// 验证缓存完整性
|
|
45
45
|
const packageJsonPath = path.join(cachePath, 'package.json');
|
|
46
46
|
if (fs.existsSync(packageJsonPath)) {
|
|
47
|
+
if (spinner) {
|
|
48
|
+
spinner.text = '✅ 缓存模板验证通过';
|
|
49
|
+
}
|
|
47
50
|
return cachePath;
|
|
48
51
|
} else {
|
|
49
52
|
// 缓存损坏,删除重新下载
|
|
@@ -79,7 +82,7 @@ export async function downloadTemplate(template, options = {}) {
|
|
|
79
82
|
|
|
80
83
|
// 下载远程模板
|
|
81
84
|
if (spinner) {
|
|
82
|
-
spinner.text =
|
|
85
|
+
spinner.text = `🌐 连接到 GitHub (${template.repo})...`;
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
try {
|
|
@@ -87,25 +90,34 @@ export async function downloadTemplate(template, options = {}) {
|
|
|
87
90
|
const tempZipPath = path.join(os.tmpdir(), cacheKey + '-' + Date.now() + '.zip');
|
|
88
91
|
const tempExtractPath = path.join(os.tmpdir(), cacheKey + '-extract-' + Date.now());
|
|
89
92
|
|
|
90
|
-
//
|
|
93
|
+
// 检查网络连接和获取文件信息
|
|
91
94
|
if (spinner) {
|
|
92
|
-
spinner.text =
|
|
95
|
+
spinner.text = `🔗 检查仓库 ${template.repo} 可用性...`;
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
// 下载zip文件
|
|
96
99
|
const response = await fetch(downloadUrl, {
|
|
97
|
-
timeout:
|
|
100
|
+
timeout: 60000, // 60秒超时,给大文件更多时间
|
|
98
101
|
headers: {
|
|
99
|
-
'User-Agent': 'Robot-CLI/1.0.
|
|
102
|
+
'User-Agent': 'Robot-CLI/1.0.4'
|
|
100
103
|
}
|
|
101
104
|
});
|
|
102
105
|
|
|
103
106
|
if (!response.ok) {
|
|
107
|
+
if (response.status === 404) {
|
|
108
|
+
throw new Error(`仓库不存在或私有: ${template.repo}\n请检查仓库地址是否正确且为公开仓库`);
|
|
109
|
+
}
|
|
104
110
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
105
111
|
}
|
|
106
112
|
|
|
107
|
-
|
|
108
|
-
|
|
113
|
+
// 显示下载进度
|
|
114
|
+
const contentLength = response.headers.get('content-length');
|
|
115
|
+
if (spinner && contentLength) {
|
|
116
|
+
const totalSize = parseInt(contentLength);
|
|
117
|
+
const sizeInMB = (totalSize / 1024 / 1024).toFixed(1);
|
|
118
|
+
spinner.text = `📦 下载模板中... (${sizeInMB}MB)`;
|
|
119
|
+
} else if (spinner) {
|
|
120
|
+
spinner.text = '📦 下载模板中...';
|
|
109
121
|
}
|
|
110
122
|
|
|
111
123
|
const buffer = await response.buffer();
|
|
@@ -118,10 +130,14 @@ export async function downloadTemplate(template, options = {}) {
|
|
|
118
130
|
// 解压文件
|
|
119
131
|
await extract(tempZipPath, { dir: tempExtractPath });
|
|
120
132
|
|
|
133
|
+
if (spinner) {
|
|
134
|
+
spinner.text = '🔍 查找项目结构...';
|
|
135
|
+
}
|
|
136
|
+
|
|
121
137
|
// 找到解压后的项目目录 (通常是 reponame-main/)
|
|
122
138
|
const extractedItems = await fs.readdir(tempExtractPath);
|
|
123
139
|
const projectDir = extractedItems.find(item =>
|
|
124
|
-
item.endsWith('-main') || item.endsWith('-master')
|
|
140
|
+
item.endsWith('-main') || item.endsWith('-master') || item === template.repo.split('/')[1]
|
|
125
141
|
);
|
|
126
142
|
|
|
127
143
|
if (!projectDir) {
|
|
@@ -130,10 +146,28 @@ export async function downloadTemplate(template, options = {}) {
|
|
|
130
146
|
|
|
131
147
|
const sourcePath = path.join(tempExtractPath, projectDir);
|
|
132
148
|
|
|
149
|
+
if (spinner) {
|
|
150
|
+
spinner.text = '✅ 验证模板完整性...';
|
|
151
|
+
}
|
|
152
|
+
|
|
133
153
|
// 验证模板完整性
|
|
134
154
|
const packageJsonPath = path.join(sourcePath, 'package.json');
|
|
135
155
|
if (!fs.existsSync(packageJsonPath)) {
|
|
136
|
-
throw new Error(`模板缺少 package.json
|
|
156
|
+
throw new Error(`模板缺少 package.json 文件,这可能不是一个有效的项目模板`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 读取并验证 package.json
|
|
160
|
+
try {
|
|
161
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
162
|
+
if (!packageJson.name) {
|
|
163
|
+
console.log(chalk.yellow('⚠️ 模板 package.json 缺少 name 字段'));
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
throw new Error(`package.json 格式错误: ${error.message}`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (spinner) {
|
|
170
|
+
spinner.text = '💾 保存到缓存...';
|
|
137
171
|
}
|
|
138
172
|
|
|
139
173
|
// 确保缓存目录存在
|
|
@@ -149,6 +183,10 @@ export async function downloadTemplate(template, options = {}) {
|
|
|
149
183
|
await fs.remove(tempZipPath).catch(() => {});
|
|
150
184
|
await fs.remove(tempExtractPath).catch(() => {});
|
|
151
185
|
|
|
186
|
+
if (spinner) {
|
|
187
|
+
spinner.text = '🎉 模板下载完成';
|
|
188
|
+
}
|
|
189
|
+
|
|
152
190
|
return cachePath;
|
|
153
191
|
|
|
154
192
|
} catch (error) {
|
package/lib/templates.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// lib/templates.js - 统一Robot
|
|
1
|
+
// lib/templates.js - 统一Robot命名风格,移除测试标记
|
|
2
2
|
export const TEMPLATE_CATEGORIES = {
|
|
3
3
|
frontend: {
|
|
4
4
|
name: '🎨 前端项目',
|
|
@@ -14,16 +14,14 @@ export const TEMPLATE_CATEGORIES = {
|
|
|
14
14
|
description: '包含30+完整示例、权限管理、图表组件、最佳实践等等',
|
|
15
15
|
repo: 'ChenyCHENYU/Robot_Admin',
|
|
16
16
|
features: ['Naive UI', 'Vue Router', 'Pinia', '权限管理', '动态路由', '图表组件', '性能优化等等'],
|
|
17
|
-
version: 'full'
|
|
18
|
-
localTest: true // 标记为测试模板
|
|
17
|
+
version: 'full'
|
|
19
18
|
},
|
|
20
19
|
'robot-admin-base': {
|
|
21
20
|
name: 'Robot Admin 精简版',
|
|
22
21
|
description: '基础架构、核心功能、快速启动',
|
|
23
22
|
repo: 'ChenyCHENYU/Robot_Admin_Base',
|
|
24
23
|
features: ['Naive UI', 'Vue Router', 'Pinia', '基础布局'],
|
|
25
|
-
version: 'base'
|
|
26
|
-
localTest: true // 标记为测试模板
|
|
24
|
+
version: 'base'
|
|
27
25
|
}
|
|
28
26
|
}
|
|
29
27
|
},
|