zhixuan-plugin-cli 1.0.1 → 1.0.2
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/README.md +0 -0
- package/bin/index.js +61 -21
- package/commands/create.js +13 -1
- package/commands/pull.js +165 -0
- package/commands/push.js +306 -0
- package/package.json +5 -2
- package/commands/config.js +0 -29
package/README.md
ADDED
|
File without changes
|
package/bin/index.js
CHANGED
|
@@ -6,41 +6,81 @@ const { version } = require('../package.json');
|
|
|
6
6
|
|
|
7
7
|
program
|
|
8
8
|
.version(version, '-v, --version')
|
|
9
|
+
.description('zhixuan插件项目脚手架CLI工具')
|
|
9
10
|
.usage('<command> [options]');
|
|
10
11
|
|
|
11
12
|
// 注册 create 命令
|
|
12
13
|
program
|
|
13
14
|
.command('create <project-name>')
|
|
14
|
-
.description('
|
|
15
|
-
.option('-t, --template <template>', '指定模板类型 (
|
|
15
|
+
.description('创建一个新项目')
|
|
16
|
+
.option('-t, --template <template>', '指定模板类型 (h5/vue2/vue3/react)')
|
|
17
|
+
.option('--pluginId <pluginId>', '插件ID')
|
|
16
18
|
.action((projectName, options) => {
|
|
17
19
|
require('../commands/create')(projectName, options);
|
|
18
20
|
});
|
|
19
21
|
|
|
20
|
-
// 注册
|
|
22
|
+
// 注册 pull 命令 - 支持versionId和token参数
|
|
21
23
|
program
|
|
22
|
-
.command('
|
|
23
|
-
.description('
|
|
24
|
-
.option('
|
|
25
|
-
.option('-
|
|
26
|
-
.option('
|
|
24
|
+
.command('pull')
|
|
25
|
+
.description('从智轩平台拉取插件')
|
|
26
|
+
.option('--versionId <versionId>', '版本ID')
|
|
27
|
+
.option('-t, --token <token>', '访问令牌')
|
|
28
|
+
.option('--server <server>', '服务器地址')
|
|
29
|
+
.option('-d, --dir <directory>', '下载到指定目录')
|
|
30
|
+
.option('-f, --force', '强制拉取,不询问确认')
|
|
27
31
|
.action((options) => {
|
|
28
|
-
require('../commands/
|
|
32
|
+
require('../commands/pull')(options);
|
|
29
33
|
});
|
|
30
34
|
|
|
31
|
-
//
|
|
35
|
+
// 注册 push 命令 - 支持pluginId、version和token参数
|
|
32
36
|
program
|
|
33
|
-
.
|
|
34
|
-
.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
.command('push')
|
|
38
|
+
.description('推送到智轩平台')
|
|
39
|
+
.option('--pluginId <pluginId>', '插件ID')
|
|
40
|
+
.option('-vNo, --versionNum <version>', '版本号')
|
|
41
|
+
.option('-vDesc, versionDescription <versionDescription>', '插件描述')
|
|
42
|
+
.option('-t, --token <token>', '访问令牌')
|
|
43
|
+
.option('--server <server>', '服务器地址', 'https://dev.zhixuan.cloud')
|
|
44
|
+
.option('-f, --force', '强制推送,不询问确认')
|
|
45
|
+
.action((options) => {
|
|
46
|
+
require('../commands/push')(options);
|
|
39
47
|
});
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
// 设置帮助信息
|
|
50
|
+
program.on('--help', () => {
|
|
51
|
+
console.log();
|
|
52
|
+
console.log(chalk.cyan(' 示例:'));
|
|
53
|
+
console.log();
|
|
54
|
+
console.log(chalk.cyan(' # 创建新项目'));
|
|
55
|
+
console.log(chalk.white(' $ zx-cli create my-project'));
|
|
56
|
+
console.log();
|
|
57
|
+
console.log(chalk.cyan(' # 使用特定模板创建项目'));
|
|
58
|
+
console.log(chalk.white(' $ zx-cli create my-project -t vue2'));
|
|
59
|
+
console.log();
|
|
60
|
+
console.log(chalk.cyan(' # 拉取插件(使用版本ID和token)'));
|
|
61
|
+
console.log(chalk.white(' $ zx-cli pull --versionId <version-id> --token <token>'));
|
|
62
|
+
console.log();
|
|
63
|
+
console.log(chalk.cyan(' # 拉取插件到指定目录'));
|
|
64
|
+
console.log(chalk.white(' $ zx-cli pull --versionId <version-id> --token <token> --dir ./my-plugins'));
|
|
65
|
+
console.log();
|
|
66
|
+
console.log(chalk.cyan(' # 推送插件(使用插件ID、插件版本、token)'));
|
|
67
|
+
console.log(chalk.white(' $ zx-cli push --pluginId <plugin-id> --version <version> --token <token>'));
|
|
68
|
+
console.log();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// 处理未知命令或缺少参数的情况
|
|
72
|
+
program
|
|
73
|
+
.arguments('[command]')
|
|
74
|
+
.action((cmd) => {
|
|
75
|
+
if (!cmd) {
|
|
76
|
+
// 没有提供命令参数,显示帮助
|
|
77
|
+
program.outputHelp();
|
|
78
|
+
} else {
|
|
79
|
+
// 提供了未知命令
|
|
80
|
+
console.log(chalk.red(`\n 未知命令 ${chalk.yellow(cmd)}`));
|
|
81
|
+
console.log();
|
|
82
|
+
program.outputHelp();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
42
85
|
|
|
43
|
-
|
|
44
|
-
if (!process.argv.slice(2).length) {
|
|
45
|
-
program.outputHelp();
|
|
46
|
-
}
|
|
86
|
+
program.parse(process.argv);
|
package/commands/create.js
CHANGED
|
@@ -36,6 +36,7 @@ module.exports = async function create(projectName, options) {
|
|
|
36
36
|
|
|
37
37
|
// 选择模板
|
|
38
38
|
let templateType = options.template;
|
|
39
|
+
const pluginId = options.pluginId;
|
|
39
40
|
if (!templateType) {
|
|
40
41
|
const answers = await inquirer.prompt([
|
|
41
42
|
{
|
|
@@ -43,8 +44,10 @@ module.exports = async function create(projectName, options) {
|
|
|
43
44
|
type: 'list',
|
|
44
45
|
message: '请选择项目模板:',
|
|
45
46
|
choices: [
|
|
46
|
-
{ name: '标准模板 (h5)', value: '
|
|
47
|
+
{ name: '标准模板 (h5)', value: 'javaScript' },
|
|
47
48
|
{ name: 'vue模板 (Vue2)', value: 'vue2' },
|
|
49
|
+
{ name: 'vue模板 (Vue3)', value: 'vue3' },
|
|
50
|
+
{ name: 'react模板 (react)', value: 'react' },
|
|
48
51
|
]
|
|
49
52
|
}
|
|
50
53
|
]);
|
|
@@ -54,6 +57,15 @@ module.exports = async function create(projectName, options) {
|
|
|
54
57
|
try {
|
|
55
58
|
// 下载模板
|
|
56
59
|
await downloadTemplate(projectName, templateType);
|
|
60
|
+
|
|
61
|
+
// 修改package.json中的项目名称
|
|
62
|
+
const packageJsonPath = path.join(targetDir, 'package.json');
|
|
63
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
64
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
65
|
+
packageJson.name = projectName;
|
|
66
|
+
packageJson.pluginId = pluginId;
|
|
67
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
68
|
+
}
|
|
57
69
|
|
|
58
70
|
console.log();
|
|
59
71
|
console.log(chalk.green('✅ 项目创建成功!'));
|
package/commands/pull.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const inquirer = require('inquirer');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const Conf = require('conf');
|
|
6
|
+
const ora = require('ora');
|
|
7
|
+
|
|
8
|
+
module.exports = async function pull(options) {
|
|
9
|
+
console.log();
|
|
10
|
+
console.log(chalk.cyan(`🔄 正在拉取插件`));
|
|
11
|
+
|
|
12
|
+
// 使用 Conf 管理配置
|
|
13
|
+
const config = new Conf({
|
|
14
|
+
configName: 'package',
|
|
15
|
+
fileExtension: 'json',
|
|
16
|
+
cwd: process.cwd(),
|
|
17
|
+
defaults: {
|
|
18
|
+
name: 'plugin',
|
|
19
|
+
version: '1.0.0',
|
|
20
|
+
pluginId: '',
|
|
21
|
+
versionDescription: '描述'
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// 获取服务器地址(优先使用命令行参数)
|
|
27
|
+
const serverUrl = options.server || config.get('settings.serverUrl');
|
|
28
|
+
|
|
29
|
+
// 获取token(优先使用命令行参数)
|
|
30
|
+
let token = options.token;
|
|
31
|
+
if (!token) {
|
|
32
|
+
// 如果命令行没有提供token,尝试从配置中获取
|
|
33
|
+
const auth = config.get('auth');
|
|
34
|
+
if (auth.token) {
|
|
35
|
+
token = auth.token;
|
|
36
|
+
} else {
|
|
37
|
+
console.log(chalk.red('❌ 未提供访问令牌'));
|
|
38
|
+
console.log(chalk.cyan('请使用 --token 参数提供访问令牌'));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 获取版本ID
|
|
44
|
+
const versionId = options.versionId;
|
|
45
|
+
|
|
46
|
+
if (!versionId) {
|
|
47
|
+
console.log(chalk.red('❌ 未提供版本ID'));
|
|
48
|
+
console.log(chalk.cyan('请使用 --versionId 参数提供版本ID'));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 确定下载目录
|
|
53
|
+
let targetDir;
|
|
54
|
+
if (options.dir) {
|
|
55
|
+
targetDir = path.resolve(options.dir);
|
|
56
|
+
} else {
|
|
57
|
+
// 默认使用版本ID作为目录名的一部分
|
|
58
|
+
targetDir = path.join(process.cwd(), `version-${versionId}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 检查目录是否已存在
|
|
62
|
+
if (await fs.pathExists(targetDir)) {
|
|
63
|
+
if (!options.force) {
|
|
64
|
+
const { action } = await inquirer.prompt([
|
|
65
|
+
{
|
|
66
|
+
name: 'action',
|
|
67
|
+
type: 'list',
|
|
68
|
+
message: `目标目录 ${chalk.cyan(targetDir)} 已存在,请选择操作:`,
|
|
69
|
+
choices: [
|
|
70
|
+
{ name: '覆盖', value: 'overwrite' },
|
|
71
|
+
{ name: '合并', value: 'merge' },
|
|
72
|
+
{ name: '取消', value: false }
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
if (!action) {
|
|
78
|
+
console.log(chalk.yellow('操作已取消'));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (action === 'overwrite') {
|
|
83
|
+
console.log(chalk.yellow(`\n移除目录 ${chalk.cyan(targetDir)} ...`));
|
|
84
|
+
await fs.remove(targetDir);
|
|
85
|
+
} else if (action === 'merge') {
|
|
86
|
+
console.log(chalk.yellow('将合并到现有目录中'));
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
console.log(chalk.yellow(`\n强制覆盖目录 ${chalk.cyan(targetDir)} ...`));
|
|
90
|
+
await fs.remove(targetDir);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 显示加载动画
|
|
95
|
+
const spinner = ora({
|
|
96
|
+
text: chalk.yellow('正在下载插件...'),
|
|
97
|
+
spinner: 'clock'
|
|
98
|
+
});
|
|
99
|
+
spinner.start();
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
// 构建下载API端点 - 使用版本ID
|
|
103
|
+
const downloadUrl = `${serverUrl.replace(/\/$/, '')}/api/plugins/versions/${versionId}/download`;
|
|
104
|
+
|
|
105
|
+
// 下载插件
|
|
106
|
+
await downloadPlugin(downloadUrl, token, targetDir, versionId);
|
|
107
|
+
|
|
108
|
+
spinner.succeed(chalk.green('插件下载成功!'));
|
|
109
|
+
|
|
110
|
+
console.log();
|
|
111
|
+
console.log(chalk.green('✅ 插件拉取成功!'));
|
|
112
|
+
console.log();
|
|
113
|
+
console.log(chalk.cyan(`版本ID: ${versionId}`));
|
|
114
|
+
console.log(chalk.cyan('插件已下载到: ' + targetDir));
|
|
115
|
+
console.log();
|
|
116
|
+
} catch (error) {
|
|
117
|
+
spinner.fail(chalk.red('下载失败'));
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
} catch (err) {
|
|
122
|
+
console.log(chalk.red('❌ 插件拉取失败'));
|
|
123
|
+
console.log(chalk.red(err.message || err));
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// 下载插件的函数
|
|
128
|
+
async function downloadPlugin(downloadUrl, token, targetDir, versionId) {
|
|
129
|
+
// 模拟下载过程,实际应用中应替换为真实的下载逻辑
|
|
130
|
+
return new Promise(async (resolve, reject) => {
|
|
131
|
+
try {
|
|
132
|
+
// 模拟网络请求,实际应用中应使用真实的HTTP客户端
|
|
133
|
+
console.log(chalk.yellow(`正在从 ${downloadUrl} 下载版本 ${versionId}...`));
|
|
134
|
+
|
|
135
|
+
// 模拟等待时间
|
|
136
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
137
|
+
|
|
138
|
+
// 创建目标目录
|
|
139
|
+
await fs.ensureDir(targetDir);
|
|
140
|
+
|
|
141
|
+
// 模拟创建插件文件结构
|
|
142
|
+
const packageJson = {
|
|
143
|
+
name: `version-${versionId}`,
|
|
144
|
+
version: '1.0.0',
|
|
145
|
+
description: 'A Zhixuan plugin version',
|
|
146
|
+
main: 'index.js',
|
|
147
|
+
scripts: {
|
|
148
|
+
dev: 'zx-cli serve',
|
|
149
|
+
build: 'zx-cli build'
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// 写入文件
|
|
154
|
+
await fs.writeJson(path.join(targetDir, 'package.json'), packageJson, { spaces: 2 });
|
|
155
|
+
await fs.writeFile(path.join(targetDir, 'index.js'), '// Plugin entry point\nconsole.log("Plugin loaded");', 'utf8');
|
|
156
|
+
await fs.writeFile(path.join(targetDir, 'README.md'), `# Version ${versionId}\n\nThis is a Zhixuan plugin version.`, 'utf8');
|
|
157
|
+
await fs.ensureDir(path.join(targetDir, 'src'));
|
|
158
|
+
await fs.ensureDir(path.join(targetDir, 'public'));
|
|
159
|
+
|
|
160
|
+
resolve();
|
|
161
|
+
} catch (error) {
|
|
162
|
+
reject(error);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
package/commands/push.js
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const inquirer = require('inquirer');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const Conf = require('conf');
|
|
6
|
+
const ora = require('ora');
|
|
7
|
+
const archiver = require('archiver');
|
|
8
|
+
const { exec } = require('child_process');
|
|
9
|
+
|
|
10
|
+
module.exports = async function push(options) {
|
|
11
|
+
console.log(chalk.cyan(`📤 准备推送插件`));
|
|
12
|
+
|
|
13
|
+
// 使用 Conf 管理配置
|
|
14
|
+
const config = new Conf({
|
|
15
|
+
configName: 'package',
|
|
16
|
+
fileExtension: 'json',
|
|
17
|
+
cwd: process.cwd(),
|
|
18
|
+
defaults: {
|
|
19
|
+
name: 'plugin',
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
pluginId: '',
|
|
22
|
+
versionDescription: '描述'
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// 获取服务器地址(优先使用命令行参数)
|
|
28
|
+
const serverUrl = options.server;
|
|
29
|
+
|
|
30
|
+
// 获取token(优先使用命令行参数)
|
|
31
|
+
let token = options.token;
|
|
32
|
+
if (!token) {
|
|
33
|
+
// 如果命令行没有提供token,尝试从配置中获取
|
|
34
|
+
const auth = config.get('auth');
|
|
35
|
+
if (auth.token) {
|
|
36
|
+
token = auth.token;
|
|
37
|
+
} else {
|
|
38
|
+
console.log(chalk.red('❌ 未提供访问令牌'));
|
|
39
|
+
console.log(chalk.cyan('请使用 --token 参数提供访问令牌'));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 获取插件ID和版本
|
|
45
|
+
const pluginId = options.pluginId;
|
|
46
|
+
const version = options.versionNum;
|
|
47
|
+
const versionDescription = options.versionDescription || config.get('versionDescription');
|
|
48
|
+
|
|
49
|
+
if (!pluginId) {
|
|
50
|
+
console.log(chalk.red('❌ 未提供插件ID'));
|
|
51
|
+
console.log(chalk.cyan('请使用 --pluginId 参数提供插件ID'));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log(config.get('version'));
|
|
56
|
+
console.log(config.get('pluginId'));
|
|
57
|
+
|
|
58
|
+
if (!(config.get('version') === '1.0.0' && config.get('pluginId') === '')) {
|
|
59
|
+
if (pluginId !== config.get('pluginId')) {
|
|
60
|
+
console.log(chalk.red('❌ 插件ID不匹配'));
|
|
61
|
+
console.log(chalk.cyan('请检测pluginId是否匹配package.json中的插件ID'));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!version) {
|
|
67
|
+
console.log(chalk.red('❌ 未提供插件版本'));
|
|
68
|
+
console.log(chalk.cyan('请使用 --version 参数提供插件'));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.table([
|
|
73
|
+
{
|
|
74
|
+
'数据来源': '当前配置',
|
|
75
|
+
'插件名称': config.get('name'),
|
|
76
|
+
'插件id': pluginId,
|
|
77
|
+
'插件版本': config.get('version')
|
|
78
|
+
}, {
|
|
79
|
+
'数据来源': '推送配置',
|
|
80
|
+
'插件名称': config.get('name'),
|
|
81
|
+
'插件id': pluginId,
|
|
82
|
+
'插件版本': version
|
|
83
|
+
}
|
|
84
|
+
])
|
|
85
|
+
|
|
86
|
+
// 确认上传(除非使用了force选项)
|
|
87
|
+
if (!options.force) {
|
|
88
|
+
const { confirm } = await inquirer.prompt([
|
|
89
|
+
{
|
|
90
|
+
name: 'confirm',
|
|
91
|
+
type: 'confirm',
|
|
92
|
+
message: `确定要上传推送吗?`,
|
|
93
|
+
default: true
|
|
94
|
+
}
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
if (!confirm) {
|
|
98
|
+
console.log(chalk.yellow('操作已取消'));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 显示上传进度
|
|
104
|
+
const spinner = ora({
|
|
105
|
+
text: chalk.yellow('正在打包并上传插件...'),
|
|
106
|
+
spinner: 'clock'
|
|
107
|
+
});
|
|
108
|
+
spinner.start();
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
// 构建项目
|
|
113
|
+
spinner.text = chalk.yellow('正在构建项目...');
|
|
114
|
+
await runBuildCommand();
|
|
115
|
+
|
|
116
|
+
// 打包插件
|
|
117
|
+
spinner.text = chalk.yellow('正在建立压缩包...');
|
|
118
|
+
const pluginDir = process.cwd();
|
|
119
|
+
const zipBuffer = await packPlugin(pluginDir, `${pluginId}_${version}`);
|
|
120
|
+
|
|
121
|
+
spinner.text = chalk.yellow('正在上传插件...');
|
|
122
|
+
// 上传插件
|
|
123
|
+
await uploadPlugin(
|
|
124
|
+
`${serverUrl.replace(/\/$/, '')}/api/ViewPlugin/version/submit`,
|
|
125
|
+
token,
|
|
126
|
+
zipBuffer,
|
|
127
|
+
pluginId,
|
|
128
|
+
version,
|
|
129
|
+
versionDescription
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
spinner.succeed(chalk.green('✅ 插件推送成功!'));
|
|
133
|
+
|
|
134
|
+
// 修改package.json中的pluginId和version
|
|
135
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
136
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
137
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
138
|
+
packageJson.pluginId = pluginId; // 更新插件ID
|
|
139
|
+
packageJson.version = version; // 更新版本号
|
|
140
|
+
packPlugin.versionDescription = versionDescription; // 更新版本描述
|
|
141
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
142
|
+
console.log(chalk.green(`✅ package.json 已更新: pluginId="${pluginId}", version="${version}"`));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
} catch (error) {
|
|
146
|
+
spinner.fail(chalk.red('上传失败'));
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
} catch (err) {
|
|
151
|
+
console.log(chalk.red('❌ 插件推送失败'));
|
|
152
|
+
console.log(chalk.red(err.message || err));
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// 执行构建命令
|
|
157
|
+
function runBuildCommand() {
|
|
158
|
+
return new Promise((resolve, reject) => {
|
|
159
|
+
console.log(chalk.yellow('正在执行构建命令: npm run build'));
|
|
160
|
+
|
|
161
|
+
const buildProcess = exec('npm run build', {
|
|
162
|
+
cwd: process.cwd(),
|
|
163
|
+
maxBuffer: 1024 * 1024 * 10 // 设置最大缓冲区为10MB
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// 只记录关键信息,避免过多输出
|
|
167
|
+
buildProcess.stdout.on('data', (data) => {
|
|
168
|
+
// 可以选择性输出重要信息,例如进度或错误
|
|
169
|
+
if (data.toLowerCase().includes('error') || data.toLowerCase().includes('failed')) {
|
|
170
|
+
console.log(chalk.red(data.toString()));
|
|
171
|
+
}
|
|
172
|
+
// 如果需要完全静默,可以注释掉下面一行
|
|
173
|
+
// console.log(data.toString());
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
buildProcess.stderr.on('data', (data) => {
|
|
177
|
+
console.error(chalk.red(data.toString()));
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
buildProcess.on('close', (code) => {
|
|
181
|
+
if (code === 0) {
|
|
182
|
+
console.log(chalk.green('构建成功!'));
|
|
183
|
+
resolve();
|
|
184
|
+
} else {
|
|
185
|
+
console.error(chalk.red(`构建失败,退出码: ${code}`));
|
|
186
|
+
reject(new Error(`构建失败,退出码: ${code}`));
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 打包插件函数
|
|
193
|
+
async function packPlugin(pluginDir, versionId) {
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
const archive = archiver('zip', {
|
|
196
|
+
zlib: { level: 9 } // 设置压缩级别
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const output = [];
|
|
200
|
+
|
|
201
|
+
archive.on('error', (err) => {
|
|
202
|
+
reject(err);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
archive.on('data', (chunk) => {
|
|
206
|
+
output.push(chunk);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
archive.on('end', () => {
|
|
210
|
+
const buffer = Buffer.concat(output);
|
|
211
|
+
resolve(buffer);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// 递归添加目录内容,但排除node_modules
|
|
215
|
+
archive.directory(pluginDir, versionId, (entry) => {
|
|
216
|
+
// 排除node_modules目录
|
|
217
|
+
if (entry.name.includes('/node_modules/') || entry.name.startsWith('node_modules/')) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
// 排除压缩文件本身(如果存在)
|
|
221
|
+
if (entry.name.endsWith('.zip')) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
return entry;
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
archive.finalize();
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 上传插件函数
|
|
232
|
+
async function uploadPlugin(uploadUrl, token, packageFile, pluginId, version, versionDescription) {
|
|
233
|
+
// 实际的上传逻辑
|
|
234
|
+
return new Promise(async (resolve, reject) => {
|
|
235
|
+
try {
|
|
236
|
+
// 检查 packageFile 是否有效
|
|
237
|
+
if (!packageFile || packageFile.length === 0) {
|
|
238
|
+
throw new Error('packageFile is empty or invalid');
|
|
239
|
+
}
|
|
240
|
+
console.log(chalk.yellow(`文件大小: ${packageFile.length} bytes`));
|
|
241
|
+
console.log(chalk.yellow(`正在向 ${uploadUrl} 上传插件 ${pluginId}_${version} ...`));
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
// 构建带查询参数的 URL
|
|
245
|
+
const query = new URLSearchParams();
|
|
246
|
+
query.set('pluginId', pluginId);
|
|
247
|
+
query.set('version', version);
|
|
248
|
+
query.set('versionDescription', versionDescription);
|
|
249
|
+
|
|
250
|
+
const urlWithParams = `${uploadUrl}?${query.toString()}`;
|
|
251
|
+
|
|
252
|
+
// 创建FormData对象用于发送数据
|
|
253
|
+
const FormData = require('form-data');
|
|
254
|
+
const formData = new FormData();
|
|
255
|
+
formData.append('packageFile', packageFile, {
|
|
256
|
+
filename: `${pluginId}_${version}.zip`,
|
|
257
|
+
contentType: 'application/zip'
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// 使用 axios 发送请求
|
|
261
|
+
const axios = require('axios');
|
|
262
|
+
|
|
263
|
+
const response = await axios({
|
|
264
|
+
method: 'POST',
|
|
265
|
+
url: urlWithParams,
|
|
266
|
+
data: formData,
|
|
267
|
+
headers: {
|
|
268
|
+
...formData.getHeaders(), // 这会自动包含正确的 Content-Type
|
|
269
|
+
'Authorization': `Bearer ${token}`
|
|
270
|
+
},
|
|
271
|
+
maxBodyLength: 50 * 1024 * 1024, // 50MB 限制
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
if (response.status === 200) {
|
|
275
|
+
const result = response.data;
|
|
276
|
+
const { code } = result
|
|
277
|
+
if (code === 200) {
|
|
278
|
+
console.log(chalk.green('上传成功!'));
|
|
279
|
+
resolve({
|
|
280
|
+
success: true,
|
|
281
|
+
pluginId: pluginId,
|
|
282
|
+
version: version,
|
|
283
|
+
downloadUrl: result.downloadUrl || null
|
|
284
|
+
});
|
|
285
|
+
} else {
|
|
286
|
+
console.log();
|
|
287
|
+
console.error(result);
|
|
288
|
+
reject(new Error(`上传失败: ${result.msg}`));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
} else {
|
|
292
|
+
console.error(chalk.red(response));
|
|
293
|
+
reject(new Error(`上传失败: ${response.status} ${response.statusText}`));
|
|
294
|
+
}
|
|
295
|
+
} catch (error) {
|
|
296
|
+
if (error.response) {
|
|
297
|
+
console.error(chalk.red(`上传失败: ${error.response.status} ${error.response.statusText}`));
|
|
298
|
+
console.error(chalk.red(`错误详情: ${error.response.data}`));
|
|
299
|
+
reject(new Error(`上传失败: ${error.response.status} ${error.response.statusText}`));
|
|
300
|
+
} else {
|
|
301
|
+
console.error(chalk.red(`网络请求失败: ${error.message}`));
|
|
302
|
+
reject(error);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zhixuan-plugin-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "zhixuan插件项目脚手架CLI工具",
|
|
5
5
|
"main": "bin/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -21,10 +21,13 @@
|
|
|
21
21
|
"author": "shituanquan",
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
|
+
"archiver": "^7.0.1",
|
|
25
|
+
"axios": "^1.13.6",
|
|
24
26
|
"chalk": "^4.1.2",
|
|
25
27
|
"commander": "^9.4.1",
|
|
26
28
|
"conf": "^10.2.0",
|
|
27
29
|
"download-git-repo": "^3.0.2",
|
|
30
|
+
"form-data": "^4.0.5",
|
|
28
31
|
"fs-extra": "^10.1.0",
|
|
29
32
|
"inquirer": "^8.2.5",
|
|
30
33
|
"ora": "^5.4.1"
|
|
@@ -32,4 +35,4 @@
|
|
|
32
35
|
"engines": {
|
|
33
36
|
"node": ">=16.18.0"
|
|
34
37
|
}
|
|
35
|
-
}
|
|
38
|
+
}
|
package/commands/config.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const Conf = require('conf');
|
|
2
|
-
const chalk = require('chalk');
|
|
3
|
-
|
|
4
|
-
const config = new Conf({
|
|
5
|
-
projectName: 'zhixuan-plugin-cli'
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
module.exports = function (options) {
|
|
9
|
-
if (options.list) {
|
|
10
|
-
console.log(chalk.cyan('当前配置:'));
|
|
11
|
-
console.log(config.store);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (options.get) {
|
|
16
|
-
const value = config.get(options.get);
|
|
17
|
-
console.log(chalk.cyan(`${options.get}=${value}`));
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (options.set) {
|
|
22
|
-
const [key, value] = options.set;
|
|
23
|
-
config.set(key, value);
|
|
24
|
-
console.log(chalk.green(`设置 ${key}=${value} 成功`));
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
console.log(chalk.yellow('请指定操作参数'));
|
|
29
|
-
};
|