create-unibest 3.3.0 → 4.0.1

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 CHANGED
@@ -1,63 +1,79 @@
1
- <h1 align="center">✨create unibest✨</h1>
1
+ # create-unibest
2
2
 
3
- <p align="center">
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
- <h2 align="center">
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
- `create-unibest` 是一个用于快速创建 `unibest` 项目的轻量脚手架工具,它可以帮助你快速创建一个基于 `vite` + `vue3` + `TS` 的 `uni-app` 项目,同时提供了一些模板供你选择。
12
+ ## 使用方式
15
13
 
16
- ## 🚤 快速使用
14
+ ### 安装
17
15
 
18
- ```shell
19
- pnpm create unibest <command> [options] # 基本命令格式
20
- pnpm create unibest my-project # 创建新的unibest项目
21
- pnpm create unibest -v # 查看版本信息
22
- pnpm create unibest -h # 查看帮助信息
23
- ```
16
+ ```bash
17
+ # 全局安装(可选)
18
+ npm i -g create-unibest
24
19
 
25
- ### 命令行参数
20
+ # 或直接使用
21
+ pnpm create unibest
22
+ ```
26
23
 
27
- 支持通过命令行参数跳过交互式询问,实现静默/快捷创建。
24
+ ### 创建项目
28
25
 
29
- | 参数 | 简写 | 说明 | 可选值 / 示例 |
30
- | :----------- | :--- | :----------- | :----------------------------------------------------------------------------------------------------- |
31
- | `--platform` | `-p` | 指定平台 | `h5`, `mp-weixin`, `app`, `mp-alipay`, `mp-toutiao`<br>示例: `-p h5,mp-weixin` 或 `-p h5 -p mp-weixin` |
32
- | `--ui` | `-u` | 指定 UI 库 | `wot-ui`, `uview-pro`, `sard-uniapp`, `uv-ui`, `uview-plus`, `none` |
33
- | `--login` | `-l` | 启用登录策略 | 无值,存在即开启 |
34
- | `--i18n` | `-i` | 启用多语言 | 无值,存在即开启 |
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
36
+ ```
35
37
 
36
- #### 示例
38
+ ### 添加 Feature
37
39
 
38
40
  ```bash
39
- # 1. 基础用法:指定 UI 库和平台(H5 + 微信小程序)
40
- pnpm create unibest my-project -u wot-ui -p h5,mp-weixin
41
+ cd my-project
42
+
43
+ # 添加多语言
44
+ pnpm create unibest add i18n
41
45
 
42
- # 2. 进阶用法:指定 UI 库,并开启登录策略和多语言
43
- pnpm create unibest my-project -u uview-plus -l -i
46
+ # 添加登录策略
47
+ pnpm create unibest add login
44
48
 
45
- # 3. 极简用法:不使用 UI 库,但支持多端(H5 + App + 微信小程序)
46
- pnpm create unibest my-project -u none -p h5,app,mp-weixin
49
+ # 强制重新注入(覆盖已有配置)
50
+ pnpm create unibest add i18n --force
47
51
  ```
48
52
 
49
- ### 全局安装(可选)
53
+ ## 参数说明
50
54
 
51
- ```shell
52
- npm i -g create-unibest # 全局安装,得到 best 命令
53
- npm update -g create-unibest # 更新 create-unibest 包
54
- ```
55
+ ### 创建项目参数
55
56
 
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` | 启用多语言 |
57
63
 
58
- ```shell
59
- best <command> [options] # 基本命令格式
60
- best my-project # 创建新的unibest项目
61
- best -v # 查看版本信息
62
- best -h # 查看帮助信息
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
@@ -6,7 +6,6 @@ import { fileURLToPath } from 'node:url'
6
6
  const __filename = fileURLToPath(import.meta.url)
7
7
  const __dirname = dirname(__filename)
8
8
 
9
- // 导入主程序
10
9
  import('../dist/index.js').catch((err) => {
11
10
  console.error('Failed to load CLI:', err)
12
11
  process.exit(1)
package/dist/index.js CHANGED
@@ -1,11 +1,258 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import process5 from "process";
4
+ import process6 from "process";
5
+ import { green as green6, yellow as yellow5 } from "kolorist";
5
6
  import minimist from "minimist";
6
7
 
7
- // src/commands/create/prompts.ts
8
- import { text, multiselect, select, confirm, cancel, isCancel } from "@clack/prompts";
8
+ // package.json
9
+ var version = "4.0.1";
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
+ "features"
28
+ ],
29
+ scripts: {
30
+ dev: "cross-env NODE_ENV=development tsup --watch",
31
+ build: "cross-env NODE_ENV=production tsup",
32
+ prepare: "pnpm build",
33
+ start: "cross-env NODE_ENV=development node bin/index.js"
34
+ },
35
+ dependencies: {
36
+ "@clack/prompts": "^0.11.0",
37
+ dayjs: "^1.11.18",
38
+ ejs: "^3.1.10",
39
+ "fs-extra": "^11.3.0",
40
+ kolorist: "^1.8.0",
41
+ minimist: "^1.2.8",
42
+ "node-fetch": "^3.3.2"
43
+ },
44
+ devDependencies: {
45
+ "@types/ejs": "^3.1.5",
46
+ "@types/fs-extra": "^11.0.4",
47
+ "@types/minimist": "^1.2.5",
48
+ "@types/node": "^24.5.0",
49
+ "cross-env": "^7.0.3",
50
+ tsup: "^8.5.0",
51
+ typescript: "^5.9.0"
52
+ }
53
+ };
54
+
55
+ // src/commands/add.ts
56
+ import fs4 from "fs";
57
+ import path3 from "path";
58
+ import process2 from "process";
59
+ import { cancel, intro, isCancel, log, multiselect } from "@clack/prompts";
60
+ import { bold as bold2, green as green2 } from "kolorist";
61
+
62
+ // src/features/interface.ts
63
+ var AVAILABLE_FEATURES = [
64
+ {
65
+ name: "i18n",
66
+ description: "\u591A\u8BED\u8A00\u652F\u6301",
67
+ dependencies: {
68
+ "vue-i18n": "^9.0.0",
69
+ "dayjs": "^1.11.0"
70
+ }
71
+ },
72
+ {
73
+ name: "login",
74
+ description: "\u767B\u5F55\u7B56\u7565\uFF08\u9ED1\u767D\u540D\u5355\u3001\u767B\u5F55\u62E6\u622A\u7B49\uFF09",
75
+ dependencies: {}
76
+ }
77
+ ];
78
+ function getFeatureByName(name) {
79
+ return AVAILABLE_FEATURES.find((f) => f.name === name);
80
+ }
81
+ function getSelectedFeatures(options) {
82
+ const features = [];
83
+ if (options.i18n) {
84
+ const feature = getFeatureByName("i18n");
85
+ if (feature) features.push(feature);
86
+ }
87
+ if (options.loginStrategy) {
88
+ const feature = getFeatureByName("login");
89
+ if (feature) features.push(feature);
90
+ }
91
+ return features;
92
+ }
93
+
94
+ // src/features/loader.ts
95
+ import fs from "fs";
96
+ import path from "path";
97
+ import { fileURLToPath } from "url";
98
+ var __filename = fileURLToPath(import.meta.url);
99
+ var __dirname = path.dirname(__filename);
100
+ var FEATURES_DIR = path.resolve(__dirname, "../../features");
101
+ async function loadFeatureHooks(featureName) {
102
+ const hooksPath = path.join(FEATURES_DIR, featureName, "hooks.ts");
103
+ if (!fs.existsSync(hooksPath)) {
104
+ return null;
105
+ }
106
+ const module = await import(hooksPath);
107
+ return module;
108
+ }
109
+ function getAvailableFeatureNames() {
110
+ if (!fs.existsSync(FEATURES_DIR)) {
111
+ return [];
112
+ }
113
+ const entries = fs.readdirSync(FEATURES_DIR, { withFileTypes: true });
114
+ return entries.filter((e) => e.isDirectory() && fs.existsSync(path.join(FEATURES_DIR, e.name, "files"))).map((e) => e.name);
115
+ }
116
+
117
+ // src/utils/injector.ts
118
+ import fs2 from "fs";
119
+ import path2 from "path";
120
+ import { dirname } from "path";
121
+ import { fileURLToPath as fileURLToPath2 } from "url";
122
+ var __filename2 = fileURLToPath2(import.meta.url);
123
+ var __dirname2 = dirname(__filename2);
124
+ var FEATURES_PATH = path2.join(__dirname2, "..", "..", "..", "features");
125
+ var FeatureInjector = class {
126
+ constructor(projectPath) {
127
+ this.projectPath = projectPath;
128
+ }
129
+ injectFile(targetPath, featureCode, placeholder) {
130
+ const fullPath = path2.join(this.projectPath, targetPath);
131
+ if (!fs2.existsSync(fullPath)) {
132
+ return { success: false, message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${targetPath}` };
133
+ }
134
+ const content = fs2.readFileSync(fullPath, "utf-8");
135
+ if (!content.includes(placeholder)) {
136
+ return { success: false, message: `\u5360\u4F4D\u7B26\u4E0D\u5B58\u5728: ${placeholder}` };
137
+ }
138
+ const newContent = content.replace(placeholder, featureCode);
139
+ fs2.writeFileSync(fullPath, newContent);
140
+ return { success: true, message: `\u6CE8\u5165\u6210\u529F: ${targetPath}` };
141
+ }
142
+ replaceFile(targetPath, featureFilePath) {
143
+ const targetFullPath = path2.join(this.projectPath, targetPath);
144
+ const featureFullPath = path2.join(FEATURES_PATH, featureFilePath);
145
+ if (!fs2.existsSync(featureFullPath)) {
146
+ return { success: false, message: `Feature \u6587\u4EF6\u4E0D\u5B58\u5728: ${featureFilePath}` };
147
+ }
148
+ const featureContent = fs2.readFileSync(featureFullPath, "utf-8");
149
+ fs2.writeFileSync(targetFullPath, featureContent);
150
+ return { success: true, message: `\u66FF\u6362\u6210\u529F: ${targetPath}` };
151
+ }
152
+ createFile(relativePath, featureFilePath) {
153
+ const targetFullPath = path2.join(this.projectPath, relativePath);
154
+ const featureFullPath = path2.join(FEATURES_PATH, featureFilePath);
155
+ if (!fs2.existsSync(featureFullPath)) {
156
+ return { success: false, message: `Feature \u6587\u4EF6\u4E0D\u5B58\u5728: ${featureFilePath}` };
157
+ }
158
+ const dir = path2.dirname(targetFullPath);
159
+ if (!fs2.existsSync(dir)) {
160
+ fs2.mkdirSync(dir, { recursive: true });
161
+ }
162
+ const featureContent = fs2.readFileSync(featureFullPath, "utf-8");
163
+ fs2.writeFileSync(targetFullPath, featureContent);
164
+ return { success: true, message: `\u521B\u5EFA\u6210\u529F: ${relativePath}` };
165
+ }
166
+ appendAfter(targetPath, marker, code) {
167
+ const fullPath = path2.join(this.projectPath, targetPath);
168
+ if (!fs2.existsSync(fullPath)) {
169
+ return { success: false, message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${targetPath}` };
170
+ }
171
+ const content = fs2.readFileSync(fullPath, "utf-8");
172
+ if (!content.includes(marker)) {
173
+ return { success: false, message: `\u6807\u8BB0\u4E0D\u5B58\u5728: ${marker}` };
174
+ }
175
+ const newContent = content.replace(marker, `${marker}
176
+ ${code}`);
177
+ fs2.writeFileSync(fullPath, newContent);
178
+ return { success: true, message: `\u8FFD\u52A0\u6210\u529F: ${targetPath}` };
179
+ }
180
+ };
181
+ async function injectI18n(projectPath) {
182
+ const results = [];
183
+ const injector = new FeatureInjector(projectPath);
184
+ results.push(injector.appendAfter(
185
+ "src/main.ts",
186
+ `import 'virtual:uno.css'`,
187
+ `import i18n from './locale/index'`
188
+ ));
189
+ results.push(injector.appendAfter(
190
+ "src/main.ts",
191
+ ` app.use(requestInterceptor)`,
192
+ ` app.use(i18n)`
193
+ ));
194
+ results.push(injector.replaceFile(
195
+ "src/tabbar/config.ts",
196
+ "i18n/files/src/tabbar/config.ts"
197
+ ));
198
+ results.push(injector.replaceFile(
199
+ "src/tabbar/index.vue",
200
+ "i18n/files/src/tabbar/index.vue"
201
+ ));
202
+ results.push(injector.replaceFile(
203
+ "src/tabbar/TabbarItem.vue",
204
+ "i18n/files/src/tabbar/TabbarItem.vue"
205
+ ));
206
+ results.push(injector.replaceFile(
207
+ "src/utils/index.ts",
208
+ "i18n/files/src/utils/index.ts"
209
+ ));
210
+ results.push(injector.replaceFile(
211
+ "src/store/token.ts",
212
+ "i18n/files/src/store/token.ts"
213
+ ));
214
+ const i18nFiles = [
215
+ "src/locale/index.ts",
216
+ "src/locale/en.json",
217
+ "src/locale/zh-Hans.json",
218
+ "src/locale/README.md",
219
+ "src/utils/i18n.ts",
220
+ "src/types/i18n.d.ts",
221
+ "src/tabbar/i18n.ts",
222
+ "src/pages/i18n/index.vue"
223
+ ];
224
+ for (const file of i18nFiles) {
225
+ const featurePath = `i18n/files/${file}`;
226
+ results.push(injector.createFile(file, featurePath));
227
+ }
228
+ return results;
229
+ }
230
+ async function injectLogin(projectPath) {
231
+ const results = [];
232
+ const injector = new FeatureInjector(projectPath);
233
+ results.push(injector.replaceFile(
234
+ "src/router/interceptor.ts",
235
+ "login/files/src/router/interceptor.ts"
236
+ ));
237
+ results.push(injector.replaceFile(
238
+ "src/router/config.ts",
239
+ "login/files/src/router/config.ts"
240
+ ));
241
+ results.push(injector.replaceFile(
242
+ "src/pages/me/me.vue",
243
+ "login/files/src/pages/me.vue"
244
+ ));
245
+ const loginFiles = [
246
+ "src/pages/auth/login.vue",
247
+ "src/pages/auth/register.vue",
248
+ "src/pages/auth/README.md"
249
+ ];
250
+ for (const file of loginFiles) {
251
+ const featurePath = `login/files/${file}`;
252
+ results.push(injector.createFile(file, featurePath));
253
+ }
254
+ return results;
255
+ }
9
256
 
10
257
  // src/utils/logger.ts
11
258
  import { bold, green, red, yellow, cyan } from "kolorist";
@@ -32,6 +279,178 @@ var logger = {
32
279
  }
33
280
  };
34
281
 
282
+ // src/utils/readPackageJson.ts
283
+ import fs3 from "fs";
284
+ function readPackageJson(pkgPath) {
285
+ if (!fs3.existsSync(pkgPath)) {
286
+ throw new Error(`package.json not found: ${pkgPath}`);
287
+ }
288
+ const content = fs3.readFileSync(pkgPath, "utf-8");
289
+ return JSON.parse(content);
290
+ }
291
+ function writePackageJson(pkgPath, pkg) {
292
+ fs3.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
293
+ `);
294
+ }
295
+
296
+ // src/commands/add.ts
297
+ function getFeatureStatusFromPackageJson(pkgPath) {
298
+ try {
299
+ const pkg = readPackageJson(pkgPath);
300
+ return {
301
+ i18n: pkg.unibest?.i18n === true,
302
+ login: pkg.unibest?.loginStrategy === true
303
+ };
304
+ } catch {
305
+ return { i18n: false, login: false };
306
+ }
307
+ }
308
+ function updatePackageJsonForFeature(pkgPath, featureName) {
309
+ const pkg = readPackageJson(pkgPath);
310
+ if (!pkg.unibest) {
311
+ pkg.unibest = {};
312
+ }
313
+ switch (featureName) {
314
+ case "i18n":
315
+ pkg.unibest.i18n = true;
316
+ break;
317
+ case "login":
318
+ pkg.unibest.loginStrategy = true;
319
+ break;
320
+ }
321
+ writePackageJson(pkgPath, pkg);
322
+ }
323
+ async function checkFeatureStatus(projectPath) {
324
+ const pkgPath = path3.join(projectPath, "package.json");
325
+ const statusFromPkg = getFeatureStatusFromPackageJson(pkgPath);
326
+ return [
327
+ { name: "i18n", enabled: statusFromPkg.i18n },
328
+ { name: "login", enabled: statusFromPkg.login }
329
+ ];
330
+ }
331
+ async function addFeature(featureName, projectPath, options = {}) {
332
+ const feature = getFeatureByName(featureName);
333
+ if (!feature) {
334
+ logger.error(`\u672A\u77E5\u7684 Feature: ${featureName}`);
335
+ return false;
336
+ }
337
+ const pkgPath = path3.join(projectPath, "package.json");
338
+ const pkg = readPackageJson(pkgPath);
339
+ let alreadyAdded = false;
340
+ if (featureName === "i18n" && pkg.unibest?.i18n === true) {
341
+ alreadyAdded = true;
342
+ } else if (featureName === "login" && pkg.unibest?.loginStrategy === true) {
343
+ alreadyAdded = true;
344
+ }
345
+ if (alreadyAdded && !options.force) {
346
+ logger.warn(`Feature ${featureName} \u5DF2\u6DFB\u52A0\u8FC7\uFF0C\u5982\u9700\u91CD\u65B0\u6CE8\u5165\u8BF7\u4F7F\u7528 --force \u53C2\u6570`);
347
+ return true;
348
+ }
349
+ log.info(`\u6B63\u5728\u6DFB\u52A0 Feature: ${green2(featureName)} - ${feature.description}`);
350
+ try {
351
+ let results;
352
+ switch (featureName) {
353
+ case "i18n":
354
+ results = await injectI18n(projectPath);
355
+ break;
356
+ case "login":
357
+ results = await injectLogin(projectPath);
358
+ break;
359
+ default:
360
+ logger.error(`\u4E0D\u652F\u6301\u7684 Feature: ${featureName}`);
361
+ return false;
362
+ }
363
+ for (const result of results) {
364
+ if (result.success) {
365
+ logger.success(result.message);
366
+ } else {
367
+ logger.warn(result.message);
368
+ }
369
+ }
370
+ const hooks = await loadFeatureHooks(featureName);
371
+ if (hooks?.postApply) {
372
+ await hooks.postApply({
373
+ options: { projectName: "", platforms: [], uiLibrary: "none", i18n: true, loginStrategy: true },
374
+ projectPath,
375
+ featureName
376
+ });
377
+ }
378
+ updatePackageJsonForFeature(pkgPath, featureName);
379
+ logger.success(`\u5DF2\u66F4\u65B0 package.json \u7684 unibest \u914D\u7F6E`);
380
+ if (feature.dependencies && Object.keys(feature.dependencies).length > 0) {
381
+ logger.info(`\u5B89\u88C5\u4F9D\u8D56: ${Object.keys(feature.dependencies).join(", ")}`);
382
+ }
383
+ logger.success(`Feature ${featureName} \u6DFB\u52A0\u6210\u529F\uFF01`);
384
+ return true;
385
+ } catch (error) {
386
+ logger.error(`\u6DFB\u52A0 Feature \u5931\u8D25: ${error.message}`);
387
+ return false;
388
+ }
389
+ }
390
+ async function addCommand(args) {
391
+ const options = {
392
+ path: args.path || args.p || ".",
393
+ feature: args._[1] || args.feature || args.f,
394
+ force: args.force || args.f
395
+ };
396
+ intro(bold2(green2(`create-unibest@v${version} \u6DFB\u52A0 Feature`)));
397
+ const projectPath = path3.isAbsolute(options.path) ? options.path : path3.join(process2.cwd(), options.path);
398
+ const pkgPath = path3.join(projectPath, "package.json");
399
+ if (!fs4.existsSync(pkgPath)) {
400
+ logger.error(`\u9879\u76EE\u4E0D\u5B58\u5728: ${projectPath}`);
401
+ process2.exit(1);
402
+ }
403
+ const pkg = readPackageJson(pkgPath);
404
+ if (pkg.name !== "unibest") {
405
+ logger.warn(`\u5F53\u524D\u9879\u76EE\u53EF\u80FD\u4E0D\u662F unibest \u9879\u76EE: ${pkg.name}`);
406
+ }
407
+ const availableFeatures = getAvailableFeatureNames();
408
+ try {
409
+ if (!options.feature) {
410
+ const detected = await checkFeatureStatus(projectPath);
411
+ const available = availableFeatures.filter((f) => !detected.find((d) => d.name === f && d.enabled));
412
+ if (available.length === 0) {
413
+ logger.info("\u6240\u6709\u53EF\u7528 Feature \u5DF2\u542F\u7528");
414
+ return;
415
+ }
416
+ const selectedFeatures = await multiselect({
417
+ message: `\u8BF7\u9009\u62E9\u8981\u6DFB\u52A0\u7684 Feature`,
418
+ options: available.map((name) => {
419
+ const feature = getFeatureByName(name);
420
+ return {
421
+ value: name,
422
+ label: feature?.name || name,
423
+ hint: feature?.description || ""
424
+ };
425
+ }),
426
+ required: false
427
+ });
428
+ if (isCancel(selectedFeatures)) {
429
+ cancel("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
430
+ process2.exit(0);
431
+ }
432
+ if (!Array.isArray(selectedFeatures) || selectedFeatures.length === 0) {
433
+ logger.info("\u672A\u9009\u62E9\u4EFB\u4F55 Feature");
434
+ return;
435
+ }
436
+ for (const featureName of selectedFeatures) {
437
+ await addFeature(featureName, projectPath, options);
438
+ }
439
+ } else {
440
+ const features = Array.isArray(options.feature) ? options.feature : [options.feature];
441
+ for (const featureName of features) {
442
+ await addFeature(featureName, projectPath, options);
443
+ }
444
+ }
445
+ } catch (error) {
446
+ logger.error(`\u6DFB\u52A0 Feature \u5931\u8D25: ${error.message}`);
447
+ process2.exit(1);
448
+ }
449
+ }
450
+
451
+ // src/commands/create/prompts.ts
452
+ import { text, multiselect as multiselect2, select, confirm, cancel as cancel2, isCancel as isCancel2 } from "@clack/prompts";
453
+
35
454
  // src/utils/validate.ts
36
455
  import { existsSync } from "fs";
37
456
  import { yellow as yellow2 } from "kolorist";
@@ -58,8 +477,8 @@ function checkProjectNameExistAndValidate(_projectName) {
58
477
  }
59
478
 
60
479
  // src/commands/create/prompts.ts
61
- import { green as green2, red as red2 } from "kolorist";
62
- import process2 from "process";
480
+ import { green as green3, red as red2 } from "kolorist";
481
+ import process3 from "process";
63
482
  var VALID_PLATFORMS = ["h5", "mp-weixin", "app", "mp-alipay", "mp-toutiao"];
64
483
  var VALID_UI_LIBRARIES = ["none", "wot-ui", "uview-pro", "sard-uniapp", "uv-ui", "uview-plus"];
65
484
  async function promptUser(projectName, argv = {}) {
@@ -79,7 +498,7 @@ async function promptUser(projectName, argv = {}) {
79
498
  if (invalidPlatforms.length > 0) {
80
499
  console.error(red2(`\u65E0\u6548\u7684\u5E73\u53F0\u53C2\u6570: ${invalidPlatforms.join(", ")}`));
81
500
  console.error(red2(`\u53EF\u9009\u503C: ${VALID_PLATFORMS.join(", ")}`));
82
- process2.exit(1);
501
+ process3.exit(1);
83
502
  }
84
503
  platforms = parsedPlatforms;
85
504
  }
@@ -88,20 +507,26 @@ async function promptUser(projectName, argv = {}) {
88
507
  if (!VALID_UI_LIBRARIES.includes(uiArg)) {
89
508
  console.error(red2(`\u65E0\u6548\u7684UI\u5E93\u53C2\u6570: ${uiArg}`));
90
509
  console.error(red2(`\u53EF\u9009\u503C: ${VALID_UI_LIBRARIES.join(", ")}`));
91
- process2.exit(1);
510
+ process3.exit(1);
92
511
  }
93
512
  uiLibrary = uiArg;
94
513
  }
95
- if (argv.l || argv.login) {
514
+ const loginArg = argv.l ?? argv.login;
515
+ if (loginArg === true || loginArg === "true") {
96
516
  loginStrategy = true;
517
+ } else if (loginArg === false || loginArg === "false") {
518
+ loginStrategy = false;
97
519
  }
98
- if (argv.i || argv.i18n) {
520
+ const i18nArg = argv.i ?? argv.i18n;
521
+ if (i18nArg === true || i18nArg === "true") {
99
522
  i18n = true;
523
+ } else if (i18nArg === false || i18nArg === "false") {
524
+ i18n = false;
100
525
  }
101
526
  try {
102
527
  if (!projectName) {
103
528
  const inputProjectName = await text({
104
- message: `\u8BF7\u8F93\u5165\u9879\u76EE\u540D\u79F0${green2("[\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]")}`,
529
+ 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]")}`,
105
530
  initialValue: "",
106
531
  validate: (value) => {
107
532
  if (!value.trim()) return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
@@ -110,15 +535,15 @@ async function promptUser(projectName, argv = {}) {
110
535
  return;
111
536
  }
112
537
  });
113
- if (isCancel(inputProjectName)) {
114
- cancel("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
115
- process2.exit(0);
538
+ if (isCancel2(inputProjectName)) {
539
+ cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
540
+ process3.exit(0);
116
541
  }
117
542
  projectName = inputProjectName;
118
543
  }
119
544
  if (!platforms) {
120
- const selectedPlatforms = await multiselect({
121
- message: `\u8BF7\u9009\u62E9\u9700\u8981\u652F\u6301\u7684\u5E73\u53F0\uFF08\u591A\u9009\uFF09${green2("[\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]")}`,
545
+ const selectedPlatforms = await multiselect2({
546
+ 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]")}`,
122
547
  options: [
123
548
  { value: "h5", label: "H5" },
124
549
  { value: "mp-weixin", label: "\u5FAE\u4FE1\u5C0F\u7A0B\u5E8F" },
@@ -130,9 +555,9 @@ async function promptUser(projectName, argv = {}) {
130
555
  // 默认选择 H5
131
556
  required: true
132
557
  });
133
- if (isCancel(selectedPlatforms)) {
134
- cancel("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
135
- process2.exit(0);
558
+ if (isCancel2(selectedPlatforms)) {
559
+ cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
560
+ process3.exit(0);
136
561
  }
137
562
  platforms = selectedPlatforms;
138
563
  }
@@ -149,20 +574,20 @@ async function promptUser(projectName, argv = {}) {
149
574
  ],
150
575
  initialValue: "none"
151
576
  });
152
- if (isCancel(selectedUiLibrary)) {
153
- cancel("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
154
- process2.exit(0);
577
+ if (isCancel2(selectedUiLibrary)) {
578
+ cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
579
+ process3.exit(0);
155
580
  }
156
581
  uiLibrary = selectedUiLibrary;
157
582
  }
158
583
  if (loginStrategy === void 0) {
159
584
  const selectedLoginStrategy = await confirm({
160
- message: `\u662F\u5426\u9700\u8981\u767B\u5F55\u7B56\u7565\uFF08\u9ED1\u767D\u540D\u5355\u3001\u767B\u5F55\u62E6\u622A\u7B49\uFF09\uFF1F${green2("[\u6682\u4E0D\u77E5\u9053\u7684\uFF0C\u9009No\u5373\u53EF\uFF0C\u9879\u76EE\u751F\u6210\u540E\u4E5F\u53EF\u4EE5\u52A0\u8BE5\u7B56\u7565]")}`,
585
+ 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]")}`,
161
586
  initialValue: false
162
587
  });
163
- if (isCancel(selectedLoginStrategy)) {
164
- cancel("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
165
- process2.exit(0);
588
+ if (isCancel2(selectedLoginStrategy)) {
589
+ cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
590
+ process3.exit(0);
166
591
  }
167
592
  loginStrategy = selectedLoginStrategy;
168
593
  }
@@ -171,9 +596,9 @@ async function promptUser(projectName, argv = {}) {
171
596
  message: "\u662F\u5426\u9700\u8981\u591A\u8BED\u8A00i18n\uFF1F",
172
597
  initialValue: false
173
598
  });
174
- if (isCancel(selectedI18n)) {
175
- cancel("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
176
- process2.exit(0);
599
+ if (isCancel2(selectedI18n)) {
600
+ cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
601
+ process3.exit(0);
177
602
  }
178
603
  i18n = selectedI18n;
179
604
  }
@@ -189,73 +614,25 @@ async function promptUser(projectName, argv = {}) {
189
614
  };
190
615
  } catch (error) {
191
616
  logger.error(`\u8BE2\u95EE\u8FC7\u7A0B\u51FA\u9519: ${error.message}`);
192
- process2.exit(1);
617
+ process3.exit(1);
193
618
  }
194
619
  }
195
620
 
196
621
  // src/commands/create/generate.ts
197
- import process4 from "process";
198
- import { log } from "@clack/prompts";
622
+ import process5 from "process";
623
+ import { log as log2 } from "@clack/prompts";
199
624
 
200
625
  // src/utils/cloneRepo.ts
201
626
  import { exec } from "child_process";
202
- import { promises as fs } from "fs";
203
- import { join as join3 } from "path";
204
- import process3 from "process";
627
+ import { promises as fsPromises } from "fs";
628
+ import { join as join3, dirname as dirname2 } from "path";
629
+ import { fileURLToPath as fileURLToPath3 } from "url";
630
+ import process4 from "process";
205
631
  import { red as red3 } from "kolorist";
206
632
 
207
633
  // src/utils/replacePackageJson.ts
208
634
  import { readFileSync, writeFileSync } from "fs";
209
635
  import { join as join2 } from "path";
210
-
211
- // package.json
212
- var version = "3.3.0";
213
- var package_default = {
214
- name: "create-unibest",
215
- type: "module",
216
- version,
217
- updateTime: "2026-01-19",
218
- packageManager: "pnpm@9.0.0",
219
- description: "\u5FEB\u901F\u521B\u5EFAunibest\u9879\u76EE\u7684\u811A\u624B\u67B6\u5DE5\u5177",
220
- author: "",
221
- license: "ISC",
222
- keywords: [],
223
- main: "dist/index.js",
224
- bin: {
225
- best: "bin/index.js",
226
- "create-unibest": "bin/index.js"
227
- },
228
- files: [
229
- "bin",
230
- "dist"
231
- ],
232
- scripts: {
233
- dev: "cross-env NODE_ENV=development tsup --watch",
234
- build: "cross-env NODE_ENV=production tsup",
235
- prepare: "cross-env NODE_ENV=production npm run build",
236
- start: "cross-env NODE_ENV=development node bin/index.js"
237
- },
238
- dependencies: {
239
- "@clack/prompts": "^0.11.0",
240
- dayjs: "^1.11.18",
241
- ejs: "^3.1.10",
242
- "fs-extra": "^11.3.0",
243
- kolorist: "^1.8.0",
244
- minimist: "^1.2.8",
245
- "node-fetch": "^3.3.2"
246
- },
247
- devDependencies: {
248
- "@types/ejs": "^3.1.5",
249
- "@types/fs-extra": "^11.0.4",
250
- "@types/minimist": "^1.2.5",
251
- "@types/node": "^24.5.0",
252
- "cross-env": "^7.0.3",
253
- tsup: "^8.5.0",
254
- typescript: "^5.9.0"
255
- }
256
- };
257
-
258
- // src/utils/replacePackageJson.ts
259
636
  import dayjs from "dayjs";
260
637
  function replaceContent(filePath, projectName, version2, options) {
261
638
  const fileContent = JSON.parse(readFileSync(filePath, "utf8"));
@@ -267,6 +644,21 @@ function replaceContent(filePath, projectName, version2, options) {
267
644
  delete fileContent.name;
268
645
  delete fileContent.version;
269
646
  const { projectName: _, ...restOptions } = options;
647
+ const selectedFeatures = getSelectedFeatures(options);
648
+ const featureDeps = {};
649
+ for (const feature of selectedFeatures) {
650
+ if (feature.dependencies) {
651
+ Object.assign(featureDeps, feature.dependencies);
652
+ }
653
+ }
654
+ if (!fileContent.dependencies) {
655
+ fileContent.dependencies = {};
656
+ }
657
+ for (const [pkg, ver] of Object.entries(featureDeps)) {
658
+ if (!fileContent.dependencies[pkg]) {
659
+ fileContent.dependencies[pkg] = ver;
660
+ }
661
+ }
270
662
  const newContent = {
271
663
  name: projectName,
272
664
  type: fileContent.type,
@@ -291,9 +683,28 @@ function replacePackageJson(root2, name, version2, options) {
291
683
  }
292
684
 
293
685
  // src/utils/cloneRepo.ts
686
+ var __filename3 = fileURLToPath3(import.meta.url);
687
+ var __dirname3 = dirname2(__filename3);
688
+ var USE_LOCAL_TEMPLATE = process4.env.LOCAL_TEMPLATE === "true";
689
+ var TEMPLATE_BASE_PATH = null;
690
+ async function getTemplateBasePath() {
691
+ if (TEMPLATE_BASE_PATH) return TEMPLATE_BASE_PATH;
692
+ const candidates = [
693
+ join3(__dirname3, "..", "..", "..", "packages", "template-base"),
694
+ join3(__dirname3, "..", "..", "packages", "template-base")
695
+ ];
696
+ const { existsSync: existsSync3 } = await import("fs");
697
+ for (const candidate of candidates) {
698
+ if (existsSync3(candidate)) {
699
+ TEMPLATE_BASE_PATH = candidate;
700
+ return candidate;
701
+ }
702
+ }
703
+ throw new Error("\u65E0\u6CD5\u627E\u5230 template-base \u76EE\u5F55");
704
+ }
294
705
  async function removeGitFolder(localPath) {
295
706
  const gitFolderPath = join3(localPath, ".git");
296
- await fs.rm(gitFolderPath, { recursive: true, force: true });
707
+ await fsPromises.rm(gitFolderPath, { recursive: true, force: true });
297
708
  }
298
709
  var REPO_URL = "https://gitee.com/feige996/unibest.git";
299
710
  async function cloneRepo(projectName, branch) {
@@ -320,12 +731,33 @@ async function cloneRepo(projectName, branch) {
320
731
  throw new Error("cloneRepo error");
321
732
  }
322
733
  }
734
+ async function copyLocalTemplate(projectName) {
735
+ const projectPath = join3(process4.cwd(), projectName);
736
+ const sourcePath = await getTemplateBasePath();
737
+ await new Promise((resolve, reject) => {
738
+ const execStr = `cp -r "${sourcePath}/." "${projectPath}/"`;
739
+ exec(execStr, (error) => {
740
+ if (error) {
741
+ reject(error);
742
+ } else {
743
+ resolve();
744
+ }
745
+ });
746
+ });
747
+ return projectPath;
748
+ }
323
749
  async function cloneRepoByBranch(root2, name, branch, options) {
324
750
  try {
325
- await cloneRepo(name, branch);
751
+ if (USE_LOCAL_TEMPLATE) {
752
+ console.log("\u4F7F\u7528\u672C\u5730\u6A21\u677F\u6D4B\u8BD5...");
753
+ await copyLocalTemplate(name);
754
+ } else {
755
+ console.log("\u4ECE Git \u514B\u9686\u57FA\u7840\u6A21\u677F...");
756
+ await cloneRepo(name, "base");
757
+ }
326
758
  } catch (error) {
327
- console.error(`${red3(`\u6A21\u677F\u7C7B\u578B${branch}\u4E0B\u8F7D\u5931\u8D25\uFF01`)} ${error}`);
328
- process3.exit(1);
759
+ console.error(`${red3(`\u6A21\u677F\u4E0B\u8F7D\u5931\u8D25\uFF01`)} ${error}`);
760
+ process4.exit(1);
329
761
  }
330
762
  const projectPath = join3(root2, name);
331
763
  replacePackageJson(projectPath, name, "1.0.0", options);
@@ -687,25 +1119,36 @@ function debug(...args) {
687
1119
  }
688
1120
 
689
1121
  // src/commands/create/generate.ts
690
- import path from "path";
691
- var root = process4.cwd();
1122
+ import path4 from "path";
1123
+ var root = process5.cwd();
692
1124
  async function generateProject(options) {
693
1125
  debug("generateProject options", options);
694
1126
  const { projectName, platforms, uiLibrary, loginStrategy, i18n } = options;
695
- if (!loginStrategy && !i18n) {
696
- debug("\u62C9\u53D6 base \u5206\u652F");
697
- await cloneRepoByBranch(root, projectName, "base", options);
698
- } else if (!loginStrategy && i18n) {
699
- debug("\u62C9\u53D6 base-i18n \u5206\u652F");
700
- await cloneRepoByBranch(root, projectName, "base-i18n", options);
701
- } else if (loginStrategy && !i18n) {
702
- debug("\u62C9\u53D6 base-login \u5206\u652F");
703
- await cloneRepoByBranch(root, projectName, "base-login", options);
704
- } else if (loginStrategy && i18n) {
705
- debug("\u62C9\u53D6 base-login-i18n \u5206\u652F");
706
- await cloneRepoByBranch(root, projectName, "base-login-i18n", options);
707
- }
708
- const projectPath = path.join(root, projectName);
1127
+ const projectPath = path4.join(root, projectName);
1128
+ debug("\u62C9\u53D6 base \u5206\u652F");
1129
+ await cloneRepoByBranch(root, projectName, "base", options);
1130
+ if (i18n) {
1131
+ debug("\u6CE8\u5165 i18n feature");
1132
+ const results = await injectI18n(projectPath);
1133
+ for (const result of results) {
1134
+ if (result.success) {
1135
+ debug(result.message);
1136
+ } else {
1137
+ logger.warn(result.message);
1138
+ }
1139
+ }
1140
+ }
1141
+ if (loginStrategy) {
1142
+ debug("\u6CE8\u5165 login feature");
1143
+ const results = await injectLogin(projectPath);
1144
+ for (const result of results) {
1145
+ if (result.success) {
1146
+ debug(result.message);
1147
+ } else {
1148
+ logger.warn(result.message);
1149
+ }
1150
+ }
1151
+ }
709
1152
  if (uiLibrary === "none") {
710
1153
  debug("\u4E0D\u5F15\u5165\u4EFB\u4F55UI\u5E93");
711
1154
  } else {
@@ -718,8 +1161,18 @@ async function generateProject(options) {
718
1161
  logger.info("\u60A8\u53EF\u4EE5\u5728\u9879\u76EE\u521B\u5EFA\u540E\u624B\u52A8\u914D\u7F6E UI \u5E93");
719
1162
  }
720
1163
  }
1164
+ const selectedFeatures = getSelectedFeatures(options);
1165
+ const allDeps = {};
1166
+ for (const feature of selectedFeatures) {
1167
+ if (feature.dependencies) {
1168
+ Object.assign(allDeps, feature.dependencies);
1169
+ }
1170
+ }
1171
+ if (Object.keys(allDeps).length > 0) {
1172
+ logger.info(`Feature \u4F9D\u8D56: ${Object.keys(allDeps).join(", ")}`);
1173
+ }
721
1174
  try {
722
- log.success(`\u9879\u76EE${projectName}\u521B\u5EFA\u6210\u529F\uFF01`);
1175
+ log2.success(`\u9879\u76EE${projectName}\u521B\u5EFA\u6210\u529F\uFF01`);
723
1176
  logger.info("\u4E0B\u4E00\u6B65:");
724
1177
  logger.info(` cd ${projectName}`);
725
1178
  logger.info(" pnpm i");
@@ -733,8 +1186,8 @@ async function generateProject(options) {
733
1186
  }
734
1187
 
735
1188
  // src/commands/create.ts
736
- import { intro, log as log2 } from "@clack/prompts";
737
- import { bold as bold2, yellow as yellow3, green as green3 } from "kolorist";
1189
+ import { intro as intro2, log as log3 } from "@clack/prompts";
1190
+ import { bold as bold3, yellow as yellow3, green as green4 } from "kolorist";
738
1191
 
739
1192
  // src/utils/unibestVersion.ts
740
1193
  import fetch from "node-fetch";
@@ -811,11 +1264,11 @@ function generateDeviceIdentifier() {
811
1264
  async function createCommand(args) {
812
1265
  const projectName = args._[1] || args._[0];
813
1266
  const versionUnibest = await getUnibestVersionFromGitee() || "4.0.0";
814
- intro(bold2(green3(`create-unibest@v${version} \u5FEB\u901F\u521B\u5EFA ${yellow3(`unibest@v${versionUnibest}`)} \u9879\u76EE`)));
1267
+ intro2(bold3(green4(`create-unibest@v${version} \u5FEB\u901F\u521B\u5EFA ${yellow3(`unibest@v${versionUnibest}`)} \u9879\u76EE`)));
815
1268
  if (projectName) {
816
1269
  const errorMessage = checkProjectNameExistAndValidate(projectName);
817
1270
  if (errorMessage) {
818
- log2.error(errorMessage);
1271
+ log3.error(errorMessage);
819
1272
  process.exit(1);
820
1273
  }
821
1274
  }
@@ -824,16 +1277,16 @@ async function createCommand(args) {
824
1277
  await generateProject(projectOptions);
825
1278
  beacon(projectOptions);
826
1279
  } catch (error) {
827
- log2.error(`\u521B\u5EFA\u9879\u76EE\u5931\u8D25: ${error.message}`);
1280
+ log3.error(`\u521B\u5EFA\u9879\u76EE\u5931\u8D25: ${error.message}`);
828
1281
  process.exit(1);
829
1282
  }
830
1283
  }
831
1284
 
832
1285
  // src/utils/color.ts
833
- import { blue, green as green4, magenta as magenta2, red as red4, yellow as yellow4 } from "kolorist";
1286
+ import { blue, green as green5, magenta as magenta2, red as red4, yellow as yellow4 } from "kolorist";
834
1287
  var color = {
835
1288
  blue,
836
- green: green4,
1289
+ green: green5,
837
1290
  magenta: magenta2,
838
1291
  red: red4,
839
1292
  yellow: yellow4
@@ -846,33 +1299,54 @@ function printHelp() {
846
1299
  console.log(color.blue("\u5FEB\u901F\u4F7F\u7528:"));
847
1300
  console.log(color.green(" pnpm create unibest <command> [options]"));
848
1301
  console.log(color.green(" pnpm create unibest my-project \u521B\u5EFA\u65B0\u7684unibest\u9879\u76EE"));
1302
+ console.log(color.green(" pnpm create unibest add i18n \u4E3A\u5DF2\u6709\u9879\u76EE\u6DFB\u52A0i18n\u7279\u6027"));
1303
+ console.log(color.green(" pnpm create unibest add login \u4E3A\u5DF2\u6709\u9879\u76EE\u6DFB\u52A0login\u7279\u6027"));
849
1304
  console.log(color.green(" pnpm create unibest -v \u67E5\u770B\u7248\u672C\u4FE1\u606F"));
850
1305
  console.log(color.green(" pnpm create unibest -h \u67E5\u770B\u5E2E\u52A9\u4FE1\u606F"));
851
1306
  console.log("");
852
- console.log(color.blue("\u9009\u9879:"));
1307
+ console.log(color.blue("\u53EF\u7528\u547D\u4EE4:"));
1308
+ console.log(" <projectName> \u521B\u5EFA\u9879\u76EE\uFF08\u4E0D\u7528 new \u5173\u952E\u5B57\uFF09");
1309
+ console.log(" create <projectName> \u521B\u5EFA\u9879\u76EE");
1310
+ console.log(" add <feature> \u4E3A\u5DF2\u6709\u9879\u76EE\u6DFB\u52A0 Feature");
1311
+ console.log("");
1312
+ console.log(color.blue("\u521B\u5EFA\u9879\u76EE\u9009\u9879:"));
853
1313
  console.log(" -p, --platform <type> \u6307\u5B9A\u5E73\u53F0 (h5, mp-weixin, app, mp-alipay, mp-toutiao)");
854
1314
  console.log(" \u652F\u6301\u591A\u9009: -p h5,mp-weixin \u6216 -p h5 -p mp-weixin");
855
1315
  console.log(" -u, --ui <library> \u6307\u5B9AUI\u5E93 (wot-ui, uview-pro, sard-uniapp, uv-ui, uview-plus, none)");
856
1316
  console.log(" -l, --login \u662F\u5426\u9700\u8981\u767B\u5F55\u7B56\u7565");
857
1317
  console.log(" -i, --i18n \u662F\u5426\u9700\u8981\u591A\u8BED\u8A00");
858
1318
  console.log("");
1319
+ console.log(color.blue("\u6DFB\u52A0Feature\u9009\u9879:"));
1320
+ console.log(" <feature> \u8981\u6DFB\u52A0\u7684 Feature (i18n, login)");
1321
+ console.log(" --path <path> \u9879\u76EE\u8DEF\u5F84 (\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55)");
1322
+ console.log(" --force \u5F3A\u5236\u91CD\u65B0\u6CE8\u5165\uFF08\u8986\u76D6\u5DF2\u6709\u914D\u7F6E\uFF09");
1323
+ console.log("");
1324
+ console.log(color.blue("\u53EF\u7528Feature:"));
1325
+ console.log(" i18n \u591A\u8BED\u8A00\u652F\u6301");
1326
+ console.log(" login \u767B\u5F55\u7B56\u7565\uFF08\u9ED1\u767D\u540D\u5355\u3001\u767B\u5F55\u62E6\u622A\u7B49\uFF09");
1327
+ console.log("");
859
1328
  console.log(color.blue("\u793A\u4F8B:"));
1329
+ console.log(" # \u521B\u5EFA\u9879\u76EE");
860
1330
  console.log(" pnpm create unibest my-project -u wot-ui -p h5,mp-weixin");
861
1331
  console.log(" pnpm create unibest my-project -u uview-plus -l -i");
862
1332
  console.log(" pnpm create unibest my-project -u none -p h5,app,mp-weixin");
863
1333
  console.log("");
864
- console.log(color.blue("\u5168\u5C40\u5B89\u88C5 (\u53EF\u9009):"));
865
- console.log(color.green(" npm i -g create-unibest \u5168\u5C40\u5B89\u88C5\uFF0C\u5F97\u5230 best \u547D\u4EE4"));
866
- console.log(color.green(" best <command> [options]"));
867
- console.log(color.green(" best my-project \u521B\u5EFA\u65B0\u7684unibest\u9879\u76EE"));
1334
+ console.log(" # \u6DFB\u52A0 Feature");
1335
+ console.log(" cd my-project && pnpm create unibest add i18n");
1336
+ console.log(" pnpm create unibest add login --path /path/to/project");
1337
+ console.log(" pnpm create unibest add i18n login");
1338
+ console.log(" pnpm create unibest add i18n --force # \u91CD\u65B0\u6CE8\u5165");
1339
+ console.log("");
1340
+ console.log(" # \u5168\u5C40\u5B89\u88C5");
1341
+ console.log(" npm i -g create-unibest");
1342
+ console.log(" best my-project");
1343
+ console.log(" best add i18n");
868
1344
  console.log("");
869
1345
  }
870
1346
 
871
1347
  // src/index.ts
872
- import { green as green5 } from "kolorist";
873
- import { yellow as yellow5 } from "kolorist";
874
1348
  function main() {
875
- const args = minimist(process5.argv.slice(2));
1349
+ const args = minimist(process6.argv.slice(2));
876
1350
  const command = args._[0];
877
1351
  debug("command:", command);
878
1352
  debug("args:", args);
@@ -889,6 +1363,9 @@ function main() {
889
1363
  case "new":
890
1364
  createCommand(args);
891
1365
  break;
1366
+ case "add":
1367
+ addCommand(args);
1368
+ break;
892
1369
  case "-h":
893
1370
  case "--help":
894
1371
  printHelp();
@@ -906,8 +1383,8 @@ async function printVersion() {
906
1383
  const cliVersion = version;
907
1384
  const latestVersion = await getUnibestVersionFromGitee();
908
1385
  if (latestVersion && latestVersion !== cliVersion) {
909
- console.log(`unibest-cli ${cliVersion} ${yellow5(`->`)} ${green5(`\u6700\u65B0\u7248\u672C: ${latestVersion}`)}`);
910
- console.log(`\u4F7F\u7528 ${green5(`npm update -g create-unibest`)} \u6216 ${green5(`pnpm add -g create-unibest`)} \u66F4\u65B0`);
1386
+ console.log(`unibest-cli ${cliVersion} ${yellow5(`->`)} ${green6(`\u6700\u65B0\u7248\u672C: ${latestVersion}`)}`);
1387
+ console.log(`\u4F7F\u7528 ${green6(`npm update -g create-unibest`)} \u6216 ${green6(`pnpm add -g create-unibest`)} \u66F4\u65B0`);
911
1388
  console.log();
912
1389
  } else {
913
1390
  console.log(`unibest-cli ${cliVersion}`);
package/package.json CHANGED
@@ -1,26 +1,26 @@
1
1
  {
2
2
  "name": "create-unibest",
3
3
  "type": "module",
4
- "version": "3.3.0",
5
- "updateTime": "2026-01-19",
6
- "packageManager": "pnpm@9.0.0",
7
- "description": "快速创建unibest项目的脚手架工具",
8
- "author": "",
9
- "license": "ISC",
10
- "keywords": [],
11
- "main": "dist/index.js",
4
+ "version": "4.0.1",
5
+ "packageManager": "pnpm@10.10.0",
6
+ "description": "快速创建 unibest 项目的脚手架工具",
7
+ "author": "feige996",
8
+ "license": "MIT",
9
+ "homepage": "https://unibest.tech",
12
10
  "bin": {
13
11
  "best": "bin/index.js",
14
- "create-unibest": "bin/index.js"
12
+ "create-unibest": "bin/index.js",
13
+ "unibest": "bin/index.js"
15
14
  },
16
15
  "files": [
17
16
  "bin",
18
- "dist"
17
+ "dist",
18
+ "features"
19
19
  ],
20
20
  "scripts": {
21
21
  "dev": "cross-env NODE_ENV=development tsup --watch",
22
22
  "build": "cross-env NODE_ENV=production tsup",
23
- "prepare": "cross-env NODE_ENV=production npm run build",
23
+ "prepare": "pnpm build",
24
24
  "start": "cross-env NODE_ENV=development node bin/index.js"
25
25
  },
26
26
  "dependencies": {
package/dist/index.d.ts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env node