@atooyu/uxto-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 +175 -0
- package/dist/commands/changelog.d.ts +1 -0
- package/dist/commands/changelog.js +19 -0
- package/dist/commands/create.d.ts +1 -0
- package/dist/commands/create.js +65 -0
- package/dist/commands/publish.d.ts +9 -0
- package/dist/commands/publish.js +140 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +56 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/generate.d.ts +3 -0
- package/dist/utils/generate.js +115 -0
- package/dist/utils/prompt.d.ts +3 -0
- package/dist/utils/prompt.js +44 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# UXTO CLI
|
|
2
|
+
|
|
3
|
+
UXTO UniApp 项目脚手架工具 - 支持 iOS、Android、鸿蒙多平台。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 全局安装
|
|
9
|
+
pnpm add -g @atooyu/uxto-cli
|
|
10
|
+
|
|
11
|
+
# 或使用 npm
|
|
12
|
+
npm install -g @atooyu/uxto-cli
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 命令
|
|
16
|
+
|
|
17
|
+
### 创建项目
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# 交互式创建
|
|
21
|
+
uxto create
|
|
22
|
+
|
|
23
|
+
# 直接指定项目名称
|
|
24
|
+
uxto create my-app
|
|
25
|
+
|
|
26
|
+
# 使用 init 别名
|
|
27
|
+
uxto init my-app
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 发布组件到 NPM
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# 发布组件
|
|
34
|
+
uxto publish
|
|
35
|
+
|
|
36
|
+
# 指定发布范围 (公开包)
|
|
37
|
+
uxto publish --access public
|
|
38
|
+
|
|
39
|
+
# 指定标签 (如 beta, next)
|
|
40
|
+
uxto publish --tag beta
|
|
41
|
+
|
|
42
|
+
# 指定 registry
|
|
43
|
+
uxto publish --registry https://registry.npmjs.org/
|
|
44
|
+
|
|
45
|
+
# 模拟发布 (不实际执行)
|
|
46
|
+
uxto publish --dry-run
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 登录 NPM
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
uxto login
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 查看版本
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
uxto -v
|
|
59
|
+
uxto --version
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 查看更新记录
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uxto changelog
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 生成的项目特性
|
|
69
|
+
|
|
70
|
+
- 基于 UniApp + Vue3 + TypeScript 构建
|
|
71
|
+
- 支持 iOS、Android、鸿蒙、H5、小程序多平台
|
|
72
|
+
- 集成 Pinia 状态管理
|
|
73
|
+
- 集成 UXTO 组件库 (`@atooyu/uxto-ui`)
|
|
74
|
+
- 内置 u-tabbar 底部导航组件
|
|
75
|
+
- 支持暗黑模式和灰色模式主题切换
|
|
76
|
+
|
|
77
|
+
## 开发命令
|
|
78
|
+
|
|
79
|
+
创建项目后:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pnpm install # 安装依赖
|
|
83
|
+
pnpm dev:h5 # 运行 H5
|
|
84
|
+
pnpm dev:app # 运行 App (iOS/Android)
|
|
85
|
+
pnpm dev:harmony # 运行鸿蒙
|
|
86
|
+
pnpm dev:mp-weixin # 运行微信小程序
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 项目结构
|
|
90
|
+
|
|
91
|
+
生成的项目结构:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
my-app/
|
|
95
|
+
├── src/
|
|
96
|
+
│ ├── pages/ # 页面
|
|
97
|
+
│ │ ├── index/ # 首页
|
|
98
|
+
│ │ ├── components/ # 组件示例
|
|
99
|
+
│ │ ├── api/ # API 示例
|
|
100
|
+
│ │ ├── store/ # 状态管理
|
|
101
|
+
│ │ ├── utils/ # 工具类
|
|
102
|
+
│ │ └── tabbar/ # 底部导航示例
|
|
103
|
+
│ ├── stores/ # Pinia Store
|
|
104
|
+
│ │ ├── counter.ts
|
|
105
|
+
│ │ ├── user.ts
|
|
106
|
+
│ │ ├── cart.ts
|
|
107
|
+
│ │ └── theme.ts # 主题状态 (暗黑/灰色模式)
|
|
108
|
+
│ ├── utils/ # 工具类
|
|
109
|
+
│ ├── assets/ # 静态资源
|
|
110
|
+
│ │ └── styles/ # 全局样式
|
|
111
|
+
│ ├── App.vue
|
|
112
|
+
│ ├── main.ts
|
|
113
|
+
│ └── pages.json
|
|
114
|
+
├── package.json
|
|
115
|
+
├── vite.config.js
|
|
116
|
+
└── tsconfig.json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## 使用 UXTO 组件库
|
|
120
|
+
|
|
121
|
+
生成的项目已集成 `@atooyu/uxto-ui` 组件库,可以直接使用:
|
|
122
|
+
|
|
123
|
+
```vue
|
|
124
|
+
<template>
|
|
125
|
+
<!-- 底部导航 -->
|
|
126
|
+
<u-tabbar
|
|
127
|
+
v-model="activeTab"
|
|
128
|
+
center-brand="U"
|
|
129
|
+
center-label="UXTO"
|
|
130
|
+
:left-tab="{ label: '首页', icon: '⌂', value: 'home' }"
|
|
131
|
+
:center-tab="{ label: 'UXTO', icon: 'U', value: 'uxto' }"
|
|
132
|
+
:right-tab="{ label: '我的', icon: '◉', value: 'mine' }"
|
|
133
|
+
@tab-change="onTabChange"
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
<!-- 开关 -->
|
|
137
|
+
<u-switch v-model="enabled" />
|
|
138
|
+
|
|
139
|
+
<!-- 其他组件... -->
|
|
140
|
+
</template>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## 发布组件流程
|
|
144
|
+
|
|
145
|
+
### 1. 登录 NPM
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
uxto login
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 2. 确保组件已构建
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# 在 uxto-ui 目录
|
|
155
|
+
npm run build:all
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 3. 发布
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
uxto publish --access public
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## 相关项目
|
|
165
|
+
|
|
166
|
+
- [uxto-ui](../uxto-ui) - UXTO 组件库
|
|
167
|
+
- [uxto-fronted](../uxto-fronted) - UXTO 前端脚手架模板 (CLI 模板来源)
|
|
168
|
+
- [template](../uxto-fronted/template) - 项目模板
|
|
169
|
+
- [example](../uxto-fronted/example) - 示例项目
|
|
170
|
+
|
|
171
|
+
> CLI 通过 `@atooyu/uxto-fronted` 包获取模板,无需本地 template 目录。
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function showChangelog(): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
const CHANGELOG = `
|
|
3
|
+
# UXTO CLI 更新记录
|
|
4
|
+
|
|
5
|
+
## v1.0.0 (2026-03-31)
|
|
6
|
+
|
|
7
|
+
### 新特性
|
|
8
|
+
- 初始版本发布
|
|
9
|
+
- 支持 iOS、Android、鸿蒙多平台
|
|
10
|
+
- 内置 uxto-ui 组件库集成
|
|
11
|
+
- 默认生成三个底部导航菜单(首页、UXTO、我的)
|
|
12
|
+
- 完善的项目结构和开发规范
|
|
13
|
+
`;
|
|
14
|
+
export function showChangelog() {
|
|
15
|
+
console.log();
|
|
16
|
+
console.log(chalk.cyan('UXTO CLI 更新记录'));
|
|
17
|
+
console.log();
|
|
18
|
+
console.log(CHANGELOG);
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createProject(projectName?: string): Promise<void>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { createRequire } from 'module';
|
|
5
|
+
import { promptProjectOptions, promptInstallDeps } from '../utils/prompt.js';
|
|
6
|
+
import { generateProject, installDependencies } from '../utils/generate.js';
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
// 获取模板目录 (从 @atooyu/uxto-fronted 包中获取)
|
|
9
|
+
function getTemplateDir() {
|
|
10
|
+
const uxtoFrontedPath = require.resolve('@atooyu/uxto-fronted/package.json');
|
|
11
|
+
return path.join(path.dirname(uxtoFrontedPath), 'template');
|
|
12
|
+
}
|
|
13
|
+
const TEMPLATE_DIR = getTemplateDir();
|
|
14
|
+
export async function createProject(projectName) {
|
|
15
|
+
console.log();
|
|
16
|
+
console.log(chalk.cyan('🚀 UXTO CLI - 创建 UniApp 项目'));
|
|
17
|
+
console.log(chalk.gray(' 支持 iOS、Android、鸿蒙多平台'));
|
|
18
|
+
console.log();
|
|
19
|
+
try {
|
|
20
|
+
// 1. 获取项目配置
|
|
21
|
+
const options = await promptProjectOptions(projectName);
|
|
22
|
+
const targetDir = path.resolve(process.cwd(), options.name);
|
|
23
|
+
// 检查目录是否已存在
|
|
24
|
+
if (await fs.pathExists(targetDir)) {
|
|
25
|
+
console.log(chalk.red(`✖ 目录 ${options.name} 已存在`));
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
// 2. 检查模板目录
|
|
29
|
+
if (!await fs.pathExists(TEMPLATE_DIR)) {
|
|
30
|
+
console.log(chalk.red('✖ 模板目录不存在'));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
console.log(chalk.gray(` 模板: uxto-starter`));
|
|
34
|
+
console.log();
|
|
35
|
+
// 3. 生成项目
|
|
36
|
+
await generateProject(TEMPLATE_DIR, targetDir, options);
|
|
37
|
+
// 4. 询问是否安装依赖
|
|
38
|
+
const shouldInstall = await promptInstallDeps();
|
|
39
|
+
if (shouldInstall) {
|
|
40
|
+
await installDependencies(targetDir);
|
|
41
|
+
}
|
|
42
|
+
// 5. 完成
|
|
43
|
+
console.log();
|
|
44
|
+
console.log(chalk.green('✔ 项目创建成功!'));
|
|
45
|
+
console.log();
|
|
46
|
+
console.log(' 下一步:');
|
|
47
|
+
console.log(chalk.cyan(` cd ${options.name}`));
|
|
48
|
+
if (!shouldInstall) {
|
|
49
|
+
console.log(chalk.cyan(' pnpm install'));
|
|
50
|
+
}
|
|
51
|
+
console.log(chalk.cyan(' pnpm dev:app # 开发 App (iOS/Android)'));
|
|
52
|
+
console.log(chalk.cyan(' pnpm dev:h5 # 开发 H5'));
|
|
53
|
+
console.log(chalk.cyan(' pnpm dev:harmony # 开发鸿蒙'));
|
|
54
|
+
console.log();
|
|
55
|
+
console.log(chalk.gray(' 提示:'));
|
|
56
|
+
console.log(chalk.gray(' - 在 src/pages.json 中添加页面'));
|
|
57
|
+
console.log(chalk.gray(' - 在 src/pages/ 中添加页面组件'));
|
|
58
|
+
console.log(chalk.gray(' - 在 src/static/tabbar/ 中添加导航图标'));
|
|
59
|
+
console.log();
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error(chalk.red('✖ 创建项目失败:'), error);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface PublishOptions {
|
|
2
|
+
registry?: string;
|
|
3
|
+
tag?: string;
|
|
4
|
+
access?: 'public' | 'restricted';
|
|
5
|
+
dryRun?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function publishComponent(options?: PublishOptions): Promise<void>;
|
|
8
|
+
export declare function loginNpm(): Promise<void>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import { exec } from 'child_process';
|
|
6
|
+
import { promisify } from 'util';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
export async function publishComponent(options = {}) {
|
|
10
|
+
console.log();
|
|
11
|
+
console.log(chalk.cyan('📦 UXTO CLI - 发布组件到 NPM'));
|
|
12
|
+
console.log();
|
|
13
|
+
try {
|
|
14
|
+
// 1. 检查是否在 uxto-ui 目录
|
|
15
|
+
const currentDir = process.cwd();
|
|
16
|
+
const pkgPath = path.join(currentDir, 'package.json');
|
|
17
|
+
if (!await fs.pathExists(pkgPath)) {
|
|
18
|
+
console.log(chalk.red('✖ 未找到 package.json,请在组件库目录下运行此命令'));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const pkg = await fs.readJson(pkgPath);
|
|
22
|
+
// 2. 显示当前包信息
|
|
23
|
+
console.log(chalk.gray(' 包名:'), chalk.white(pkg.name));
|
|
24
|
+
console.log(chalk.gray(' 版本:'), chalk.white(pkg.version));
|
|
25
|
+
console.log(chalk.gray(' 描述:'), chalk.white(pkg.description));
|
|
26
|
+
console.log();
|
|
27
|
+
// 3. 确认发布
|
|
28
|
+
const { confirm } = await inquirer.prompt([
|
|
29
|
+
{
|
|
30
|
+
type: 'confirm',
|
|
31
|
+
name: 'confirm',
|
|
32
|
+
message: '确认发布此组件?',
|
|
33
|
+
default: false,
|
|
34
|
+
},
|
|
35
|
+
]);
|
|
36
|
+
if (!confirm) {
|
|
37
|
+
console.log(chalk.yellow('已取消发布'));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// 4. 检查是否需要登录
|
|
41
|
+
const spinner = ora('检查 npm 登录状态...').start();
|
|
42
|
+
try {
|
|
43
|
+
await execAsync('npm whoami');
|
|
44
|
+
spinner.succeed('已登录 npm');
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
spinner.warn('未登录 npm,请先登录');
|
|
48
|
+
console.log();
|
|
49
|
+
console.log(chalk.cyan(' 运行 npm login 进行登录'));
|
|
50
|
+
console.log();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// 5. 构建组件
|
|
54
|
+
const buildSpinner = ora('构建组件...').start();
|
|
55
|
+
try {
|
|
56
|
+
// 检查是否有构建脚本
|
|
57
|
+
if (pkg.scripts?.['build:all']) {
|
|
58
|
+
await execAsync('npm run build:all', { cwd: currentDir });
|
|
59
|
+
}
|
|
60
|
+
else if (pkg.scripts?.build) {
|
|
61
|
+
await execAsync('npm run build', { cwd: currentDir });
|
|
62
|
+
}
|
|
63
|
+
buildSpinner.succeed('构建完成');
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
buildSpinner.fail('构建失败');
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
// 6. 发布到 npm
|
|
70
|
+
if (options.dryRun) {
|
|
71
|
+
console.log(chalk.yellow('\n [Dry Run] 跳过实际发布'));
|
|
72
|
+
console.log(chalk.gray(' 将执行的命令: npm publish'));
|
|
73
|
+
if (options.access) {
|
|
74
|
+
console.log(chalk.gray(` --access ${options.access}`));
|
|
75
|
+
}
|
|
76
|
+
if (options.tag) {
|
|
77
|
+
console.log(chalk.gray(` --tag ${options.tag}`));
|
|
78
|
+
}
|
|
79
|
+
if (options.registry) {
|
|
80
|
+
console.log(chalk.gray(` --registry ${options.registry}`));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const publishSpinner = ora('发布到 npm...').start();
|
|
85
|
+
let publishCmd = 'npm publish';
|
|
86
|
+
if (options.access) {
|
|
87
|
+
publishCmd += ` --access ${options.access}`;
|
|
88
|
+
}
|
|
89
|
+
if (options.tag) {
|
|
90
|
+
publishCmd += ` --tag ${options.tag}`;
|
|
91
|
+
}
|
|
92
|
+
if (options.registry) {
|
|
93
|
+
publishCmd += ` --registry ${options.registry}`;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
await execAsync(publishCmd, { cwd: currentDir });
|
|
97
|
+
publishSpinner.succeed('发布成功');
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
publishSpinner.fail('发布失败');
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// 7. 完成
|
|
105
|
+
console.log();
|
|
106
|
+
console.log(chalk.green('✔ 组件发布成功!'));
|
|
107
|
+
console.log();
|
|
108
|
+
console.log(chalk.gray(' 安装方式:'));
|
|
109
|
+
console.log(chalk.cyan(` npm install ${pkg.name}`));
|
|
110
|
+
console.log(chalk.cyan(` pnpm add ${pkg.name}`));
|
|
111
|
+
console.log(chalk.cyan(` yarn add ${pkg.name}`));
|
|
112
|
+
console.log();
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error(chalk.red('✖ 发布失败:'), error);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export async function loginNpm() {
|
|
120
|
+
console.log();
|
|
121
|
+
console.log(chalk.cyan('🔐 登录 NPM'));
|
|
122
|
+
console.log();
|
|
123
|
+
try {
|
|
124
|
+
// 使用 spawn 来保持交互式输入
|
|
125
|
+
const { spawn } = await import('child_process');
|
|
126
|
+
const child = spawn('npm', ['login'], { stdio: 'inherit' });
|
|
127
|
+
child.on('close', (code) => {
|
|
128
|
+
if (code === 0) {
|
|
129
|
+
console.log();
|
|
130
|
+
console.log(chalk.green('✔ 登录成功!'));
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
console.log(chalk.red('✖ 登录失败'));
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
console.error(chalk.red('✖ 登录失败:'), error);
|
|
139
|
+
}
|
|
140
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { createProject } from './commands/create.js';
|
|
5
|
+
import { publishComponent, loginNpm } from './commands/publish.js';
|
|
6
|
+
import { showChangelog } from './commands/changelog.js';
|
|
7
|
+
// 从 package.json 读取版本号
|
|
8
|
+
import { createRequire } from 'module';
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const pkg = require('../package.json');
|
|
11
|
+
const program = new Command();
|
|
12
|
+
program
|
|
13
|
+
.name('uxto-cli')
|
|
14
|
+
.description('UXTO UniApp 项目脚手架工具 - 支持 iOS、Android、鸿蒙')
|
|
15
|
+
.version(pkg.version, '-v, --version', '显示版本号');
|
|
16
|
+
program
|
|
17
|
+
.command('create [project-name]')
|
|
18
|
+
.description('创建一个新的 UXTO UniApp 项目')
|
|
19
|
+
.action(async (projectName) => {
|
|
20
|
+
await createProject(projectName);
|
|
21
|
+
});
|
|
22
|
+
program
|
|
23
|
+
.command('init [project-name]')
|
|
24
|
+
.description('创建一个新的 UXTO UniApp 项目 (create 的别名)')
|
|
25
|
+
.action(async (projectName) => {
|
|
26
|
+
await createProject(projectName);
|
|
27
|
+
});
|
|
28
|
+
program
|
|
29
|
+
.command('publish')
|
|
30
|
+
.description('发布组件到 NPM')
|
|
31
|
+
.option('-r, --registry <url>', '指定 npm registry')
|
|
32
|
+
.option('-t, --tag <tag>', '指定发布标签 (如 beta, next)')
|
|
33
|
+
.option('-a, --access <access>', '发布范围 (public 或 restricted)')
|
|
34
|
+
.option('--dry-run', '模拟发布,不实际执行')
|
|
35
|
+
.action(async (options) => {
|
|
36
|
+
await publishComponent(options);
|
|
37
|
+
});
|
|
38
|
+
program
|
|
39
|
+
.command('login')
|
|
40
|
+
.description('登录 NPM')
|
|
41
|
+
.action(async () => {
|
|
42
|
+
await loginNpm();
|
|
43
|
+
});
|
|
44
|
+
program
|
|
45
|
+
.command('changelog')
|
|
46
|
+
.description('查看版本更新记录')
|
|
47
|
+
.action(() => {
|
|
48
|
+
showChangelog();
|
|
49
|
+
});
|
|
50
|
+
// 处理未知命令
|
|
51
|
+
program.on('command:*', () => {
|
|
52
|
+
console.error(chalk.red('无效的命令: %s'), program.args.join(' '));
|
|
53
|
+
console.log(chalk.gray('使用 uxto-cli --help 查看可用命令'));
|
|
54
|
+
process.exit(1);
|
|
55
|
+
});
|
|
56
|
+
program.parse();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
// 需要排除的目录
|
|
5
|
+
const EXCLUDE_DIRS = ['node_modules', 'dist'];
|
|
6
|
+
// 需要从 package.json 中移除的字段
|
|
7
|
+
const REMOVE_PKG_FIELDS = [
|
|
8
|
+
'main',
|
|
9
|
+
'module',
|
|
10
|
+
'types',
|
|
11
|
+
'exports',
|
|
12
|
+
'files',
|
|
13
|
+
'keywords',
|
|
14
|
+
'repository',
|
|
15
|
+
];
|
|
16
|
+
// 需要替换模板变量的文件扩展名
|
|
17
|
+
const TEMPLATE_FILE_EXTENSIONS = ['.json', '.html', '.ts', '.js', '.vue', '.md', '.scss'];
|
|
18
|
+
/**
|
|
19
|
+
* 替换模板变量
|
|
20
|
+
*/
|
|
21
|
+
function replaceTemplateVariables(content, options) {
|
|
22
|
+
return content
|
|
23
|
+
.replace(/\{\{\s*name\s*\}\}/g, options.name)
|
|
24
|
+
.replace(/\{\{\s*description\s*\}\}/g, options.description || '')
|
|
25
|
+
.replace(/\{\{\s*author\s*\}\}/g, options.author || '')
|
|
26
|
+
.replace(/<%=\s*appName\s*%>/g, options.name);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 检查文件是否需要模板替换
|
|
30
|
+
*/
|
|
31
|
+
function isTemplateFile(filePath) {
|
|
32
|
+
const ext = path.extname(filePath);
|
|
33
|
+
return TEMPLATE_FILE_EXTENSIONS.includes(ext);
|
|
34
|
+
}
|
|
35
|
+
export async function generateProject(templateDir, targetDir, options) {
|
|
36
|
+
const spinner = ora('生成项目...').start();
|
|
37
|
+
try {
|
|
38
|
+
// 确保目标目录存在
|
|
39
|
+
await fs.ensureDir(targetDir);
|
|
40
|
+
// 获取所有模板文件
|
|
41
|
+
const files = await getAllFiles(templateDir);
|
|
42
|
+
for (const file of files) {
|
|
43
|
+
const relativePath = path.relative(templateDir, file);
|
|
44
|
+
const targetPath = path.join(targetDir, relativePath);
|
|
45
|
+
// 确保目标目录存在
|
|
46
|
+
await fs.ensureDir(path.dirname(targetPath));
|
|
47
|
+
// 检查是否需要模板替换
|
|
48
|
+
if (isTemplateFile(file)) {
|
|
49
|
+
let content = await fs.readFile(file, 'utf-8');
|
|
50
|
+
content = replaceTemplateVariables(content, options);
|
|
51
|
+
await fs.writeFile(targetPath, content);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// 直接复制
|
|
55
|
+
await fs.copy(file, targetPath);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// 更新 package.json
|
|
59
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
60
|
+
if (await fs.pathExists(pkgPath)) {
|
|
61
|
+
const pkg = await fs.readJson(pkgPath);
|
|
62
|
+
// 移除无效字段
|
|
63
|
+
for (const field of REMOVE_PKG_FIELDS) {
|
|
64
|
+
delete pkg[field];
|
|
65
|
+
}
|
|
66
|
+
// 设置项目信息
|
|
67
|
+
pkg.name = options.name;
|
|
68
|
+
pkg.description = options.description;
|
|
69
|
+
if (options.author) {
|
|
70
|
+
pkg.author = options.author;
|
|
71
|
+
}
|
|
72
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
73
|
+
}
|
|
74
|
+
spinner.succeed('项目生成成功');
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
spinner.fail('项目生成失败');
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 递归获取所有文件
|
|
83
|
+
*/
|
|
84
|
+
async function getAllFiles(dir) {
|
|
85
|
+
const files = [];
|
|
86
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
const fullPath = path.join(dir, entry.name);
|
|
89
|
+
// 排除特定目录
|
|
90
|
+
if (EXCLUDE_DIRS.includes(entry.name)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (entry.isDirectory()) {
|
|
94
|
+
files.push(...await getAllFiles(fullPath));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
files.push(fullPath);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return files;
|
|
101
|
+
}
|
|
102
|
+
export async function installDependencies(projectDir) {
|
|
103
|
+
const spinner = ora('安装依赖...').start();
|
|
104
|
+
try {
|
|
105
|
+
const { exec } = await import('child_process');
|
|
106
|
+
const { promisify } = await import('util');
|
|
107
|
+
const execAsync = promisify(exec);
|
|
108
|
+
await execAsync('pnpm install', { cwd: projectDir });
|
|
109
|
+
spinner.succeed('依赖安装成功');
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
spinner.fail('依赖安装失败');
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
export async function promptProjectOptions(defaultName) {
|
|
3
|
+
const questions = [
|
|
4
|
+
{
|
|
5
|
+
type: 'input',
|
|
6
|
+
name: 'name',
|
|
7
|
+
message: '项目名称:',
|
|
8
|
+
default: defaultName || 'uxto-project',
|
|
9
|
+
validate: (input) => {
|
|
10
|
+
if (!input.trim()) {
|
|
11
|
+
return '项目名称不能为空';
|
|
12
|
+
}
|
|
13
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(input)) {
|
|
14
|
+
return '项目名称只能包含字母、数字、下划线和中划线';
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: 'input',
|
|
21
|
+
name: 'description',
|
|
22
|
+
message: '项目描述:',
|
|
23
|
+
default: 'UXTO UniApp 项目 - 支持 iOS、Android、鸿蒙',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: 'input',
|
|
27
|
+
name: 'author',
|
|
28
|
+
message: '作者:',
|
|
29
|
+
default: '',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
return inquirer.prompt(questions);
|
|
33
|
+
}
|
|
34
|
+
export async function promptInstallDeps() {
|
|
35
|
+
const { install } = await inquirer.prompt([
|
|
36
|
+
{
|
|
37
|
+
type: 'confirm',
|
|
38
|
+
name: 'install',
|
|
39
|
+
message: '是否安装依赖?',
|
|
40
|
+
default: true,
|
|
41
|
+
},
|
|
42
|
+
]);
|
|
43
|
+
return install;
|
|
44
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atooyu/uxto-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "UXTO UniApp 项目脚手架工具 - 支持 iOS、Android、鸿蒙",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"uxto": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"start": "node dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"cli",
|
|
21
|
+
"scaffold",
|
|
22
|
+
"uxto",
|
|
23
|
+
"uniapp",
|
|
24
|
+
"vue",
|
|
25
|
+
"mobile",
|
|
26
|
+
"ios",
|
|
27
|
+
"android",
|
|
28
|
+
"harmony",
|
|
29
|
+
"atooyu"
|
|
30
|
+
],
|
|
31
|
+
"author": "atooyu <chujingu@163.com>",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"chalk": "^5.3.0",
|
|
35
|
+
"commander": "^12.0.0",
|
|
36
|
+
"fs-extra": "^11.2.0",
|
|
37
|
+
"inquirer": "^9.2.15",
|
|
38
|
+
"ora": "^8.0.1"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/fs-extra": "^11.0.4",
|
|
42
|
+
"@types/inquirer": "^9.0.7",
|
|
43
|
+
"@types/node": "^20.11.30",
|
|
44
|
+
"typescript": "^5.4.2"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|