xgb-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.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # xgb-cli
2
+
3
+ A CLI tool to download and initialize projects from GitHub templates.
4
+
5
+ ## templates
6
+
7
+ - [react-components-menorepo-template](https://github.com/xgbbing/react-components-monorepo-template)
8
+ 基于 dumi + @ice/pkg 的 React Menorepo 组件库项目模板
9
+ - [react-components-repo-template](https://github.com/xgbbing/react-components-repo-template)
10
+ 基于 dumi 的 React 组件库项目模板
11
+ - [vue3-admin-repo-template](https://github.com/xgbbing/vue3-admin-repo-template)
12
+ 基于 Vue3 + TypeScript + Vite + Pinia + Element-Plus 的 后台管理系统项目模板
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install -g xgb-cli
18
+ ```
19
+
20
+ Or link it locally for development:
21
+
22
+ ```bash
23
+ npm link
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### Initialize a new project
29
+
30
+ ```bash
31
+ xgb-cli init
32
+ ```
33
+
34
+ This will prompt you for:
35
+ - Project name
36
+ - GitHub template
37
+
38
+ ### Use with options
39
+
40
+ ```bash
41
+ # Examples
42
+ xgb-cli init my-app
43
+ xgb-cli init -t react-components-menorepo-template
44
+ xgb-cli init my-app -t react-components-menorepo-template
45
+ ```
46
+
47
+ ## Features
48
+
49
+ - 🚀 Quick project initialization
50
+ - 📥 Download templates from GitHub
51
+ - 💬 Interactive prompts
52
+ - ✨ Clean and simple interface
53
+
54
+ ## License
55
+
56
+ MIT
package/bin/index.js ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const chalk = require('chalk');
5
+ const figlet = require('figlet');
6
+ const { version } = require('../package.json');
7
+ const { initProject } = require('../src/commands/init');
8
+
9
+ console.log(
10
+ chalk.cyan(
11
+ figlet.textSync('XGB CLI', { horizontalLayout: 'full' })
12
+ )
13
+ );
14
+
15
+ program
16
+ .name('xgb-cli')
17
+ .description('前端脚手架CLI工具')
18
+ .version(version, '-v, --version', '查看版本号');
19
+
20
+ // Init command
21
+ program
22
+ .command('init [project-name]')
23
+ .description('初始化一个新项目')
24
+ .option('-t, --template <template>', '指定模板')
25
+ .action((projectName, options) => {
26
+ initProject(projectName, options);
27
+ });
28
+
29
+ program.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "xgb-cli",
3
+ "version": "1.0.0",
4
+ "description": "前端脚手架CLI",
5
+ "bin": {
6
+ "xgb-cli": "./bin/index.js"
7
+ },
8
+ "scripts": {
9
+ "start": "node bin/index.js"
10
+ },
11
+ "keywords": [
12
+ "cli",
13
+ "github",
14
+ "template",
15
+ "init"
16
+ ],
17
+ "author": "xgb.xu@qq.com",
18
+ "license": "MIT",
19
+ "dependencies": {
20
+ "commander": "^11.1.0",
21
+ "inquirer": "^8.2.5",
22
+ "ora": "^5.4.1",
23
+ "chalk": "^4.1.2",
24
+ "download-git-repo": "^2.0.0",
25
+ "figlet": "^1.7.0",
26
+ "fs-extra": "^11.0.0"
27
+ },
28
+ "devDependencies": {}
29
+ }
@@ -0,0 +1,117 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+ const inquirer = require('inquirer');
4
+ const chalk = require('chalk');
5
+ const templates = require('../templates');
6
+ const { downloadTemplate } = require('../utils/download');
7
+ const logger = require('../utils/logger');
8
+
9
+ const ora = require('ora');
10
+
11
+ /**
12
+ * Initialize a new project from GitHub template
13
+ * @param {Object} options - Command options
14
+ * @param {string} options.projectName - projectName
15
+ * @param {string} options.template - GitHub template (format: owner/repo)
16
+ */
17
+ async function initProject(projectName, options) {
18
+ try {
19
+ const answers = await promptUser(projectName, options)
20
+ const { name, template } = answers;
21
+
22
+ const targetDir = path.join(process.cwd(), name);
23
+
24
+ if (fs.existsSync(targetDir)) {
25
+ const { overwrite } = await inquirer.prompt([
26
+ {
27
+ type: 'confirm',
28
+ name: 'overwrite',
29
+ message: chalk.yellow(`目录${name}已经存在,是否覆盖?`),
30
+ default: false
31
+ }
32
+ ]);
33
+
34
+ if (!overwrite) {
35
+ logger.warn('已取消创建')
36
+ process.exit(0);
37
+ }
38
+
39
+ await fs.remove(targetDir);
40
+ }
41
+
42
+ const selectTemplate = templates.find(t => t.value === template)
43
+
44
+ await downloadTemplate(selectTemplate.repo, targetDir);
45
+
46
+ await updatePackageJson(targetDir, name);
47
+
48
+ printSuccess(name);
49
+
50
+ } catch (err) {
51
+ logger.error(`创建项目失败: ${err.message}`)
52
+ process.exit(1);
53
+ }
54
+ }
55
+
56
+ async function promptUser(projectName, options) {
57
+ const questions = []
58
+
59
+ if (!projectName) {
60
+ questions.push({
61
+ type: 'input',
62
+ name: 'name',
63
+ message: '请输入项目名称:',
64
+ default: 'my-app',
65
+ validate(input) {
66
+ if (!input.trim()) return '项目名称不能为空';
67
+ if (!/^[a-zA-Z0-9-_]+$/.test(input)) return '项目名称只能包含字母、数字、下划线、中划线';
68
+ return true;
69
+ }
70
+ });
71
+ }
72
+
73
+ if (!options.template) {
74
+ questions.push({
75
+ type: 'list',
76
+ name: 'template',
77
+ message: '请选择项目模板:',
78
+ choices: templates.map(t => ({
79
+ name: `${t.name} - ${chalk.gray(t.description)}`,
80
+ value: t.value
81
+ })),
82
+ });
83
+ }
84
+
85
+ const answers = await inquirer.prompt(questions);
86
+
87
+ return {
88
+ name: projectName || answers.name,
89
+ template: options.template || answers.template
90
+ }
91
+ }
92
+
93
+ async function updatePackageJson(targetDir, projectName) {
94
+ const pkgPath = path.join(targetDir, 'package.json');
95
+ if (fs.existsSync(pkgPath)) {
96
+ const pkg = await fs.readJson(pkgPath);
97
+ pkg.name = projectName;
98
+ pkg.version = '0.1.0';
99
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
100
+ }
101
+ }
102
+
103
+ function printSuccess(name) {
104
+ console.log()
105
+ logger.success('✅ 项目初始化成功!')
106
+ console.log()
107
+ logger.info('👉 执行以下命令开始开发:')
108
+ console.log()
109
+ logger.warn(` cd ${name}`)
110
+ logger.warn(' pnpm install')
111
+ logger.warn(' pnpm start')
112
+ console.log()
113
+ }
114
+
115
+ module.exports = {
116
+ initProject
117
+ };
@@ -0,0 +1,23 @@
1
+ module.exports = [
2
+ {
3
+ name: 'react-components-menorepo-template',
4
+ description: '基于 dumi + @ice/pkg 的 React Menorepo 组件库项目模板',
5
+ value: 'react-components-menorepo-template',
6
+ repo: 'github:xgbbing/react-components-menorepo-template',
7
+ branch: 'main'
8
+ },
9
+ {
10
+ name: 'react-components-repo-template',
11
+ description: '基于 dumi 的 React 组件库项目模板',
12
+ value: 'react-components-repo-template',
13
+ repo: 'github:xgbbing/react-components-repo-template',
14
+ branch: 'main'
15
+ },
16
+ {
17
+ name: 'vue3-admin-repo-template',
18
+ description: '基于 Vue3 + TypeScript + Vite + Pinia + Element-Plus 的 后台管理系统项目模板',
19
+ value: 'vue3-admin-repo-template',
20
+ repo: 'github:xgbbing/vue3-admin-repo-template',
21
+ branch: 'main'
22
+ }
23
+ ]
@@ -0,0 +1,33 @@
1
+ const downloadGitRepo = require('download-git-repo');
2
+ const ora = require('ora');
3
+ const chalk = require('chalk');
4
+
5
+ /**
6
+ * Download template
7
+ * @param {string} repo - GitHub repo (github:user/repo#branch)
8
+ * @param {string} targetDir - Target directory
9
+ */
10
+ async function downloadTemplate(repo, targetDir) {
11
+ const spinner = ora({
12
+ text: `正在下载模板 ${chalk.cyan(repo)}...`,
13
+ color: 'cyan'
14
+ }).start();
15
+
16
+ return new Promise((resolve, reject) => {
17
+ downloadGitRepo(repo, targetDir,
18
+ {
19
+ clone: false
20
+ }, // false 用 download,true 用 git clone (私有仓库用 true)
21
+ (err) => {
22
+ if (err) {
23
+ spinner.fail('模板下载失败');
24
+ reject(err);
25
+ } else {
26
+ spinner.succeed('模板下载成功');
27
+ resolve();
28
+ }
29
+ });
30
+ });
31
+ }
32
+
33
+ module.exports = { downloadTemplate };
@@ -0,0 +1,40 @@
1
+ const { spawn } = require('child_process');
2
+ const ora = require('ora');
3
+ const chalk = require('chalk');
4
+
5
+ function installDeps(targetDir, packageManager = 'pnpm') {
6
+ const spinner = ora({
7
+ text: `正在使用 ${chalk.cyan(packageManager)} 安装依赖...`,
8
+ color: 'cyan'
9
+ }).start();
10
+
11
+ return new Promise((resolve, reject) => {
12
+ // windows 兼容
13
+ const isWin = process.platform === 'win32';
14
+ const cmd = isWin ? `${packageManager}.cmd` : packageManager;
15
+
16
+ const child = spawn(cmd, ['install'], {
17
+ cwd: targetDir, // 在项目目录下执行
18
+ stdio: 'inherit' // 继承父进程的输出
19
+ });
20
+
21
+ child.on('close', (code) => {
22
+ if (code !== 0) {
23
+ spinner.fail(chalk.red('依赖安装失败'));
24
+ reject(new Error(`${packageManager} install 失败,退出码: ${code}`));
25
+ } else {
26
+ spinner.succeed(chalk.green('依赖安装成功'));
27
+ resolve();
28
+ }
29
+ })
30
+
31
+ child.on('error', (err) => {
32
+ spinner.fail(chalk.red('依赖安装出错'));
33
+ reject(err);
34
+ });
35
+ })
36
+ }
37
+
38
+ module.exports = {
39
+ installDeps
40
+ }
@@ -0,0 +1,16 @@
1
+ const chalk = require('chalk');
2
+
3
+ module.exports = {
4
+ info: (...args) => {
5
+ console.log(chalk.cyan(...args));
6
+ },
7
+ success: (...args) => {
8
+ console.log(chalk.green(...args));
9
+ },
10
+ warn: (...args) => {
11
+ console.log(chalk.yellow(...args));
12
+ },
13
+ error: (...args) => {
14
+ console.log(chalk.red(...args));
15
+ },
16
+ };