lzx-test-cli 1.0.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.
@@ -0,0 +1,5 @@
1
+ {
2
+ "recommendations": [
3
+ "github.copilot"
4
+ ]
5
+ }
package/bin/index.js ADDED
@@ -0,0 +1,274 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import fs from 'fs-extra';
5
+ import inquirer from 'inquirer';
6
+ import chalk from 'chalk';
7
+ // import ora from 'ora';
8
+ import compressing from 'compressing'; //支持- tar - gzip - tgz - zip
9
+ import path from 'path';
10
+ import { spawn } from 'child_process';
11
+ import fetch from 'node-fetch';
12
+ import cliProgress from 'cli-progress';
13
+ // import download from 'download-git-repo';
14
+ console.log(
15
+ chalk.green('开始创建项目!')
16
+ );
17
+
18
+ /**
19
+ *
20
+ * @description: 根据不同的压缩格式进行解压
21
+ * @returns
22
+ */
23
+ function unCompressByType(filePath, unzipTarget, fileExtname) {
24
+ switch (fileExtname) {
25
+ case '.zip':
26
+ return compressing.zip.uncompress(filePath, unzipTarget);
27
+ case '.tgz':
28
+ return compressing.tgz.uncompress(filePath, unzipTarget);
29
+ case '.tar':
30
+ return compressing.tar.uncompress(filePath, unzipTarget);
31
+ case '.gz':
32
+ return compressing.gzip.uncompress(filePath, unzipTarget);
33
+ default:
34
+ throw new Error(`不支持的压缩格式: ${fileExtname}`);
35
+ }
36
+ }
37
+
38
+ /**
39
+ *
40
+ * @description: 运行项目依赖安装命令
41
+ * @returns
42
+ */
43
+ function runCommand(cwd) {
44
+ const safeCwd = path.resolve(cwd, '../package');
45
+
46
+ if (!fs.existsSync(safeCwd)) {
47
+ throw new Error(`目录不存在: ${safeCwd}`);
48
+ }
49
+
50
+ return new Promise((resolve, reject) => {
51
+ const child = process.platform === 'win32'
52
+ ? spawn('cmd.exe', ['/c', 'pnpm', 'install'], {
53
+ cwd: safeCwd,
54
+ stdio: 'inherit'
55
+ })
56
+ : spawn('npm', ['install'], {
57
+ cwd: safeCwd,
58
+ stdio: 'inherit'
59
+ });
60
+ console.log(chalk.green("\n依赖安装中..."));
61
+ child.on('error', reject);
62
+ child.on('close', code => {
63
+ if (code !== 0) {
64
+ reject(new Error(`npm install exited with code ${code}`));
65
+ } else {
66
+ console.log(chalk.green("\n依赖安装完成!"));
67
+ resolve();
68
+ }
69
+ });
70
+ });
71
+ }
72
+
73
+ /**
74
+ * @param {string} url 下载链接
75
+ * @param {string} targetDir 目标文件夹
76
+ * @param {string} fileName 保存的文件名
77
+ * @description: 下载文件并显示进度条
78
+ */
79
+ export async function downloadFileToFolder(url, targetDir, fileName) {
80
+ // 目标文件夹确保存在
81
+ await fs.promises.mkdir(targetDir, { recursive: true });
82
+
83
+ const dest = path.join(targetDir, fileName);
84
+
85
+ const res = await fetch(url, {
86
+ headers: {
87
+ 'User-Agent': 'node.js',
88
+ 'Accept': 'application/octet-stream'
89
+ // 'Authorization': `token ${YOUR_TOKEN}`
90
+ },
91
+ redirect: 'follow', // 跟随重定向
92
+ });
93
+ if (!res.ok) throw new Error(`下载失败: ${res.status} ${res.statusText}`);
94
+
95
+ // 获取 content-length 用于进度条(如果服务器提供)
96
+ const total = +res.headers.get('content-length') || 0;
97
+ const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
98
+ if (total > 0) progressBar.start(total, 0);
99
+
100
+ const fileStream = fs.createWriteStream(dest);
101
+
102
+
103
+ let downloaded = 0;
104
+ // 监听 data 更新进度
105
+ res.body.on('data', chunk => {
106
+ downloaded += chunk.length;
107
+ if (total > 0) progressBar.update(downloaded);
108
+ });
109
+
110
+ await new Promise((resolve, reject) => {
111
+ // 只在 res.body 上 pipe 一次,不要先消费流
112
+ res.body.pipe(fileStream);
113
+ res.body.on('error', reject);
114
+ fileStream.on('finish', resolve);
115
+ });
116
+ return dest;
117
+ }
118
+
119
+ /**
120
+ * @description 解压并安装依赖
121
+ */
122
+ async function unCompressFileAndInstall(dir) {
123
+ try {
124
+ const entries = await fs.readdir(dir);
125
+
126
+ for (const name of entries) {
127
+ const filePath = path.join(dir, name);
128
+ const stat = await fs.stat(filePath); //返回一个 Stats 对象,这个对象包含了关于这个路径的详细信息
129
+ const CompressedType = ['.zip', '.tgz', '.tar', '.gz'];
130
+ const fileExtname = path.extname(name).toLowerCase();
131
+
132
+ if (stat.isFile() && CompressedType.includes(fileExtname)) {
133
+ const unzipTarget = path.dirname(filePath);
134
+ const unzipDir = path.join(dir, path.basename(name, fileExtname));
135
+ // 确保解压目标文件夹存在
136
+ await fs.ensureDir(unzipTarget);
137
+ await unCompressByType(filePath, unzipTarget, fileExtname); // 解压
138
+ await runCommand(unzipDir); // 运行 npm install
139
+ await fs.remove(filePath); // 解压后删除压缩文件
140
+ }
141
+ }
142
+ } catch (err) {
143
+ throw new Error('\n操作失败' + err);
144
+ }
145
+ }
146
+
147
+ function getFileNameFromUrl(urlStr) {
148
+ const u = new URL(urlStr); //使用new URL 可以自动处理 query、hash
149
+ const fileName = path.basename(u.pathname);
150
+ return fileName;
151
+ }
152
+
153
+ const main_proj_remote_url = 'http://10.14.121.116:8081/repository/npm-hosted/artery5-manager-platform/-/artery5-manager-platform-1.0.2.tgz';
154
+ const sub_proj_remote_url = 'http://10.14.121.116:8081/repository/npm-hosted/artery5-manager-platform/-/artery5-manager-platform-1.0.2.tgz';
155
+
156
+ const program = new Command();
157
+ program
158
+ .command('create')
159
+ .alias('init')
160
+ .description('创建一个项目')
161
+ // .argument('<project-name>', '项目名称')
162
+ // .option('-f, --force', '强制覆盖,如果文件存在则强制覆盖')
163
+ // .option('-l, --local-path <path>', '从本地下载模版创建')
164
+ .action(async () => {
165
+ try {
166
+ let { projectName } = await inquirer.prompt([
167
+ {
168
+ type: 'input',
169
+ name: 'projectName',
170
+ message: '请输入项目名称',
171
+ default: 'my-project',
172
+ },
173
+ ]);
174
+ let { projectType } = await inquirer.prompt([
175
+ {
176
+ type: 'select', //Inquirer(从 v13+ 开始)已经不再支持 type: 'list' 这个 prompt 类型了
177
+ name: 'projectType',
178
+ message: '请选择项目类型',
179
+ choices: [
180
+ { name: "vue2", value: "vue2" },
181
+ { name: "vue3", value: "vue3" }
182
+ ],
183
+ default: 'vue3',
184
+ },
185
+ ]);
186
+ let { downloadType } = await inquirer.prompt([
187
+ {
188
+ type: 'select',
189
+ name: 'downloadType',
190
+ message: '请选择模版下载方式',
191
+ choices: [
192
+ { name: "本地", value: "local" },
193
+ { name: "远程", value: "remote" }
194
+ ],
195
+ default: 'remote',
196
+ },
197
+ ]);
198
+ let { templateChosen } = await inquirer.prompt([
199
+ {
200
+ type: 'select',
201
+ name: 'templateChosen',
202
+ message: '请选择模板类型',
203
+ choices: [
204
+ // 'template01', 'template02'
205
+ { name: "template-main", value: "template-main" },
206
+ { name: "template-sub", value: "template-sub" }
207
+ ],
208
+ default: 'template-main',
209
+ // filter: (val) => {
210
+ // return val.name
211
+ // }
212
+ },
213
+ ]);
214
+ let { force } = await inquirer.prompt([
215
+ {
216
+ type: 'confirm',
217
+ name: 'force',
218
+ message: '是否强制覆盖',
219
+ },
220
+ ]);
221
+ // const syncTemplate = ora('同步模版中...')
222
+ // syncTemplate.start()
223
+ const cwd = process.cwd();
224
+ const targetDir = `${cwd}/${projectName}`;
225
+ if (fs.existsSync(targetDir)) {
226
+ if (force) {
227
+ fs.removeSync(targetDir); //删除项目下已有的
228
+ }
229
+ }
230
+ fs.mkdirsSync(targetDir);
231
+
232
+ const templatePath = path.resolve("", templateChosen)
233
+ if(downloadType === 'remote') {
234
+ const repoMap = {
235
+ 'template-main': main_proj_remote_url,
236
+ 'template-sub': sub_proj_remote_url
237
+ };
238
+ const repoUrl = repoMap[templateChosen];
239
+ if (repoUrl) {
240
+ const headers = {
241
+ 'User-Agent': 'node.js',
242
+ 'Accept': 'application/octet-stream',
243
+ }
244
+ const fileName = getFileNameFromUrl(repoUrl);
245
+ await downloadFileToFolder(repoUrl, targetDir, fileName, headers);
246
+ }
247
+ } else{
248
+ await fs.copySync(templatePath, targetDir)
249
+ }
250
+ console.log(chalk.green("\n同步模版成功,开始解压并安装依赖..."));
251
+ await unCompressFileAndInstall(targetDir);
252
+ // syncTemplate.succeed('同步模版成功')
253
+ console.log(
254
+ chalk.green(chalk.blue.underline.bold(projectName) + ' 项目创建成功!')
255
+ );
256
+ // 退出
257
+ process.stdin.pause();
258
+ process.exit(0); // 完成后正常退出
259
+ } catch (e) {
260
+ if (e && e.name === 'ExitPromptError') {
261
+ console.log('\n 已取消创建!');
262
+ process.exit(0);
263
+ }
264
+ process.exit(1); // 遇错退出
265
+ }
266
+ })
267
+
268
+ program.on('--help', function () {
269
+ console.log('\nExamples:')
270
+ console.log(`artery-cli create`)
271
+ })
272
+ program.parse();
273
+
274
+
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "lzx-test-cli",
3
+ "version": "1.0.0",
4
+ "bin": {
5
+ "lzx-test-cli": "bin/index.js"
6
+ },
7
+ "main": "bin/index.js",
8
+ "type": "module",
9
+ "scripts": {
10
+ "dev": "ts-node bin/index.ts",
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "keywords": [],
14
+ "author": "",
15
+ "license": "ISC",
16
+ "description": "",
17
+ "devDependencies": {
18
+ "@types/node": "^25.0.6",
19
+ "ts-node": "^10.9.2",
20
+ "typescript": "^5.9.3"
21
+ },
22
+ "dependencies": {
23
+ "@types/fs-extra": "^11.0.4",
24
+ "chalk": "^5.6.2",
25
+ "cli-progress": "^3.12.0",
26
+ "commander": "^14.0.2",
27
+ "compressing": "^2.0.0",
28
+ "download-git-repo": "^3.0.2",
29
+ "fs-extra": "^11.3.3",
30
+ "inquirer": "^13.2.0",
31
+ "node-fetch": "^3.3.2",
32
+ "ora": "^9.0.0"
33
+ }
34
+ }
Binary file
Binary file
package/tsconfig.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ // Visit https://aka.ms/tsconfig to read more about this file
3
+ "compilerOptions": {
4
+ // File Layout
5
+ // "rootDir": "./src",
6
+ "outDir": "./dist",
7
+ // Environment Settings
8
+ // See also https://aka.ms/tsconfig/module
9
+ "module": "commonjs",
10
+ "target": "esnext",
11
+ "types": [],
12
+ // For nodejs:
13
+ // "lib": ["esnext"],
14
+ // "types": ["node"],
15
+ // and npm install -D @types/node
16
+ // Other Outputs
17
+ "sourceMap": true,
18
+ "declaration": true,
19
+ "declarationMap": true,
20
+ // Stricter Typechecking Options
21
+ "noUncheckedIndexedAccess": true,
22
+ "exactOptionalPropertyTypes": true,
23
+ // Style Options
24
+ // "noImplicitReturns": true,
25
+ // "noImplicitOverride": true,
26
+ // "noUnusedLocals": true,
27
+ // "noUnusedParameters": true,
28
+ // "noFallthroughCasesInSwitch": true,
29
+ // "noPropertyAccessFromIndexSignature": true,
30
+ // Recommended Options
31
+ "strict": true,
32
+ "jsx": "react-jsx",
33
+ "verbatimModuleSyntax": true,
34
+ "isolatedModules": true,
35
+ "noUncheckedSideEffectImports": true,
36
+ "moduleDetection": "force",
37
+ "skipLibCheck": true,
38
+ },
39
+ "include": [
40
+ "/src/**/*"
41
+ ],
42
+ "exclude": [
43
+ "node_modules",
44
+ "dist"
45
+ ]
46
+ }