create-unibest 3.2.1 → 4.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 +65 -29
- package/bin/index.js +0 -1
- package/dist/index.js +727 -174
- package/package.json +9 -9
- package/dist/index.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,43 +1,79 @@
|
|
|
1
|
-
|
|
1
|
+
# create-unibest
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
<a href="https://www.npmjs.com/package/create-unibest"><img src="https://img.shields.io/npm/dm/create-unibest?colorA=363a4f&colorB=f5a97f&style=for-the-badge"></a>
|
|
5
|
-
<a href="https://www.npmjs.com/package/create-unibest"><img src="https://img.shields.io/npm/v/create-unibest?colorA=363a4f&colorB=a6da95&style=for-the-badge"></a>
|
|
6
|
-
</p>
|
|
3
|
+
跨平台 uniapp 项目脚手架工具。
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
<sub>>_ Easy to use create unibest</sub>
|
|
10
|
-
</h2>
|
|
5
|
+
## 功能特性
|
|
11
6
|
|
|
12
|
-
|
|
7
|
+
- 🚀 **快速创建** - 通过命令行快速生成 unibest 项目
|
|
8
|
+
- 🎯 **灵活配置** - 支持选择平台、UI库、Feature
|
|
9
|
+
- 🔧 **Feature 注入** - 创建后可动态添加 i18n、login 等功能
|
|
10
|
+
- 📦 **多平台支持** - H5、小程序、App 一键生成
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
## 使用方式
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
### 安装
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
```bash
|
|
17
|
+
# 全局安装(可选)
|
|
18
|
+
npm i -g create-unibest
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
npm update -g create-unibest # 更新 create-unibest 包
|
|
20
|
+
# 或直接使用
|
|
21
|
+
pnpm create unibest
|
|
23
22
|
```
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
### 创建项目
|
|
26
25
|
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
```bash
|
|
27
|
+
# 交互式创建
|
|
28
|
+
pnpm create unibest my-project
|
|
29
|
+
|
|
30
|
+
# 命令行参数创建
|
|
31
|
+
pnpm create unibest my-project \
|
|
32
|
+
--platform h5,mp-weixin \
|
|
33
|
+
--ui none \
|
|
34
|
+
--i18n \
|
|
35
|
+
--login
|
|
33
36
|
```
|
|
34
37
|
|
|
35
|
-
###
|
|
38
|
+
### 添加 Feature
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
cd my-project
|
|
42
|
+
|
|
43
|
+
# 添加多语言
|
|
44
|
+
pnpm create unibest add i18n
|
|
45
|
+
|
|
46
|
+
# 添加登录策略
|
|
47
|
+
pnpm create unibest add login
|
|
36
48
|
|
|
37
|
-
|
|
38
|
-
pnpm create unibest
|
|
39
|
-
pnpm create unibest my-project # 创建新的unibest项目
|
|
40
|
-
pnpm create unibest new my-project # 创建新的unibest项目
|
|
41
|
-
pnpm create unibest -v # 查看版本信息
|
|
42
|
-
pnpm create unibest -h # 查看帮助信息
|
|
49
|
+
# 强制重新注入(覆盖已有配置)
|
|
50
|
+
pnpm create unibest add i18n --force
|
|
43
51
|
```
|
|
52
|
+
|
|
53
|
+
## 参数说明
|
|
54
|
+
|
|
55
|
+
### 创建项目参数
|
|
56
|
+
|
|
57
|
+
| 参数 | 说明 |
|
|
58
|
+
|------|------|
|
|
59
|
+
| `-p, --platform` | 平台类型,支持 `h5`, `mp-weixin`, `app`, `mp-alipay`, `mp-toutiao` |
|
|
60
|
+
| `-u, --ui` | UI 库,支持 `none`, `wot-ui`, `uview-pro`, `sard-uniapp`, `uv-ui`, `uview-plus` |
|
|
61
|
+
| `-l, --login` | 启用登录策略 |
|
|
62
|
+
| `-i, --i18n` | 启用多语言 |
|
|
63
|
+
|
|
64
|
+
### 添加 Feature 参数
|
|
65
|
+
|
|
66
|
+
| 参数 | 说明 |
|
|
67
|
+
|------|------|
|
|
68
|
+
| `--path` | 项目路径,默认当前目录 |
|
|
69
|
+
| `--force` | 强制重新注入 Feature |
|
|
70
|
+
|
|
71
|
+
## 与 unibest 模板版本对应关系
|
|
72
|
+
|
|
73
|
+
| create-unibest 版本 | unibest 模板版本 |
|
|
74
|
+
|---------------------|------------------|
|
|
75
|
+
| 4.0.0 | 4.3.0+ |
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
MIT
|
package/bin/index.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,11 +1,254 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import
|
|
4
|
+
import process6 from "process";
|
|
5
|
+
import { green as green6, yellow as yellow5 } from "kolorist";
|
|
5
6
|
import minimist from "minimist";
|
|
6
7
|
|
|
7
|
-
//
|
|
8
|
-
|
|
8
|
+
// package.json
|
|
9
|
+
var version = "4.0.0";
|
|
10
|
+
var package_default = {
|
|
11
|
+
name: "create-unibest",
|
|
12
|
+
type: "module",
|
|
13
|
+
version,
|
|
14
|
+
packageManager: "pnpm@10.10.0",
|
|
15
|
+
description: "\u5FEB\u901F\u521B\u5EFA unibest \u9879\u76EE\u7684\u811A\u624B\u67B6\u5DE5\u5177",
|
|
16
|
+
author: "feige996",
|
|
17
|
+
license: "MIT",
|
|
18
|
+
homepage: "https://unibest.tech",
|
|
19
|
+
bin: {
|
|
20
|
+
best: "bin/index.js",
|
|
21
|
+
"create-unibest": "bin/index.js",
|
|
22
|
+
unibest: "bin/index.js"
|
|
23
|
+
},
|
|
24
|
+
files: [
|
|
25
|
+
"bin",
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
scripts: {
|
|
29
|
+
dev: "cross-env NODE_ENV=development tsup --watch",
|
|
30
|
+
build: "cross-env NODE_ENV=production tsup",
|
|
31
|
+
prepare: "pnpm build",
|
|
32
|
+
start: "cross-env NODE_ENV=development node bin/index.js"
|
|
33
|
+
},
|
|
34
|
+
dependencies: {
|
|
35
|
+
"@clack/prompts": "^0.11.0",
|
|
36
|
+
dayjs: "^1.11.18",
|
|
37
|
+
ejs: "^3.1.10",
|
|
38
|
+
"fs-extra": "^11.3.0",
|
|
39
|
+
kolorist: "^1.8.0",
|
|
40
|
+
minimist: "^1.2.8",
|
|
41
|
+
"node-fetch": "^3.3.2"
|
|
42
|
+
},
|
|
43
|
+
devDependencies: {
|
|
44
|
+
"@types/ejs": "^3.1.5",
|
|
45
|
+
"@types/fs-extra": "^11.0.4",
|
|
46
|
+
"@types/minimist": "^1.2.5",
|
|
47
|
+
"@types/node": "^24.5.0",
|
|
48
|
+
"cross-env": "^7.0.3",
|
|
49
|
+
tsup: "^8.5.0",
|
|
50
|
+
typescript: "^5.9.0"
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// src/commands/add.ts
|
|
55
|
+
import fs4 from "fs";
|
|
56
|
+
import path3 from "path";
|
|
57
|
+
import process2 from "process";
|
|
58
|
+
import { cancel, intro, isCancel, log, multiselect } from "@clack/prompts";
|
|
59
|
+
import { bold as bold2, green as green2 } from "kolorist";
|
|
60
|
+
|
|
61
|
+
// src/features/interface.ts
|
|
62
|
+
var AVAILABLE_FEATURES = [
|
|
63
|
+
{
|
|
64
|
+
name: "i18n",
|
|
65
|
+
description: "\u591A\u8BED\u8A00\u652F\u6301",
|
|
66
|
+
dependencies: {
|
|
67
|
+
"vue-i18n": "^9.0.0",
|
|
68
|
+
"dayjs": "^1.11.0"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "login",
|
|
73
|
+
description: "\u767B\u5F55\u7B56\u7565\uFF08\u9ED1\u767D\u540D\u5355\u3001\u767B\u5F55\u62E6\u622A\u7B49\uFF09",
|
|
74
|
+
dependencies: {}
|
|
75
|
+
}
|
|
76
|
+
];
|
|
77
|
+
function getFeatureByName(name) {
|
|
78
|
+
return AVAILABLE_FEATURES.find((f) => f.name === name);
|
|
79
|
+
}
|
|
80
|
+
function getSelectedFeatures(options) {
|
|
81
|
+
const features = [];
|
|
82
|
+
if (options.i18n) {
|
|
83
|
+
const feature = getFeatureByName("i18n");
|
|
84
|
+
if (feature) features.push(feature);
|
|
85
|
+
}
|
|
86
|
+
if (options.loginStrategy) {
|
|
87
|
+
const feature = getFeatureByName("login");
|
|
88
|
+
if (feature) features.push(feature);
|
|
89
|
+
}
|
|
90
|
+
return features;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/features/loader.ts
|
|
94
|
+
import fs from "fs";
|
|
95
|
+
import path from "path";
|
|
96
|
+
var FEATURES_DIR = path.resolve(process.cwd(), "../../features");
|
|
97
|
+
async function loadFeatureHooks(featureName) {
|
|
98
|
+
const hooksPath = path.join(FEATURES_DIR, featureName, "hooks.ts");
|
|
99
|
+
if (!fs.existsSync(hooksPath)) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
const module = await import(hooksPath);
|
|
103
|
+
return module;
|
|
104
|
+
}
|
|
105
|
+
function getAvailableFeatureNames() {
|
|
106
|
+
if (!fs.existsSync(FEATURES_DIR)) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
const entries = fs.readdirSync(FEATURES_DIR, { withFileTypes: true });
|
|
110
|
+
return entries.filter((e) => e.isDirectory() && fs.existsSync(path.join(FEATURES_DIR, e.name, "files"))).map((e) => e.name);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/utils/injector.ts
|
|
114
|
+
import fs2 from "fs";
|
|
115
|
+
import path2 from "path";
|
|
116
|
+
import { dirname } from "path";
|
|
117
|
+
import { fileURLToPath } from "url";
|
|
118
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
119
|
+
var __dirname = dirname(__filename);
|
|
120
|
+
var FEATURES_PATH = path2.join(__dirname, "..", "..", "..", "features");
|
|
121
|
+
var FeatureInjector = class {
|
|
122
|
+
constructor(projectPath) {
|
|
123
|
+
this.projectPath = projectPath;
|
|
124
|
+
}
|
|
125
|
+
injectFile(targetPath, featureCode, placeholder) {
|
|
126
|
+
const fullPath = path2.join(this.projectPath, targetPath);
|
|
127
|
+
if (!fs2.existsSync(fullPath)) {
|
|
128
|
+
return { success: false, message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${targetPath}` };
|
|
129
|
+
}
|
|
130
|
+
const content = fs2.readFileSync(fullPath, "utf-8");
|
|
131
|
+
if (!content.includes(placeholder)) {
|
|
132
|
+
return { success: false, message: `\u5360\u4F4D\u7B26\u4E0D\u5B58\u5728: ${placeholder}` };
|
|
133
|
+
}
|
|
134
|
+
const newContent = content.replace(placeholder, featureCode);
|
|
135
|
+
fs2.writeFileSync(fullPath, newContent);
|
|
136
|
+
return { success: true, message: `\u6CE8\u5165\u6210\u529F: ${targetPath}` };
|
|
137
|
+
}
|
|
138
|
+
replaceFile(targetPath, featureFilePath) {
|
|
139
|
+
const targetFullPath = path2.join(this.projectPath, targetPath);
|
|
140
|
+
const featureFullPath = path2.join(FEATURES_PATH, featureFilePath);
|
|
141
|
+
if (!fs2.existsSync(featureFullPath)) {
|
|
142
|
+
return { success: false, message: `Feature \u6587\u4EF6\u4E0D\u5B58\u5728: ${featureFilePath}` };
|
|
143
|
+
}
|
|
144
|
+
const featureContent = fs2.readFileSync(featureFullPath, "utf-8");
|
|
145
|
+
fs2.writeFileSync(targetFullPath, featureContent);
|
|
146
|
+
return { success: true, message: `\u66FF\u6362\u6210\u529F: ${targetPath}` };
|
|
147
|
+
}
|
|
148
|
+
createFile(relativePath, featureFilePath) {
|
|
149
|
+
const targetFullPath = path2.join(this.projectPath, relativePath);
|
|
150
|
+
const featureFullPath = path2.join(FEATURES_PATH, featureFilePath);
|
|
151
|
+
if (!fs2.existsSync(featureFullPath)) {
|
|
152
|
+
return { success: false, message: `Feature \u6587\u4EF6\u4E0D\u5B58\u5728: ${featureFilePath}` };
|
|
153
|
+
}
|
|
154
|
+
const dir = path2.dirname(targetFullPath);
|
|
155
|
+
if (!fs2.existsSync(dir)) {
|
|
156
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
157
|
+
}
|
|
158
|
+
const featureContent = fs2.readFileSync(featureFullPath, "utf-8");
|
|
159
|
+
fs2.writeFileSync(targetFullPath, featureContent);
|
|
160
|
+
return { success: true, message: `\u521B\u5EFA\u6210\u529F: ${relativePath}` };
|
|
161
|
+
}
|
|
162
|
+
appendAfter(targetPath, marker, code) {
|
|
163
|
+
const fullPath = path2.join(this.projectPath, targetPath);
|
|
164
|
+
if (!fs2.existsSync(fullPath)) {
|
|
165
|
+
return { success: false, message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${targetPath}` };
|
|
166
|
+
}
|
|
167
|
+
const content = fs2.readFileSync(fullPath, "utf-8");
|
|
168
|
+
if (!content.includes(marker)) {
|
|
169
|
+
return { success: false, message: `\u6807\u8BB0\u4E0D\u5B58\u5728: ${marker}` };
|
|
170
|
+
}
|
|
171
|
+
const newContent = content.replace(marker, `${marker}
|
|
172
|
+
${code}`);
|
|
173
|
+
fs2.writeFileSync(fullPath, newContent);
|
|
174
|
+
return { success: true, message: `\u8FFD\u52A0\u6210\u529F: ${targetPath}` };
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
async function injectI18n(projectPath) {
|
|
178
|
+
const results = [];
|
|
179
|
+
const injector = new FeatureInjector(projectPath);
|
|
180
|
+
results.push(injector.appendAfter(
|
|
181
|
+
"src/main.ts",
|
|
182
|
+
`import 'virtual:uno.css'`,
|
|
183
|
+
`import i18n from './locale/index'`
|
|
184
|
+
));
|
|
185
|
+
results.push(injector.appendAfter(
|
|
186
|
+
"src/main.ts",
|
|
187
|
+
` app.use(requestInterceptor)`,
|
|
188
|
+
` app.use(i18n)`
|
|
189
|
+
));
|
|
190
|
+
results.push(injector.replaceFile(
|
|
191
|
+
"src/tabbar/config.ts",
|
|
192
|
+
"i18n/files/src/tabbar/config.ts"
|
|
193
|
+
));
|
|
194
|
+
results.push(injector.replaceFile(
|
|
195
|
+
"src/tabbar/index.vue",
|
|
196
|
+
"i18n/files/src/tabbar/index.vue"
|
|
197
|
+
));
|
|
198
|
+
results.push(injector.replaceFile(
|
|
199
|
+
"src/tabbar/TabbarItem.vue",
|
|
200
|
+
"i18n/files/src/tabbar/TabbarItem.vue"
|
|
201
|
+
));
|
|
202
|
+
results.push(injector.replaceFile(
|
|
203
|
+
"src/utils/index.ts",
|
|
204
|
+
"i18n/files/src/utils/index.ts"
|
|
205
|
+
));
|
|
206
|
+
results.push(injector.replaceFile(
|
|
207
|
+
"src/store/token.ts",
|
|
208
|
+
"i18n/files/src/store/token.ts"
|
|
209
|
+
));
|
|
210
|
+
const i18nFiles = [
|
|
211
|
+
"src/locale/index.ts",
|
|
212
|
+
"src/locale/en.json",
|
|
213
|
+
"src/locale/zh-Hans.json",
|
|
214
|
+
"src/locale/README.md",
|
|
215
|
+
"src/utils/i18n.ts",
|
|
216
|
+
"src/types/i18n.d.ts",
|
|
217
|
+
"src/tabbar/i18n.ts",
|
|
218
|
+
"src/pages/i18n/index.vue"
|
|
219
|
+
];
|
|
220
|
+
for (const file of i18nFiles) {
|
|
221
|
+
const featurePath = `i18n/files/${file}`;
|
|
222
|
+
results.push(injector.createFile(file, featurePath));
|
|
223
|
+
}
|
|
224
|
+
return results;
|
|
225
|
+
}
|
|
226
|
+
async function injectLogin(projectPath) {
|
|
227
|
+
const results = [];
|
|
228
|
+
const injector = new FeatureInjector(projectPath);
|
|
229
|
+
results.push(injector.replaceFile(
|
|
230
|
+
"src/router/interceptor.ts",
|
|
231
|
+
"login/files/src/router/interceptor.ts"
|
|
232
|
+
));
|
|
233
|
+
results.push(injector.replaceFile(
|
|
234
|
+
"src/router/config.ts",
|
|
235
|
+
"login/files/src/router/config.ts"
|
|
236
|
+
));
|
|
237
|
+
results.push(injector.replaceFile(
|
|
238
|
+
"src/pages/me/me.vue",
|
|
239
|
+
"login/files/src/pages/me.vue"
|
|
240
|
+
));
|
|
241
|
+
const loginFiles = [
|
|
242
|
+
"src/pages/auth/login.vue",
|
|
243
|
+
"src/pages/auth/register.vue",
|
|
244
|
+
"src/pages/auth/README.md"
|
|
245
|
+
];
|
|
246
|
+
for (const file of loginFiles) {
|
|
247
|
+
const featurePath = `login/files/${file}`;
|
|
248
|
+
results.push(injector.createFile(file, featurePath));
|
|
249
|
+
}
|
|
250
|
+
return results;
|
|
251
|
+
}
|
|
9
252
|
|
|
10
253
|
// src/utils/logger.ts
|
|
11
254
|
import { bold, green, red, yellow, cyan } from "kolorist";
|
|
@@ -32,6 +275,178 @@ var logger = {
|
|
|
32
275
|
}
|
|
33
276
|
};
|
|
34
277
|
|
|
278
|
+
// src/utils/readPackageJson.ts
|
|
279
|
+
import fs3 from "fs";
|
|
280
|
+
function readPackageJson(pkgPath) {
|
|
281
|
+
if (!fs3.existsSync(pkgPath)) {
|
|
282
|
+
throw new Error(`package.json not found: ${pkgPath}`);
|
|
283
|
+
}
|
|
284
|
+
const content = fs3.readFileSync(pkgPath, "utf-8");
|
|
285
|
+
return JSON.parse(content);
|
|
286
|
+
}
|
|
287
|
+
function writePackageJson(pkgPath, pkg) {
|
|
288
|
+
fs3.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
289
|
+
`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/commands/add.ts
|
|
293
|
+
function getFeatureStatusFromPackageJson(pkgPath) {
|
|
294
|
+
try {
|
|
295
|
+
const pkg = readPackageJson(pkgPath);
|
|
296
|
+
return {
|
|
297
|
+
i18n: pkg.unibest?.i18n === true,
|
|
298
|
+
login: pkg.unibest?.loginStrategy === true
|
|
299
|
+
};
|
|
300
|
+
} catch {
|
|
301
|
+
return { i18n: false, login: false };
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function updatePackageJsonForFeature(pkgPath, featureName) {
|
|
305
|
+
const pkg = readPackageJson(pkgPath);
|
|
306
|
+
if (!pkg.unibest) {
|
|
307
|
+
pkg.unibest = {};
|
|
308
|
+
}
|
|
309
|
+
switch (featureName) {
|
|
310
|
+
case "i18n":
|
|
311
|
+
pkg.unibest.i18n = true;
|
|
312
|
+
break;
|
|
313
|
+
case "login":
|
|
314
|
+
pkg.unibest.loginStrategy = true;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
writePackageJson(pkgPath, pkg);
|
|
318
|
+
}
|
|
319
|
+
async function checkFeatureStatus(projectPath) {
|
|
320
|
+
const pkgPath = path3.join(projectPath, "package.json");
|
|
321
|
+
const statusFromPkg = getFeatureStatusFromPackageJson(pkgPath);
|
|
322
|
+
return [
|
|
323
|
+
{ name: "i18n", enabled: statusFromPkg.i18n },
|
|
324
|
+
{ name: "login", enabled: statusFromPkg.login }
|
|
325
|
+
];
|
|
326
|
+
}
|
|
327
|
+
async function addFeature(featureName, projectPath, options = {}) {
|
|
328
|
+
const feature = getFeatureByName(featureName);
|
|
329
|
+
if (!feature) {
|
|
330
|
+
logger.error(`\u672A\u77E5\u7684 Feature: ${featureName}`);
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
const pkgPath = path3.join(projectPath, "package.json");
|
|
334
|
+
const pkg = readPackageJson(pkgPath);
|
|
335
|
+
let alreadyAdded = false;
|
|
336
|
+
if (featureName === "i18n" && pkg.unibest?.i18n === true) {
|
|
337
|
+
alreadyAdded = true;
|
|
338
|
+
} else if (featureName === "login" && pkg.unibest?.loginStrategy === true) {
|
|
339
|
+
alreadyAdded = true;
|
|
340
|
+
}
|
|
341
|
+
if (alreadyAdded && !options.force) {
|
|
342
|
+
logger.warn(`Feature ${featureName} \u5DF2\u6DFB\u52A0\u8FC7\uFF0C\u5982\u9700\u91CD\u65B0\u6CE8\u5165\u8BF7\u4F7F\u7528 --force \u53C2\u6570`);
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
log.info(`\u6B63\u5728\u6DFB\u52A0 Feature: ${green2(featureName)} - ${feature.description}`);
|
|
346
|
+
try {
|
|
347
|
+
let results;
|
|
348
|
+
switch (featureName) {
|
|
349
|
+
case "i18n":
|
|
350
|
+
results = await injectI18n(projectPath);
|
|
351
|
+
break;
|
|
352
|
+
case "login":
|
|
353
|
+
results = await injectLogin(projectPath);
|
|
354
|
+
break;
|
|
355
|
+
default:
|
|
356
|
+
logger.error(`\u4E0D\u652F\u6301\u7684 Feature: ${featureName}`);
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
for (const result of results) {
|
|
360
|
+
if (result.success) {
|
|
361
|
+
logger.success(result.message);
|
|
362
|
+
} else {
|
|
363
|
+
logger.warn(result.message);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const hooks = await loadFeatureHooks(featureName);
|
|
367
|
+
if (hooks?.postApply) {
|
|
368
|
+
await hooks.postApply({
|
|
369
|
+
options: { projectName: "", platforms: [], uiLibrary: "none", i18n: true, loginStrategy: true },
|
|
370
|
+
projectPath,
|
|
371
|
+
featureName
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
updatePackageJsonForFeature(pkgPath, featureName);
|
|
375
|
+
logger.success(`\u5DF2\u66F4\u65B0 package.json \u7684 unibest \u914D\u7F6E`);
|
|
376
|
+
if (feature.dependencies && Object.keys(feature.dependencies).length > 0) {
|
|
377
|
+
logger.info(`\u5B89\u88C5\u4F9D\u8D56: ${Object.keys(feature.dependencies).join(", ")}`);
|
|
378
|
+
}
|
|
379
|
+
logger.success(`Feature ${featureName} \u6DFB\u52A0\u6210\u529F\uFF01`);
|
|
380
|
+
return true;
|
|
381
|
+
} catch (error) {
|
|
382
|
+
logger.error(`\u6DFB\u52A0 Feature \u5931\u8D25: ${error.message}`);
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
async function addCommand(args) {
|
|
387
|
+
const options = {
|
|
388
|
+
path: args.path || args.p || ".",
|
|
389
|
+
feature: args._[1] || args.feature || args.f,
|
|
390
|
+
force: args.force || args.f
|
|
391
|
+
};
|
|
392
|
+
intro(bold2(green2(`create-unibest@v${version} \u6DFB\u52A0 Feature`)));
|
|
393
|
+
const projectPath = path3.isAbsolute(options.path) ? options.path : path3.join(process2.cwd(), options.path);
|
|
394
|
+
const pkgPath = path3.join(projectPath, "package.json");
|
|
395
|
+
if (!fs4.existsSync(pkgPath)) {
|
|
396
|
+
logger.error(`\u9879\u76EE\u4E0D\u5B58\u5728: ${projectPath}`);
|
|
397
|
+
process2.exit(1);
|
|
398
|
+
}
|
|
399
|
+
const pkg = readPackageJson(pkgPath);
|
|
400
|
+
if (pkg.name !== "unibest") {
|
|
401
|
+
logger.warn(`\u5F53\u524D\u9879\u76EE\u53EF\u80FD\u4E0D\u662F unibest \u9879\u76EE: ${pkg.name}`);
|
|
402
|
+
}
|
|
403
|
+
const availableFeatures = getAvailableFeatureNames();
|
|
404
|
+
try {
|
|
405
|
+
if (!options.feature) {
|
|
406
|
+
const detected = await checkFeatureStatus(projectPath);
|
|
407
|
+
const available = availableFeatures.filter((f) => !detected.find((d) => d.name === f && d.enabled));
|
|
408
|
+
if (available.length === 0) {
|
|
409
|
+
logger.info("\u6240\u6709\u53EF\u7528 Feature \u5DF2\u542F\u7528");
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const selectedFeatures = await multiselect({
|
|
413
|
+
message: `\u8BF7\u9009\u62E9\u8981\u6DFB\u52A0\u7684 Feature`,
|
|
414
|
+
options: available.map((name) => {
|
|
415
|
+
const feature = getFeatureByName(name);
|
|
416
|
+
return {
|
|
417
|
+
value: name,
|
|
418
|
+
label: feature?.name || name,
|
|
419
|
+
hint: feature?.description || ""
|
|
420
|
+
};
|
|
421
|
+
}),
|
|
422
|
+
required: false
|
|
423
|
+
});
|
|
424
|
+
if (isCancel(selectedFeatures)) {
|
|
425
|
+
cancel("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
|
|
426
|
+
process2.exit(0);
|
|
427
|
+
}
|
|
428
|
+
if (!Array.isArray(selectedFeatures) || selectedFeatures.length === 0) {
|
|
429
|
+
logger.info("\u672A\u9009\u62E9\u4EFB\u4F55 Feature");
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
for (const featureName of selectedFeatures) {
|
|
433
|
+
await addFeature(featureName, projectPath, options);
|
|
434
|
+
}
|
|
435
|
+
} else {
|
|
436
|
+
const features = Array.isArray(options.feature) ? options.feature : [options.feature];
|
|
437
|
+
for (const featureName of features) {
|
|
438
|
+
await addFeature(featureName, projectPath, options);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
} catch (error) {
|
|
442
|
+
logger.error(`\u6DFB\u52A0 Feature \u5931\u8D25: ${error.message}`);
|
|
443
|
+
process2.exit(1);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// src/commands/create/prompts.ts
|
|
448
|
+
import { text, multiselect as multiselect2, select, confirm, cancel as cancel2, isCancel as isCancel2 } from "@clack/prompts";
|
|
449
|
+
|
|
35
450
|
// src/utils/validate.ts
|
|
36
451
|
import { existsSync } from "fs";
|
|
37
452
|
import { yellow as yellow2 } from "kolorist";
|
|
@@ -58,12 +473,56 @@ function checkProjectNameExistAndValidate(_projectName) {
|
|
|
58
473
|
}
|
|
59
474
|
|
|
60
475
|
// src/commands/create/prompts.ts
|
|
61
|
-
import { green as
|
|
476
|
+
import { green as green3, red as red2 } from "kolorist";
|
|
477
|
+
import process3 from "process";
|
|
478
|
+
var VALID_PLATFORMS = ["h5", "mp-weixin", "app", "mp-alipay", "mp-toutiao"];
|
|
479
|
+
var VALID_UI_LIBRARIES = ["none", "wot-ui", "uview-pro", "sard-uniapp", "uv-ui", "uview-plus"];
|
|
62
480
|
async function promptUser(projectName, argv = {}) {
|
|
481
|
+
let platforms;
|
|
482
|
+
let uiLibrary;
|
|
483
|
+
let loginStrategy;
|
|
484
|
+
let i18n;
|
|
485
|
+
const platformArg = argv.p || argv.platform;
|
|
486
|
+
if (platformArg) {
|
|
487
|
+
let parsedPlatforms = [];
|
|
488
|
+
if (Array.isArray(platformArg)) {
|
|
489
|
+
parsedPlatforms = platformArg;
|
|
490
|
+
} else if (typeof platformArg === "string") {
|
|
491
|
+
parsedPlatforms = platformArg.split(",");
|
|
492
|
+
}
|
|
493
|
+
const invalidPlatforms = parsedPlatforms.filter((p) => !VALID_PLATFORMS.includes(p));
|
|
494
|
+
if (invalidPlatforms.length > 0) {
|
|
495
|
+
console.error(red2(`\u65E0\u6548\u7684\u5E73\u53F0\u53C2\u6570: ${invalidPlatforms.join(", ")}`));
|
|
496
|
+
console.error(red2(`\u53EF\u9009\u503C: ${VALID_PLATFORMS.join(", ")}`));
|
|
497
|
+
process3.exit(1);
|
|
498
|
+
}
|
|
499
|
+
platforms = parsedPlatforms;
|
|
500
|
+
}
|
|
501
|
+
const uiArg = argv.u || argv.ui;
|
|
502
|
+
if (uiArg) {
|
|
503
|
+
if (!VALID_UI_LIBRARIES.includes(uiArg)) {
|
|
504
|
+
console.error(red2(`\u65E0\u6548\u7684UI\u5E93\u53C2\u6570: ${uiArg}`));
|
|
505
|
+
console.error(red2(`\u53EF\u9009\u503C: ${VALID_UI_LIBRARIES.join(", ")}`));
|
|
506
|
+
process3.exit(1);
|
|
507
|
+
}
|
|
508
|
+
uiLibrary = uiArg;
|
|
509
|
+
}
|
|
510
|
+
const loginArg = argv.l ?? argv.login;
|
|
511
|
+
if (loginArg === true || loginArg === "true") {
|
|
512
|
+
loginStrategy = true;
|
|
513
|
+
} else if (loginArg === false || loginArg === "false") {
|
|
514
|
+
loginStrategy = false;
|
|
515
|
+
}
|
|
516
|
+
const i18nArg = argv.i ?? argv.i18n;
|
|
517
|
+
if (i18nArg === true || i18nArg === "true") {
|
|
518
|
+
i18n = true;
|
|
519
|
+
} else if (i18nArg === false || i18nArg === "false") {
|
|
520
|
+
i18n = false;
|
|
521
|
+
}
|
|
63
522
|
try {
|
|
64
523
|
if (!projectName) {
|
|
65
524
|
const inputProjectName = await text({
|
|
66
|
-
message: `\u8BF7\u8F93\u5165\u9879\u76EE\u540D\u79F0${
|
|
525
|
+
message: `\u8BF7\u8F93\u5165\u9879\u76EE\u540D\u79F0${green3("[\u9879\u76EE\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u77ED\u6A2A\u7EBF\uFF0C\u5343\u4E07\u522B\u5199\u4E2D\u6587]")}`,
|
|
67
526
|
initialValue: "",
|
|
68
527
|
validate: (value) => {
|
|
69
528
|
if (!value.trim()) return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
|
|
@@ -72,60 +531,72 @@ async function promptUser(projectName, argv = {}) {
|
|
|
72
531
|
return;
|
|
73
532
|
}
|
|
74
533
|
});
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
534
|
+
if (isCancel2(inputProjectName)) {
|
|
535
|
+
cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
|
|
536
|
+
process3.exit(0);
|
|
78
537
|
}
|
|
79
538
|
projectName = inputProjectName;
|
|
80
539
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
options: [
|
|
101
|
-
{ value: "none", label: "\u65E0UI\u5E93" },
|
|
102
|
-
{ value: "wot-ui", label: "wot-ui" },
|
|
103
|
-
{ value: "uview-pro", label: "uview-pro" },
|
|
104
|
-
{ value: "sard-uniapp", label: "sard-uniapp" },
|
|
105
|
-
{ value: "uv-ui", label: "uv-ui" },
|
|
106
|
-
{ value: "uview-plus", label: "uview-plus" }
|
|
107
|
-
],
|
|
108
|
-
initialValue: "none"
|
|
109
|
-
});
|
|
110
|
-
if (isCancel(uiLibrary)) {
|
|
111
|
-
cancel("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
|
|
112
|
-
process.exit(0);
|
|
540
|
+
if (!platforms) {
|
|
541
|
+
const selectedPlatforms = await multiselect2({
|
|
542
|
+
message: `\u8BF7\u9009\u62E9\u9700\u8981\u652F\u6301\u7684\u5E73\u53F0\uFF08\u591A\u9009\uFF09${green3("[\u811A\u624B\u67B6\u5C06\u6839\u636E\u6240\u9009\u5E73\u53F0\u751F\u6210\u5BF9\u5E94\u7684\u5E73\u53F0\u4EE3\u7801\uFF0C\u8BF7\u6839\u636E\u5B9E\u9645\u60C5\u51B5\u9009\u62E9]")}`,
|
|
543
|
+
options: [
|
|
544
|
+
{ value: "h5", label: "H5" },
|
|
545
|
+
{ value: "mp-weixin", label: "\u5FAE\u4FE1\u5C0F\u7A0B\u5E8F" },
|
|
546
|
+
{ value: "app", label: "APP" },
|
|
547
|
+
{ value: "mp-alipay", label: "\u652F\u4ED8\u5B9D\u5C0F\u7A0B\u5E8F\uFF08\u5305\u542B\u9489\u9489\uFF09" },
|
|
548
|
+
{ value: "mp-toutiao", label: "\u6296\u97F3\u5C0F\u7A0B\u5E8F" }
|
|
549
|
+
],
|
|
550
|
+
initialValues: ["h5"],
|
|
551
|
+
// 默认选择 H5
|
|
552
|
+
required: true
|
|
553
|
+
});
|
|
554
|
+
if (isCancel2(selectedPlatforms)) {
|
|
555
|
+
cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
|
|
556
|
+
process3.exit(0);
|
|
557
|
+
}
|
|
558
|
+
platforms = selectedPlatforms;
|
|
113
559
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
560
|
+
if (!uiLibrary) {
|
|
561
|
+
const selectedUiLibrary = await select({
|
|
562
|
+
message: "\u8BF7\u9009\u62E9UI\u5E93",
|
|
563
|
+
options: [
|
|
564
|
+
{ value: "none", label: "\u65E0UI\u5E93" },
|
|
565
|
+
{ value: "wot-ui", label: "wot-ui" },
|
|
566
|
+
{ value: "uview-pro", label: "uview-pro" },
|
|
567
|
+
{ value: "sard-uniapp", label: "sard-uniapp" },
|
|
568
|
+
{ value: "uv-ui", label: "uv-ui" },
|
|
569
|
+
{ value: "uview-plus", label: "uview-plus" }
|
|
570
|
+
],
|
|
571
|
+
initialValue: "none"
|
|
572
|
+
});
|
|
573
|
+
if (isCancel2(selectedUiLibrary)) {
|
|
574
|
+
cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
|
|
575
|
+
process3.exit(0);
|
|
576
|
+
}
|
|
577
|
+
uiLibrary = selectedUiLibrary;
|
|
121
578
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
579
|
+
if (loginStrategy === void 0) {
|
|
580
|
+
const selectedLoginStrategy = await confirm({
|
|
581
|
+
message: `\u662F\u5426\u9700\u8981\u767B\u5F55\u7B56\u7565\uFF08\u9ED1\u767D\u540D\u5355\u3001\u767B\u5F55\u62E6\u622A\u7B49\uFF09\uFF1F${green3("[\u6682\u4E0D\u77E5\u9053\u7684\uFF0C\u9009No\u5373\u53EF\uFF0C\u9879\u76EE\u751F\u6210\u540E\u4E5F\u53EF\u4EE5\u52A0\u8BE5\u7B56\u7565]")}`,
|
|
582
|
+
initialValue: false
|
|
583
|
+
});
|
|
584
|
+
if (isCancel2(selectedLoginStrategy)) {
|
|
585
|
+
cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
|
|
586
|
+
process3.exit(0);
|
|
587
|
+
}
|
|
588
|
+
loginStrategy = selectedLoginStrategy;
|
|
589
|
+
}
|
|
590
|
+
if (i18n === void 0) {
|
|
591
|
+
const selectedI18n = await confirm({
|
|
592
|
+
message: "\u662F\u5426\u9700\u8981\u591A\u8BED\u8A00i18n\uFF1F",
|
|
593
|
+
initialValue: false
|
|
594
|
+
});
|
|
595
|
+
if (isCancel2(selectedI18n)) {
|
|
596
|
+
cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
|
|
597
|
+
process3.exit(0);
|
|
598
|
+
}
|
|
599
|
+
i18n = selectedI18n;
|
|
129
600
|
}
|
|
130
601
|
return {
|
|
131
602
|
projectName,
|
|
@@ -139,49 +610,106 @@ async function promptUser(projectName, argv = {}) {
|
|
|
139
610
|
};
|
|
140
611
|
} catch (error) {
|
|
141
612
|
logger.error(`\u8BE2\u95EE\u8FC7\u7A0B\u51FA\u9519: ${error.message}`);
|
|
142
|
-
|
|
613
|
+
process3.exit(1);
|
|
143
614
|
}
|
|
144
615
|
}
|
|
145
616
|
|
|
146
617
|
// src/commands/create/generate.ts
|
|
147
|
-
import
|
|
148
|
-
import { log } from "@clack/prompts";
|
|
618
|
+
import process5 from "process";
|
|
619
|
+
import { log as log2 } from "@clack/prompts";
|
|
149
620
|
|
|
150
621
|
// src/utils/cloneRepo.ts
|
|
151
622
|
import { exec } from "child_process";
|
|
152
|
-
import { promises as
|
|
153
|
-
import { join as join3 } from "path";
|
|
154
|
-
import
|
|
155
|
-
import
|
|
623
|
+
import { promises as fsPromises } from "fs";
|
|
624
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
625
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
626
|
+
import process4 from "process";
|
|
627
|
+
import { red as red3 } from "kolorist";
|
|
156
628
|
|
|
157
629
|
// src/utils/replacePackageJson.ts
|
|
158
630
|
import { readFileSync, writeFileSync } from "fs";
|
|
159
631
|
import { join as join2 } from "path";
|
|
160
|
-
|
|
632
|
+
import dayjs from "dayjs";
|
|
633
|
+
function replaceContent(filePath, projectName, version2, options) {
|
|
161
634
|
const fileContent = JSON.parse(readFileSync(filePath, "utf8"));
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
635
|
+
const unibestVersion = fileContent["unibest-version"];
|
|
636
|
+
const unibestUpdateTime = fileContent["unibest-update-time"];
|
|
637
|
+
delete fileContent["unibest-version"];
|
|
638
|
+
delete fileContent["unibest-update-time"];
|
|
639
|
+
delete fileContent.metadata;
|
|
640
|
+
delete fileContent.name;
|
|
641
|
+
delete fileContent.version;
|
|
642
|
+
const { projectName: _, ...restOptions } = options;
|
|
643
|
+
const selectedFeatures = getSelectedFeatures(options);
|
|
644
|
+
const featureDeps = {};
|
|
645
|
+
for (const feature of selectedFeatures) {
|
|
646
|
+
if (feature.dependencies) {
|
|
647
|
+
Object.assign(featureDeps, feature.dependencies);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (!fileContent.dependencies) {
|
|
651
|
+
fileContent.dependencies = {};
|
|
652
|
+
}
|
|
653
|
+
for (const [pkg, ver] of Object.entries(featureDeps)) {
|
|
654
|
+
if (!fileContent.dependencies[pkg]) {
|
|
655
|
+
fileContent.dependencies[pkg] = ver;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
const newContent = {
|
|
659
|
+
name: projectName,
|
|
660
|
+
type: fileContent.type,
|
|
661
|
+
// 保持 type 在前(如果存在)
|
|
662
|
+
version: version2,
|
|
663
|
+
unibest: {
|
|
664
|
+
...restOptions,
|
|
665
|
+
cliVersion: version,
|
|
666
|
+
unibestVersion,
|
|
667
|
+
unibestUpdateTime,
|
|
668
|
+
createdAt: dayjs().format("YYYY-MM-DD HH:mm:ss")
|
|
669
|
+
},
|
|
670
|
+
...fileContent
|
|
671
|
+
// 剩余字段
|
|
672
|
+
};
|
|
673
|
+
writeFileSync(filePath, JSON.stringify(newContent, null, 2));
|
|
165
674
|
}
|
|
166
|
-
function replacePackageJson(root2, name, version2) {
|
|
675
|
+
function replacePackageJson(root2, name, version2, options) {
|
|
167
676
|
const projectName = name.toLocaleLowerCase().replace(/\s/g, "-");
|
|
168
677
|
const pkgPath = join2(root2, "package.json");
|
|
169
|
-
replaceContent(pkgPath, projectName, version2);
|
|
678
|
+
replaceContent(pkgPath, projectName, version2, options);
|
|
170
679
|
}
|
|
171
680
|
|
|
172
681
|
// src/utils/cloneRepo.ts
|
|
682
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
683
|
+
var __dirname2 = dirname2(__filename2);
|
|
684
|
+
var USE_LOCAL_TEMPLATE = process4.env.LOCAL_TEMPLATE === "true";
|
|
685
|
+
var TEMPLATE_BASE_PATH = null;
|
|
686
|
+
async function getTemplateBasePath() {
|
|
687
|
+
if (TEMPLATE_BASE_PATH) return TEMPLATE_BASE_PATH;
|
|
688
|
+
const candidates = [
|
|
689
|
+
join3(__dirname2, "..", "..", "..", "packages", "template-base"),
|
|
690
|
+
join3(__dirname2, "..", "..", "packages", "template-base")
|
|
691
|
+
];
|
|
692
|
+
const { existsSync: existsSync3 } = await import("fs");
|
|
693
|
+
for (const candidate of candidates) {
|
|
694
|
+
if (existsSync3(candidate)) {
|
|
695
|
+
TEMPLATE_BASE_PATH = candidate;
|
|
696
|
+
return candidate;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
throw new Error("\u65E0\u6CD5\u627E\u5230 template-base \u76EE\u5F55");
|
|
700
|
+
}
|
|
173
701
|
async function removeGitFolder(localPath) {
|
|
174
702
|
const gitFolderPath = join3(localPath, ".git");
|
|
175
|
-
await
|
|
703
|
+
await fsPromises.rm(gitFolderPath, { recursive: true, force: true });
|
|
176
704
|
}
|
|
177
|
-
var REPO_URL = "https://
|
|
705
|
+
var REPO_URL = "https://gitee.com/feige996/unibest.git";
|
|
178
706
|
async function cloneRepo(projectName, branch) {
|
|
179
707
|
try {
|
|
180
708
|
await new Promise((resolve, reject) => {
|
|
181
709
|
const execStr = `git clone --depth=1 -b ${branch} ${REPO_URL} "${projectName}"`;
|
|
182
710
|
exec(execStr, async (error) => {
|
|
183
711
|
if (error) {
|
|
184
|
-
console.error(`${
|
|
712
|
+
console.error(`${red3("exec error:")} ${error}`);
|
|
185
713
|
reject(error);
|
|
186
714
|
return;
|
|
187
715
|
}
|
|
@@ -195,19 +723,40 @@ async function cloneRepo(projectName, branch) {
|
|
|
195
723
|
});
|
|
196
724
|
return;
|
|
197
725
|
} catch (error) {
|
|
198
|
-
console.error(`${
|
|
726
|
+
console.error(`${red3("cloneRepo error:")} ${error}`);
|
|
199
727
|
throw new Error("cloneRepo error");
|
|
200
728
|
}
|
|
201
729
|
}
|
|
202
|
-
async function
|
|
730
|
+
async function copyLocalTemplate(projectName) {
|
|
731
|
+
const projectPath = join3(process4.cwd(), projectName);
|
|
732
|
+
const sourcePath = await getTemplateBasePath();
|
|
733
|
+
await new Promise((resolve, reject) => {
|
|
734
|
+
const execStr = `cp -r "${sourcePath}/." "${projectPath}/"`;
|
|
735
|
+
exec(execStr, (error) => {
|
|
736
|
+
if (error) {
|
|
737
|
+
reject(error);
|
|
738
|
+
} else {
|
|
739
|
+
resolve();
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
return projectPath;
|
|
744
|
+
}
|
|
745
|
+
async function cloneRepoByBranch(root2, name, branch, options) {
|
|
203
746
|
try {
|
|
204
|
-
|
|
747
|
+
if (USE_LOCAL_TEMPLATE) {
|
|
748
|
+
console.log("\u4F7F\u7528\u672C\u5730\u6A21\u677F\u6D4B\u8BD5...");
|
|
749
|
+
await copyLocalTemplate(name);
|
|
750
|
+
} else {
|
|
751
|
+
console.log("\u4ECE Git \u514B\u9686\u57FA\u7840\u6A21\u677F...");
|
|
752
|
+
await cloneRepo(name, "base");
|
|
753
|
+
}
|
|
205
754
|
} catch (error) {
|
|
206
|
-
console.error(`${
|
|
207
|
-
|
|
755
|
+
console.error(`${red3(`\u6A21\u677F\u4E0B\u8F7D\u5931\u8D25\uFF01`)} ${error}`);
|
|
756
|
+
process4.exit(1);
|
|
208
757
|
}
|
|
209
758
|
const projectPath = join3(root2, name);
|
|
210
|
-
replacePackageJson(projectPath, name, "1.0.0");
|
|
759
|
+
replacePackageJson(projectPath, name, "1.0.0", options);
|
|
211
760
|
}
|
|
212
761
|
|
|
213
762
|
// src/utils/uiLibrary.ts
|
|
@@ -566,25 +1115,36 @@ function debug(...args) {
|
|
|
566
1115
|
}
|
|
567
1116
|
|
|
568
1117
|
// src/commands/create/generate.ts
|
|
569
|
-
import
|
|
570
|
-
var root =
|
|
1118
|
+
import path4 from "path";
|
|
1119
|
+
var root = process5.cwd();
|
|
571
1120
|
async function generateProject(options) {
|
|
572
1121
|
debug("generateProject options", options);
|
|
573
1122
|
const { projectName, platforms, uiLibrary, loginStrategy, i18n } = options;
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
debug("\
|
|
579
|
-
await
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
1123
|
+
const projectPath = path4.join(root, projectName);
|
|
1124
|
+
debug("\u62C9\u53D6 base \u5206\u652F");
|
|
1125
|
+
await cloneRepoByBranch(root, projectName, "base", options);
|
|
1126
|
+
if (i18n) {
|
|
1127
|
+
debug("\u6CE8\u5165 i18n feature");
|
|
1128
|
+
const results = await injectI18n(projectPath);
|
|
1129
|
+
for (const result of results) {
|
|
1130
|
+
if (result.success) {
|
|
1131
|
+
debug(result.message);
|
|
1132
|
+
} else {
|
|
1133
|
+
logger.warn(result.message);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
if (loginStrategy) {
|
|
1138
|
+
debug("\u6CE8\u5165 login feature");
|
|
1139
|
+
const results = await injectLogin(projectPath);
|
|
1140
|
+
for (const result of results) {
|
|
1141
|
+
if (result.success) {
|
|
1142
|
+
debug(result.message);
|
|
1143
|
+
} else {
|
|
1144
|
+
logger.warn(result.message);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
588
1148
|
if (uiLibrary === "none") {
|
|
589
1149
|
debug("\u4E0D\u5F15\u5165\u4EFB\u4F55UI\u5E93");
|
|
590
1150
|
} else {
|
|
@@ -597,8 +1157,18 @@ async function generateProject(options) {
|
|
|
597
1157
|
logger.info("\u60A8\u53EF\u4EE5\u5728\u9879\u76EE\u521B\u5EFA\u540E\u624B\u52A8\u914D\u7F6E UI \u5E93");
|
|
598
1158
|
}
|
|
599
1159
|
}
|
|
1160
|
+
const selectedFeatures = getSelectedFeatures(options);
|
|
1161
|
+
const allDeps = {};
|
|
1162
|
+
for (const feature of selectedFeatures) {
|
|
1163
|
+
if (feature.dependencies) {
|
|
1164
|
+
Object.assign(allDeps, feature.dependencies);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
if (Object.keys(allDeps).length > 0) {
|
|
1168
|
+
logger.info(`Feature \u4F9D\u8D56: ${Object.keys(allDeps).join(", ")}`);
|
|
1169
|
+
}
|
|
600
1170
|
try {
|
|
601
|
-
|
|
1171
|
+
log2.success(`\u9879\u76EE${projectName}\u521B\u5EFA\u6210\u529F\uFF01`);
|
|
602
1172
|
logger.info("\u4E0B\u4E00\u6B65:");
|
|
603
1173
|
logger.info(` cd ${projectName}`);
|
|
604
1174
|
logger.info(" pnpm i");
|
|
@@ -611,67 +1181,19 @@ async function generateProject(options) {
|
|
|
611
1181
|
}
|
|
612
1182
|
}
|
|
613
1183
|
|
|
614
|
-
// package.json
|
|
615
|
-
var version = "3.2.1";
|
|
616
|
-
var package_default = {
|
|
617
|
-
name: "create-unibest",
|
|
618
|
-
type: "module",
|
|
619
|
-
version,
|
|
620
|
-
packageManager: "pnpm@9.0.0",
|
|
621
|
-
description: "\u5FEB\u901F\u521B\u5EFAunibest\u9879\u76EE\u7684\u811A\u624B\u67B6\u5DE5\u5177",
|
|
622
|
-
author: "",
|
|
623
|
-
license: "ISC",
|
|
624
|
-
keywords: [],
|
|
625
|
-
main: "dist/index.js",
|
|
626
|
-
bin: {
|
|
627
|
-
best: "bin/index.js",
|
|
628
|
-
"create-unibest": "bin/index.js"
|
|
629
|
-
},
|
|
630
|
-
files: [
|
|
631
|
-
"bin",
|
|
632
|
-
"dist"
|
|
633
|
-
],
|
|
634
|
-
scripts: {
|
|
635
|
-
dev: "cross-env NODE_ENV=development tsup --watch",
|
|
636
|
-
build: "cross-env NODE_ENV=production tsup",
|
|
637
|
-
prepare: "cross-env NODE_ENV=production npm run build",
|
|
638
|
-
start: "cross-env NODE_ENV=development node bin/index.js"
|
|
639
|
-
},
|
|
640
|
-
dependencies: {
|
|
641
|
-
"@clack/prompts": "^0.11.0",
|
|
642
|
-
dayjs: "^1.11.18",
|
|
643
|
-
ejs: "^3.1.10",
|
|
644
|
-
"fs-extra": "^11.3.0",
|
|
645
|
-
kolorist: "^1.8.0",
|
|
646
|
-
minimist: "^1.2.8",
|
|
647
|
-
"node-fetch": "^3.3.2"
|
|
648
|
-
},
|
|
649
|
-
devDependencies: {
|
|
650
|
-
"@types/ejs": "^3.1.5",
|
|
651
|
-
"@types/fs-extra": "^11.0.4",
|
|
652
|
-
"@types/minimist": "^1.2.5",
|
|
653
|
-
"@types/node": "^24.5.0",
|
|
654
|
-
"cross-env": "^7.0.3",
|
|
655
|
-
tsup: "^8.5.0",
|
|
656
|
-
typescript: "^5.9.0"
|
|
657
|
-
}
|
|
658
|
-
};
|
|
659
|
-
|
|
660
1184
|
// src/commands/create.ts
|
|
661
|
-
import { intro, log as
|
|
662
|
-
import { bold as bold3, yellow as yellow3, green as
|
|
1185
|
+
import { intro as intro2, log as log3 } from "@clack/prompts";
|
|
1186
|
+
import { bold as bold3, yellow as yellow3, green as green4 } from "kolorist";
|
|
663
1187
|
|
|
664
1188
|
// src/utils/unibestVersion.ts
|
|
665
1189
|
import fetch from "node-fetch";
|
|
666
|
-
async function
|
|
1190
|
+
async function getUnibestVersionFromGitee() {
|
|
667
1191
|
try {
|
|
668
|
-
const apiUrl = `https://
|
|
1192
|
+
const apiUrl = `https://gitee.com/api/v5/repos/feige996/unibest/contents/package.json?ref=main`;
|
|
669
1193
|
const response = await fetch(apiUrl, {
|
|
670
1194
|
method: "GET",
|
|
671
1195
|
headers: {
|
|
672
|
-
"Content-Type": "application/json"
|
|
673
|
-
// GitHub API 要求 User-Agent 头
|
|
674
|
-
"User-Agent": "unibest-cli"
|
|
1196
|
+
"Content-Type": "application/json"
|
|
675
1197
|
}
|
|
676
1198
|
});
|
|
677
1199
|
if (response.ok) {
|
|
@@ -694,12 +1216,12 @@ async function getUnibestVersionFromGithub() {
|
|
|
694
1216
|
|
|
695
1217
|
// src/utils/beacon.ts
|
|
696
1218
|
import fetch2 from "node-fetch";
|
|
697
|
-
import
|
|
1219
|
+
import dayjs2 from "dayjs";
|
|
698
1220
|
import os from "os";
|
|
699
1221
|
import crypto from "crypto";
|
|
700
1222
|
async function beacon(options) {
|
|
701
1223
|
try {
|
|
702
|
-
const unibestVersion = await
|
|
1224
|
+
const unibestVersion = await getUnibestVersionFromGitee();
|
|
703
1225
|
const deviceIdentifier = generateDeviceIdentifier();
|
|
704
1226
|
await fetch2("https://ukw0y1.laf.run/create-unibest-v3/beacon", {
|
|
705
1227
|
method: "POST",
|
|
@@ -710,7 +1232,7 @@ async function beacon(options) {
|
|
|
710
1232
|
...options,
|
|
711
1233
|
version: unibestVersion,
|
|
712
1234
|
cbVersion: package_default.version,
|
|
713
|
-
createAt:
|
|
1235
|
+
createAt: dayjs2().format("YYYY-MM-DD HH:mm:ss"),
|
|
714
1236
|
nodeVersion: process.version,
|
|
715
1237
|
osPlatform: process.platform,
|
|
716
1238
|
cpuModel: os.cpus()[0]?.model || "unknown",
|
|
@@ -737,12 +1259,12 @@ function generateDeviceIdentifier() {
|
|
|
737
1259
|
// src/commands/create.ts
|
|
738
1260
|
async function createCommand(args) {
|
|
739
1261
|
const projectName = args._[1] || args._[0];
|
|
740
|
-
const versionUnibest = await
|
|
741
|
-
|
|
1262
|
+
const versionUnibest = await getUnibestVersionFromGitee() || "4.0.0";
|
|
1263
|
+
intro2(bold3(green4(`create-unibest@v${version} \u5FEB\u901F\u521B\u5EFA ${yellow3(`unibest@v${versionUnibest}`)} \u9879\u76EE`)));
|
|
742
1264
|
if (projectName) {
|
|
743
1265
|
const errorMessage = checkProjectNameExistAndValidate(projectName);
|
|
744
1266
|
if (errorMessage) {
|
|
745
|
-
|
|
1267
|
+
log3.error(errorMessage);
|
|
746
1268
|
process.exit(1);
|
|
747
1269
|
}
|
|
748
1270
|
}
|
|
@@ -751,18 +1273,18 @@ async function createCommand(args) {
|
|
|
751
1273
|
await generateProject(projectOptions);
|
|
752
1274
|
beacon(projectOptions);
|
|
753
1275
|
} catch (error) {
|
|
754
|
-
|
|
1276
|
+
log3.error(`\u521B\u5EFA\u9879\u76EE\u5931\u8D25: ${error.message}`);
|
|
755
1277
|
process.exit(1);
|
|
756
1278
|
}
|
|
757
1279
|
}
|
|
758
1280
|
|
|
759
1281
|
// src/utils/color.ts
|
|
760
|
-
import { blue, green as
|
|
1282
|
+
import { blue, green as green5, magenta as magenta2, red as red4, yellow as yellow4 } from "kolorist";
|
|
761
1283
|
var color = {
|
|
762
1284
|
blue,
|
|
763
|
-
green:
|
|
1285
|
+
green: green5,
|
|
764
1286
|
magenta: magenta2,
|
|
765
|
-
red:
|
|
1287
|
+
red: red4,
|
|
766
1288
|
yellow: yellow4
|
|
767
1289
|
};
|
|
768
1290
|
|
|
@@ -770,29 +1292,57 @@ var color = {
|
|
|
770
1292
|
function printHelp() {
|
|
771
1293
|
console.log(color.green("\ncreate-unibest - \u8DE8\u5E73\u53F0\u5F00\u53D1\u6846\u67B6\u811A\u624B\u67B6"));
|
|
772
1294
|
console.log("");
|
|
773
|
-
console.log(color.blue("\
|
|
774
|
-
console.log(color.green(" npm i -g create-unibest \u5168\u5C40\u5B89\u88C5\uFF0C\u5F97\u5230 best \u547D\u4EE4"));
|
|
775
|
-
console.log(color.green(" npm update -g create-unibest \u66F4\u65B0 create-unibest \u5305"));
|
|
776
|
-
console.log("");
|
|
777
|
-
console.log(color.green(" best <command> [options]"));
|
|
778
|
-
console.log(color.green(" best new my-project \u521B\u5EFA\u65B0\u7684unibest\u9879\u76EE"));
|
|
779
|
-
console.log(color.green(" best -v \u67E5\u770B\u7248\u672C\u4FE1\u606F"));
|
|
780
|
-
console.log(color.green(" best -h \u67E5\u770B\u5E2E\u52A9\u4FE1\u606F"));
|
|
781
|
-
console.log("");
|
|
782
|
-
console.log("");
|
|
783
|
-
console.log(color.blue("\u4E34\u65F6\u4F7F\u7528:"));
|
|
1295
|
+
console.log(color.blue("\u5FEB\u901F\u4F7F\u7528:"));
|
|
784
1296
|
console.log(color.green(" pnpm create unibest <command> [options]"));
|
|
785
|
-
console.log(color.green(" pnpm create unibest
|
|
1297
|
+
console.log(color.green(" pnpm create unibest my-project \u521B\u5EFA\u65B0\u7684unibest\u9879\u76EE"));
|
|
1298
|
+
console.log(color.green(" pnpm create unibest add i18n \u4E3A\u5DF2\u6709\u9879\u76EE\u6DFB\u52A0i18n\u7279\u6027"));
|
|
1299
|
+
console.log(color.green(" pnpm create unibest add login \u4E3A\u5DF2\u6709\u9879\u76EE\u6DFB\u52A0login\u7279\u6027"));
|
|
786
1300
|
console.log(color.green(" pnpm create unibest -v \u67E5\u770B\u7248\u672C\u4FE1\u606F"));
|
|
787
1301
|
console.log(color.green(" pnpm create unibest -h \u67E5\u770B\u5E2E\u52A9\u4FE1\u606F"));
|
|
788
1302
|
console.log("");
|
|
1303
|
+
console.log(color.blue("\u53EF\u7528\u547D\u4EE4:"));
|
|
1304
|
+
console.log(" <projectName> \u521B\u5EFA\u9879\u76EE\uFF08\u4E0D\u7528 new \u5173\u952E\u5B57\uFF09");
|
|
1305
|
+
console.log(" create <projectName> \u521B\u5EFA\u9879\u76EE");
|
|
1306
|
+
console.log(" add <feature> \u4E3A\u5DF2\u6709\u9879\u76EE\u6DFB\u52A0 Feature");
|
|
1307
|
+
console.log("");
|
|
1308
|
+
console.log(color.blue("\u521B\u5EFA\u9879\u76EE\u9009\u9879:"));
|
|
1309
|
+
console.log(" -p, --platform <type> \u6307\u5B9A\u5E73\u53F0 (h5, mp-weixin, app, mp-alipay, mp-toutiao)");
|
|
1310
|
+
console.log(" \u652F\u6301\u591A\u9009: -p h5,mp-weixin \u6216 -p h5 -p mp-weixin");
|
|
1311
|
+
console.log(" -u, --ui <library> \u6307\u5B9AUI\u5E93 (wot-ui, uview-pro, sard-uniapp, uv-ui, uview-plus, none)");
|
|
1312
|
+
console.log(" -l, --login \u662F\u5426\u9700\u8981\u767B\u5F55\u7B56\u7565");
|
|
1313
|
+
console.log(" -i, --i18n \u662F\u5426\u9700\u8981\u591A\u8BED\u8A00");
|
|
1314
|
+
console.log("");
|
|
1315
|
+
console.log(color.blue("\u6DFB\u52A0Feature\u9009\u9879:"));
|
|
1316
|
+
console.log(" <feature> \u8981\u6DFB\u52A0\u7684 Feature (i18n, login)");
|
|
1317
|
+
console.log(" --path <path> \u9879\u76EE\u8DEF\u5F84 (\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55)");
|
|
1318
|
+
console.log(" --force \u5F3A\u5236\u91CD\u65B0\u6CE8\u5165\uFF08\u8986\u76D6\u5DF2\u6709\u914D\u7F6E\uFF09");
|
|
1319
|
+
console.log("");
|
|
1320
|
+
console.log(color.blue("\u53EF\u7528Feature:"));
|
|
1321
|
+
console.log(" i18n \u591A\u8BED\u8A00\u652F\u6301");
|
|
1322
|
+
console.log(" login \u767B\u5F55\u7B56\u7565\uFF08\u9ED1\u767D\u540D\u5355\u3001\u767B\u5F55\u62E6\u622A\u7B49\uFF09");
|
|
1323
|
+
console.log("");
|
|
1324
|
+
console.log(color.blue("\u793A\u4F8B:"));
|
|
1325
|
+
console.log(" # \u521B\u5EFA\u9879\u76EE");
|
|
1326
|
+
console.log(" pnpm create unibest my-project -u wot-ui -p h5,mp-weixin");
|
|
1327
|
+
console.log(" pnpm create unibest my-project -u uview-plus -l -i");
|
|
1328
|
+
console.log(" pnpm create unibest my-project -u none -p h5,app,mp-weixin");
|
|
1329
|
+
console.log("");
|
|
1330
|
+
console.log(" # \u6DFB\u52A0 Feature");
|
|
1331
|
+
console.log(" cd my-project && pnpm create unibest add i18n");
|
|
1332
|
+
console.log(" pnpm create unibest add login --path /path/to/project");
|
|
1333
|
+
console.log(" pnpm create unibest add i18n login");
|
|
1334
|
+
console.log(" pnpm create unibest add i18n --force # \u91CD\u65B0\u6CE8\u5165");
|
|
1335
|
+
console.log("");
|
|
1336
|
+
console.log(" # \u5168\u5C40\u5B89\u88C5");
|
|
1337
|
+
console.log(" npm i -g create-unibest");
|
|
1338
|
+
console.log(" best my-project");
|
|
1339
|
+
console.log(" best add i18n");
|
|
1340
|
+
console.log("");
|
|
789
1341
|
}
|
|
790
1342
|
|
|
791
1343
|
// src/index.ts
|
|
792
|
-
import { green as green5 } from "kolorist";
|
|
793
|
-
import { yellow as yellow5 } from "kolorist";
|
|
794
1344
|
function main() {
|
|
795
|
-
const args = minimist(
|
|
1345
|
+
const args = minimist(process6.argv.slice(2));
|
|
796
1346
|
const command = args._[0];
|
|
797
1347
|
debug("command:", command);
|
|
798
1348
|
debug("args:", args);
|
|
@@ -809,6 +1359,9 @@ function main() {
|
|
|
809
1359
|
case "new":
|
|
810
1360
|
createCommand(args);
|
|
811
1361
|
break;
|
|
1362
|
+
case "add":
|
|
1363
|
+
addCommand(args);
|
|
1364
|
+
break;
|
|
812
1365
|
case "-h":
|
|
813
1366
|
case "--help":
|
|
814
1367
|
printHelp();
|
|
@@ -824,10 +1377,10 @@ function main() {
|
|
|
824
1377
|
}
|
|
825
1378
|
async function printVersion() {
|
|
826
1379
|
const cliVersion = version;
|
|
827
|
-
const latestVersion = await
|
|
1380
|
+
const latestVersion = await getUnibestVersionFromGitee();
|
|
828
1381
|
if (latestVersion && latestVersion !== cliVersion) {
|
|
829
|
-
console.log(`unibest-cli ${cliVersion} ${yellow5(`->`)} ${
|
|
830
|
-
console.log(`\u4F7F\u7528 ${
|
|
1382
|
+
console.log(`unibest-cli ${cliVersion} ${yellow5(`->`)} ${green6(`\u6700\u65B0\u7248\u672C: ${latestVersion}`)}`);
|
|
1383
|
+
console.log(`\u4F7F\u7528 ${green6(`npm update -g create-unibest`)} \u6216 ${green6(`pnpm add -g create-unibest`)} \u66F4\u65B0`);
|
|
831
1384
|
console.log();
|
|
832
1385
|
} else {
|
|
833
1386
|
console.log(`unibest-cli ${cliVersion}`);
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-unibest",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
5
|
-
"packageManager": "pnpm@
|
|
6
|
-
"description": "快速创建unibest项目的脚手架工具",
|
|
7
|
-
"author": "",
|
|
8
|
-
"license": "
|
|
9
|
-
"
|
|
10
|
-
"main": "dist/index.js",
|
|
4
|
+
"version": "4.0.0",
|
|
5
|
+
"packageManager": "pnpm@10.10.0",
|
|
6
|
+
"description": "快速创建 unibest 项目的脚手架工具",
|
|
7
|
+
"author": "feige996",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"homepage": "https://unibest.tech",
|
|
11
10
|
"bin": {
|
|
12
11
|
"best": "bin/index.js",
|
|
13
|
-
"create-unibest": "bin/index.js"
|
|
12
|
+
"create-unibest": "bin/index.js",
|
|
13
|
+
"unibest": "bin/index.js"
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"bin",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"dev": "cross-env NODE_ENV=development tsup --watch",
|
|
21
21
|
"build": "cross-env NODE_ENV=production tsup",
|
|
22
|
-
"prepare": "
|
|
22
|
+
"prepare": "pnpm build",
|
|
23
23
|
"start": "cross-env NODE_ENV=development node bin/index.js"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
package/dist/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|