create-whale-env 1.0.0 → 1.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 ADDED
@@ -0,0 +1,150 @@
1
+ # create-whale-env
2
+
3
+ 交互式创建 / 追加 / 编辑 / 卸载 `app-env` 下的公司(corp)环境配置骨架。
4
+
5
+ 生成目录示例:
6
+
7
+ ```text
8
+ app-env/src/corp/<corp-code>/
9
+ ├── build.json
10
+ └── <project>/ # officeAutomatic / driver / ...
11
+ ├── env/
12
+ └── manifest/
13
+ ```
14
+
15
+ ## 要求
16
+
17
+ - Node.js `>= 18`
18
+ - 在 `app-env` 目录(或含有 `src/corp` 的目录)下执行
19
+
20
+ ## 快速开始
21
+
22
+ ```bash
23
+ # 推荐:在 app-env 下执行
24
+ cd app-env
25
+ pnpm create whale-env
26
+ # 或
27
+ npm create whale-env
28
+ yarn create whale-env
29
+ ```
30
+
31
+ 也可显式安装后再跑:
32
+
33
+ ```bash
34
+ pnpm add create-whale-env
35
+ pnpm exec create-whale-env
36
+ ```
37
+
38
+ 带公司编码:
39
+
40
+ ```bash
41
+ pnpm create whale-env demo001
42
+ ```
43
+
44
+ 不传编码时,会先交互询问公司编码。
45
+
46
+ ## 卸载
47
+
48
+ ```bash
49
+ # 在 app-env 下
50
+ pnpm exec create-whale-env uninstall
51
+ pnpm exec create-whale-env uninstall demo001
52
+
53
+ # 跳过确认,直接删整个公司
54
+ pnpm exec create-whale-env uninstall demo001 --force
55
+ ```
56
+
57
+ 卸载时可选择:
58
+
59
+ - **卸载整个公司**:删除 `src/corp/<corp-code>/`
60
+ - **卸载部分业务端**:多选后只删对应端目录;若端被删光,会一并删除公司目录
61
+
62
+ ## 命令参数
63
+
64
+ ### 创建 / 编辑
65
+
66
+ ```bash
67
+ pnpm create whale-env [corp-code] [options]
68
+ # 或
69
+ pnpm exec create-whale-env [corp-code] [options]
70
+ ```
71
+
72
+ | 参数 / 选项 | 说明 |
73
+ |---|---|
74
+ | `corp-code` | 公司编码(目录名 / `build.json.value`),可省略 |
75
+ | `--root <path>` | 指定 `app-env` 根目录;默认从当前目录向上查找含 `src/corp` 的目录 |
76
+ | `--force` | 公司已存在时直接走编辑模式,跳过写入方式选择 |
77
+ | `-h, --help` | 帮助 |
78
+ | `-v, --version` | 版本 |
79
+
80
+ ### 卸载
81
+
82
+ ```bash
83
+ pnpm exec create-whale-env uninstall [corp-code] [options]
84
+ ```
85
+
86
+ | 参数 / 选项 | 说明 |
87
+ |---|---|
88
+ | `corp-code` | 公司编码,可省略(交互输入) |
89
+ | `--root <path>` | 指定 `app-env` 根目录 |
90
+ | `--force` | 跳过确认,直接卸载整个公司 |
91
+
92
+ 公司编码规则:字母开头,仅含字母 / 数字 / `_` / `-`,不能以下划线开头。
93
+
94
+ ## 交互流程
95
+
96
+ 1. **公司编码**(未传参时询问)
97
+ 2. 若公司已存在:展示已有业务端
98
+ 3. **公司中文名**
99
+ - 新建:直接填写
100
+ - 已存在:选择「保持原名 / 重命名」
101
+ 4. 若公司已存在:选择写入方式
102
+ - **追加新端**:只生成尚未开通的端
103
+ - **编辑已有的端**:回填已有配置后修改(全部端都已开通时不出现「追加」)
104
+ - **取消**
105
+ 5. **多选业务端**(空格勾选,回车确认)
106
+ 6. 按端填写配置(有默认值显示「回车保留」,非必填空值显示「回车跳过」)
107
+
108
+ ### 可开通业务端
109
+
110
+ | 显示名 | 目录名 | CLIENT |
111
+ |---|---|---|
112
+ | 移动OA | `officeAutomatic` | `EMPLOYEE` |
113
+ | 司机端 | `driver` | `DRIVER` |
114
+ | 冶金仓储 | `steelWare` | `EMPLOYEE` |
115
+ | 供应商端 | `vendor` | `VENDOR` |
116
+ | 客户端 | `customer` | `CUSTOMER` |
117
+ | 生产商端 | `manufactUser` | `MANUFACTURER` |
118
+ | 自定义端 | `portal` | (空) |
119
+
120
+ ### 每端会询问的字段
121
+
122
+ | 字段 | 必填 | 写入位置(示意) |
123
+ |---|---|---|
124
+ | DOMAIN | 是 | `env/base.ts` |
125
+ | App 显示名 | 是 | `manifest/*.manifest.json` → `name` |
126
+ | WECHAT_APPID | 否 | `env/base.ts` / `env/wechat/base.ts` |
127
+ | WORK_WECHAT_APPID | 否 | 同上 |
128
+ | WECHAT_SERVICE_APPID | 否 | 同上 |
129
+ | uni-app appid | 否 | `manifest` → `appid`(空则占位) |
130
+ | DCloud AppKey | 否 | `env/android|ios/base.ts` |
131
+ | 集团域名 | 否 | `env/group*.ts`(默认占位域名) |
132
+
133
+ 每个业务端会生成完整 `env`(含 wechat / android / ios × 各环境)+ 三份 manifest。
134
+
135
+ ## 本地开发
136
+
137
+ ```bash
138
+ cd app-cli/packages/env
139
+ pnpm install
140
+ pnpm dev -- demo001 --root ../../../app-env
141
+ # 或
142
+ pnpm build && node ./dist/cli.js demo001 --root ../../../app-env
143
+ # 卸载本地验证
144
+ pnpm build && node ./dist/cli.js uninstall demo001 --root ../../../app-env
145
+ ```
146
+
147
+ ## 说明
148
+
149
+ - `loginConf` / `pages` / `userCenter` 等在 `app-env/src/app/config/<project>/`,按业务端共享,本工具不生成。
150
+ - 生成后执行 `app-env` 的 `pnpm build`,新公司会出现在公司选择列表中。
package/dist/cli.js CHANGED
@@ -2,14 +2,13 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { cac } from "cac";
5
- import { consola as consola3 } from "consola";
5
+ import { consola as consola4 } from "consola";
6
6
 
7
7
  // src/commands/init.ts
8
8
  import { checkbox, input, select } from "@inquirer/prompts";
9
9
  import { consola as consola2 } from "consola";
10
- import { colors as colors2 } from "consola/utils";
11
- import fs4 from "fs";
12
- import path4 from "path";
10
+ import fs5 from "fs";
11
+ import path5 from "path";
13
12
 
14
13
  // src/constants.ts
15
14
  var PROJECTS = [
@@ -97,19 +96,19 @@ function renderTypesTs() {
97
96
  }
98
97
  `;
99
98
  }
100
- function renderBaseTs(input2) {
99
+ function renderBaseTs(input3) {
101
100
  const lines = [
102
- `import { getAppConfEnv } from '../../../../app/config/${input2.project}'`,
101
+ `import { getAppConfEnv } from '../../../../app/config/${input3.project}'`,
103
102
  "",
104
103
  "export default{",
105
104
  " ...getAppConfEnv(),",
106
- ` CLIENT: '${input2.client}', // \u5BA2\u6237\u7AEF\u7C7B\u578B`,
107
- ` DOMAIN:'${input2.domain}',//\u57DF\u540D`,
108
- ` WECHAT_APPID:'${input2.wechatAppId}',//\u5FAE\u4FE1`,
109
- ` WORK_WECHAT_APPID:'${input2.workWechatAppId}',//\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528ID`
105
+ ` CLIENT: '${input3.client}', // \u5BA2\u6237\u7AEF\u7C7B\u578B`,
106
+ ` DOMAIN:'${input3.domain}',//\u57DF\u540D`,
107
+ ` WECHAT_APPID:'${input3.wechatAppId}',//\u5FAE\u4FE1`,
108
+ ` WORK_WECHAT_APPID:'${input3.workWechatAppId}',//\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528ID`
110
109
  ];
111
- if (input2.wechatServiceAppId) {
112
- lines.push(` WECHAT_SERVICE_APPID:'${input2.wechatServiceAppId}',//\u516C\u4F17\u53F7/\u670D\u52A1\u53F7appid`);
110
+ if (input3.wechatServiceAppId) {
111
+ lines.push(` WECHAT_SERVICE_APPID:'${input3.wechatServiceAppId}',//\u516C\u4F17\u53F7/\u670D\u52A1\u53F7appid`);
113
112
  }
114
113
  lines.push("}");
115
114
  lines.push("");
@@ -129,17 +128,17 @@ export default{
129
128
  ${domainLine}}
130
129
  `;
131
130
  }
132
- function renderPlatformBaseTs(platform, input2) {
131
+ function renderPlatformBaseTs(platform, input3) {
133
132
  const factory = platformFactoryName(platform);
134
133
  if (platform === "wechat") {
135
134
  const body = [
136
135
  " ...options,",
137
- ` WECHAT_APPID:'${input2.wechatAppId}',//\u5FAE\u4FE1`,
138
- ` WORK_WECHAT_APPID:'${input2.workWechatAppId}',//\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528ID`
136
+ ` WECHAT_APPID:'${input3.wechatAppId}',//\u5FAE\u4FE1`,
137
+ ` WORK_WECHAT_APPID:'${input3.workWechatAppId}',//\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528ID`
139
138
  ];
140
- if (input2.wechatServiceAppId) {
139
+ if (input3.wechatServiceAppId) {
141
140
  body.push(
142
- ` WECHAT_SERVICE_APPID:'${input2.wechatServiceAppId}',//\u516C\u4F17\u53F7/\u670D\u52A1\u53F7appid`
141
+ ` WECHAT_SERVICE_APPID:'${input3.wechatServiceAppId}',//\u516C\u4F17\u53F7/\u670D\u52A1\u53F7appid`
143
142
  );
144
143
  }
145
144
  return `import type { EnvOptions } from '../types'
@@ -156,7 +155,7 @@ export default ${factory}
156
155
 
157
156
  export const ${factory} = (options:EnvOptions)=>{
158
157
  return {
159
- DCLOUD_APP_KEY_IOS:'${input2.dcloudAppKey}',
158
+ DCLOUD_APP_KEY_IOS:'${input3.dcloudAppKey}',
160
159
  ...options,
161
160
  }
162
161
  }
@@ -173,19 +172,19 @@ export default ${factory}({
173
172
  })
174
173
  `;
175
174
  }
176
- function buildEnvFileMap(input2) {
175
+ function buildEnvFileMap(input3) {
177
176
  const files = {
178
177
  "types.ts": renderTypesTs(),
179
- "base.ts": renderBaseTs(input2)
178
+ "base.ts": renderBaseTs(input3)
180
179
  };
181
180
  for (const env of ENV_NAMES) {
182
181
  files[`${env}.ts`] = renderRootEnvTs(env);
183
182
  }
184
183
  for (const env of GROUP_ENV_NAMES) {
185
- files[`${env}.ts`] = renderRootEnvTs(env, input2.groupDomain);
184
+ files[`${env}.ts`] = renderRootEnvTs(env, input3.groupDomain);
186
185
  }
187
186
  for (const platform of CLIENT_PLATFORMS) {
188
- files[`${platform}/base.ts`] = renderPlatformBaseTs(platform, input2);
187
+ files[`${platform}/base.ts`] = renderPlatformBaseTs(platform, input3);
189
188
  for (const env of [...ENV_NAMES, ...GROUP_ENV_NAMES]) {
190
189
  files[`${platform}/${env}.ts`] = renderPlatformEnvTs(platform, env);
191
190
  }
@@ -198,11 +197,11 @@ function fallbackUniAppId(project) {
198
197
  const seed = project.replace(/[^a-zA-Z0-9]/g, "").toUpperCase().slice(0, 7).padEnd(7, "0");
199
198
  return `__UNI__${seed}`;
200
199
  }
201
- function renderManifest(input2) {
202
- const uniAppId = input2.uniAppId || fallbackUniAppId(input2.project);
203
- const wechatAppId = input2.wechatAppId || "";
200
+ function renderManifest(input3) {
201
+ const uniAppId = input3.uniAppId || fallbackUniAppId(input3.project);
202
+ const wechatAppId = input3.wechatAppId || "";
204
203
  return `{
205
- "name" : ${JSON.stringify(input2.appName)},
204
+ "name" : ${JSON.stringify(input3.appName)},
206
205
  "appid" : ${JSON.stringify(uniAppId)},
207
206
  "description" : "",
208
207
  "versionName" : "1.0.0",
@@ -279,8 +278,8 @@ function renderManifest(input2) {
279
278
  }
280
279
  `;
281
280
  }
282
- function buildManifestFileMap(input2) {
283
- const content = renderManifest(input2);
281
+ function buildManifestFileMap(input3) {
282
+ const content = renderManifest(input3);
284
283
  const files = {};
285
284
  for (const platform of CLIENT_PLATFORMS) {
286
285
  files[`${platform}.manifest.json`] = content;
@@ -293,13 +292,13 @@ async function writeFile(filePath, content) {
293
292
  await fs2.mkdir(path2.dirname(filePath), { recursive: true });
294
293
  await fs2.writeFile(filePath, content, "utf8");
295
294
  }
296
- async function generateCorp(appEnvRoot, input2) {
297
- const corpDir = getCorpDir(appEnvRoot, input2.corpCode);
295
+ async function generateCorp(appEnvRoot, input3) {
296
+ const corpDir = getCorpDir(appEnvRoot, input3.corpCode);
298
297
  await fs2.mkdir(corpDir, { recursive: true });
299
298
  const buildJson = JSON.stringify(
300
299
  {
301
- name: input2.corpName,
302
- value: input2.corpCode
300
+ name: input3.corpName,
301
+ value: input3.corpCode
303
302
  },
304
303
  null,
305
304
  2
@@ -307,7 +306,7 @@ async function generateCorp(appEnvRoot, input2) {
307
306
  await writeFile(path2.join(corpDir, "build.json"), `${buildJson}
308
307
  `);
309
308
  consola.success(`\u5199\u5165 ${path2.relative(appEnvRoot, path2.join(corpDir, "build.json"))}`);
310
- for (const project of input2.projects) {
309
+ for (const project of input3.projects) {
311
310
  const projectDir = path2.join(corpDir, project.project);
312
311
  const envDir = path2.join(projectDir, "env");
313
312
  const manifestDir = path2.join(projectDir, "manifest");
@@ -326,6 +325,11 @@ async function generateCorp(appEnvRoot, input2) {
326
325
  return corpDir;
327
326
  }
328
327
 
328
+ // src/utils/corp.ts
329
+ import fs3 from "fs";
330
+ import path3 from "path";
331
+ import { colors as colors2 } from "consola/utils";
332
+
329
333
  // src/utils/noticeBox.ts
330
334
  import stringWidth from "string-width";
331
335
  import { colors } from "consola/utils";
@@ -369,12 +373,53 @@ function printSectionBox(message) {
369
373
  printNoticeBox(message, [], { minWidth: 36, borderColor: "cyan" });
370
374
  }
371
375
 
376
+ // src/utils/corp.ts
377
+ function listExistingProjects(corpDir) {
378
+ if (!fs3.existsSync(corpDir)) return [];
379
+ return fs3.readdirSync(corpDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name).filter((name2) => PROJECTS.some((p) => p.value === name2));
380
+ }
381
+ function getProjectLabel(value) {
382
+ return getProjectMeta(value)?.name.replace(/[【】]/g, "") || value;
383
+ }
384
+ function formatProjectLabels(projectValues) {
385
+ return projectValues.map((value) => getProjectLabel(value)).join("\u3001");
386
+ }
387
+ function readCorpName(corpDir, fallback = "") {
388
+ const buildJsonPath = path3.join(corpDir, "build.json");
389
+ if (!fs3.existsSync(buildJsonPath)) return fallback;
390
+ try {
391
+ const data = JSON.parse(fs3.readFileSync(buildJsonPath, "utf8"));
392
+ return data.name?.trim() || fallback;
393
+ } catch {
394
+ return fallback;
395
+ }
396
+ }
397
+ function printCorpNotice(title, corpCode, corpDir, existing) {
398
+ const corpName = readCorpName(corpDir);
399
+ const body = [
400
+ `${colors2.bold("\u516C\u53F8\u7F16\u7801")} ${colors2.cyan(corpCode)}`
401
+ ];
402
+ if (corpName) {
403
+ body.push(`${colors2.bold("\u516C\u53F8\u540D\u79F0")} ${colors2.white(corpName)}`);
404
+ }
405
+ if (existing.length) {
406
+ body.push(
407
+ `${colors2.bold("\u5DF2\u6709\u7AEF")} ${colors2.dim(`${existing.length} \u4E2A`)}`,
408
+ "",
409
+ ...existing.map(
410
+ (value) => `${colors2.green("\u2714")} ${colors2.white(getProjectLabel(value))}`
411
+ )
412
+ );
413
+ }
414
+ printNoticeBox(title, body);
415
+ }
416
+
372
417
  // src/utils/readExistingProject.ts
373
- import fs3 from "fs";
374
- import path3 from "path";
418
+ import fs4 from "fs";
419
+ import path4 from "path";
375
420
  function readText(filePath) {
376
- if (!fs3.existsSync(filePath)) return "";
377
- return fs3.readFileSync(filePath, "utf8");
421
+ if (!fs4.existsSync(filePath)) return "";
422
+ return fs4.readFileSync(filePath, "utf8");
378
423
  }
379
424
  function pickString(source, key) {
380
425
  const re = new RegExp(
@@ -395,14 +440,14 @@ function pickJsonString(source, key) {
395
440
  }
396
441
  }
397
442
  function readExistingProjectConfig(corpDir, project) {
398
- const projectDir = path3.join(corpDir, project);
399
- if (!fs3.existsSync(projectDir)) return {};
400
- const baseTs = readText(path3.join(projectDir, "env/base.ts"));
401
- const wechatBaseTs = readText(path3.join(projectDir, "env/wechat/base.ts"));
402
- const androidBaseTs = readText(path3.join(projectDir, "env/android/base.ts"));
403
- const groupDevTs = readText(path3.join(projectDir, "env/groupDev.ts"));
443
+ const projectDir = path4.join(corpDir, project);
444
+ if (!fs4.existsSync(projectDir)) return {};
445
+ const baseTs = readText(path4.join(projectDir, "env/base.ts"));
446
+ const wechatBaseTs = readText(path4.join(projectDir, "env/wechat/base.ts"));
447
+ const androidBaseTs = readText(path4.join(projectDir, "env/android/base.ts"));
448
+ const groupDevTs = readText(path4.join(projectDir, "env/groupDev.ts"));
404
449
  const manifestRaw = readText(
405
- path3.join(projectDir, "manifest/wechat.manifest.json")
450
+ path4.join(projectDir, "manifest/wechat.manifest.json")
406
451
  );
407
452
  return {
408
453
  domain: pickString(baseTs, "DOMAIN"),
@@ -417,41 +462,6 @@ function readExistingProjectConfig(corpDir, project) {
417
462
  }
418
463
 
419
464
  // src/commands/init.ts
420
- function listExistingProjects(corpDir) {
421
- if (!fs4.existsSync(corpDir)) return [];
422
- return fs4.readdirSync(corpDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name).filter((name2) => PROJECTS.some((p) => p.value === name2));
423
- }
424
- function formatProjectLabels(projectValues) {
425
- return projectValues.map((value) => getProjectMeta(value)?.name.replace(/[【】]/g, "") || value).join("\u3001");
426
- }
427
- function getProjectLabel(value) {
428
- return getProjectMeta(value)?.name.replace(/[【】]/g, "") || value;
429
- }
430
- function printExistingCorpNotice(corpCode, existing) {
431
- const body = [
432
- `${colors2.bold("\u516C\u53F8\u7F16\u7801")} ${colors2.cyan(corpCode)}`
433
- ];
434
- if (existing.length) {
435
- body.push(
436
- `${colors2.bold("\u5DF2\u6709\u7AEF")} ${colors2.dim(`${existing.length} \u4E2A`)}`,
437
- "",
438
- ...existing.map(
439
- (value) => `${colors2.green("\u2714")} ${colors2.white(getProjectLabel(value))}`
440
- )
441
- );
442
- }
443
- printNoticeBox("\u516C\u53F8\u5DF2\u5B58\u5728", body);
444
- }
445
- function readCorpName(corpDir, fallback = "") {
446
- const buildJsonPath = path4.join(corpDir, "build.json");
447
- if (!fs4.existsSync(buildJsonPath)) return fallback;
448
- try {
449
- const data = JSON.parse(fs4.readFileSync(buildJsonPath, "utf8"));
450
- return data.name?.trim() || fallback;
451
- } catch {
452
- return fallback;
453
- }
454
- }
455
465
  function optionalHint(current) {
456
466
  const value = current?.trim();
457
467
  return value ? `\uFF08\u56DE\u8F66\u4FDD\u7559: ${value}\uFF09` : "\uFF08\u56DE\u8F66\u8DF3\u8FC7\uFF09";
@@ -527,12 +537,12 @@ async function runInit(corpCodeArg, options = {}) {
527
537
  const appEnvRoot = resolveAppEnvRoot(options.root ?? process.cwd());
528
538
  const corpDir = getCorpDir(appEnvRoot, corpCode);
529
539
  consola2.info(`app-env \u6839\u76EE\u5F55: ${appEnvRoot}`);
530
- consola2.info(`\u76EE\u6807\u516C\u53F8\u76EE\u5F55: ${path4.relative(appEnvRoot, corpDir)}`);
531
- const exists = fs4.existsSync(corpDir);
540
+ consola2.info(`\u76EE\u6807\u516C\u53F8\u76EE\u5F55: ${path5.relative(appEnvRoot, corpDir)}`);
541
+ const exists = fs5.existsSync(corpDir);
532
542
  let mode = "create";
533
543
  const existingProjects = listExistingProjects(corpDir);
534
544
  if (exists && !options.force) {
535
- printExistingCorpNotice(corpCode, existingProjects);
545
+ printCorpNotice("\u516C\u53F8\u5DF2\u5B58\u5728", corpCode, corpDir, existingProjects);
536
546
  }
537
547
  const currentName = readCorpName(corpDir);
538
548
  let corpName = currentName;
@@ -661,28 +671,136 @@ async function runInit(corpCodeArg, options = {}) {
661
671
  }
662
672
  }
663
673
 
674
+ // src/commands/uninstall.ts
675
+ import { checkbox as checkbox2, input as input2, select as select2 } from "@inquirer/prompts";
676
+ import { consola as consola3 } from "consola";
677
+ import fs6 from "fs/promises";
678
+ import fsSync from "fs";
679
+ import path6 from "path";
680
+ async function resolveCorpCode(corpCodeArg) {
681
+ let corpCode = corpCodeArg?.trim() || "";
682
+ if (!corpCode) {
683
+ corpCode = (await input2({
684
+ message: "\u516C\u53F8\u7F16\u7801\uFF08\u76EE\u5F55\u540D / build.json.value\uFF09",
685
+ validate: validateCorpCode
686
+ })).trim();
687
+ } else {
688
+ assertValidCorpCode(corpCode);
689
+ }
690
+ return corpCode;
691
+ }
692
+ async function runUninstall(corpCodeArg, options = {}) {
693
+ const corpCode = await resolveCorpCode(corpCodeArg);
694
+ const appEnvRoot = resolveAppEnvRoot(options.root ?? process.cwd());
695
+ const corpDir = getCorpDir(appEnvRoot, corpCode);
696
+ consola3.info(`app-env \u6839\u76EE\u5F55: ${appEnvRoot}`);
697
+ consola3.info(`\u76EE\u6807\u516C\u53F8\u76EE\u5F55: ${path6.relative(appEnvRoot, corpDir)}`);
698
+ if (!fsSync.existsSync(corpDir)) {
699
+ throw new Error(`\u516C\u53F8\u4E0D\u5B58\u5728: ${corpCode}`);
700
+ }
701
+ const existingProjects = listExistingProjects(corpDir);
702
+ printCorpNotice("\u51C6\u5907\u5378\u8F7D", corpCode, corpDir, existingProjects);
703
+ let removeWhole = false;
704
+ let selectedProjects = [];
705
+ if (options.force) {
706
+ removeWhole = true;
707
+ } else {
708
+ const scope = await select2({
709
+ message: "\u8BF7\u9009\u62E9\u5378\u8F7D\u8303\u56F4",
710
+ choices: [
711
+ { name: "\u5378\u8F7D\u6574\u4E2A\u516C\u53F8", value: "all" },
712
+ {
713
+ name: "\u5378\u8F7D\u90E8\u5206\u4E1A\u52A1\u7AEF",
714
+ value: "partial",
715
+ disabled: existingProjects.length ? false : "\u6CA1\u6709\u53EF\u5378\u8F7D\u7684\u4E1A\u52A1\u7AEF"
716
+ },
717
+ { name: "\u53D6\u6D88", value: "cancel" }
718
+ ]
719
+ });
720
+ if (scope === "cancel") {
721
+ consola3.warn("\u5DF2\u53D6\u6D88");
722
+ return;
723
+ }
724
+ if (scope === "all") {
725
+ removeWhole = true;
726
+ } else {
727
+ selectedProjects = await checkbox2({
728
+ message: "\u9009\u62E9\u8981\u5378\u8F7D\u7684\u4E1A\u52A1\u7AEF\uFF08\u7A7A\u683C\u591A\u9009\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
729
+ required: true,
730
+ choices: existingProjects.map((value) => ({
731
+ name: getProjectLabel(value),
732
+ value
733
+ }))
734
+ });
735
+ if (!selectedProjects.length) {
736
+ throw new Error("\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u4E1A\u52A1\u7AEF");
737
+ }
738
+ if (selectedProjects.length >= existingProjects.length) {
739
+ removeWhole = true;
740
+ }
741
+ }
742
+ const confirmMsg = removeWhole ? `\u786E\u8BA4\u5378\u8F7D\u6574\u4E2A\u516C\u53F8\u300C${corpCode}\u300D\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D` : `\u786E\u8BA4\u5378\u8F7D\u4E1A\u52A1\u7AEF: ${formatProjectLabels(selectedProjects)}\uFF1F`;
743
+ const confirmAction = await select2({
744
+ message: confirmMsg,
745
+ choices: [
746
+ { name: "\u786E\u8BA4\u5378\u8F7D", value: "yes" },
747
+ { name: "\u53D6\u6D88", value: "no" }
748
+ ]
749
+ });
750
+ if (confirmAction === "no") {
751
+ consola3.warn("\u5DF2\u53D6\u6D88");
752
+ return;
753
+ }
754
+ }
755
+ if (removeWhole) {
756
+ await fs6.rm(corpDir, { recursive: true, force: true });
757
+ consola3.success(`\u5DF2\u5378\u8F7D\u6574\u4E2A\u516C\u53F8: ${corpCode}`);
758
+ consola3.info(`\u5DF2\u5220\u9664 ${path6.relative(appEnvRoot, corpDir)}`);
759
+ return;
760
+ }
761
+ for (const project of selectedProjects) {
762
+ const projectDir = path6.join(corpDir, project);
763
+ await fs6.rm(projectDir, { recursive: true, force: true });
764
+ consola3.success(`\u5DF2\u5378\u8F7D\u4E1A\u52A1\u7AEF: ${formatProjectLabels([project])}`);
765
+ }
766
+ const remain = listExistingProjects(corpDir);
767
+ if (!remain.length) {
768
+ await fs6.rm(corpDir, { recursive: true, force: true });
769
+ consola3.info("\u5DF2\u65E0\u5269\u4F59\u4E1A\u52A1\u7AEF\uFF0C\u5DF2\u4E00\u5E76\u5220\u9664\u516C\u53F8\u76EE\u5F55");
770
+ } else {
771
+ consola3.info(`\u672A\u6539\u52A8\u7684\u5DF2\u6709\u7AEF: ${formatProjectLabels(remain)}`);
772
+ }
773
+ }
774
+
664
775
  // src/index.ts
665
776
  var version = "1.0.0";
666
777
 
667
778
  // src/cli.ts
668
- var cli = cac("pnpm create whale-env");
669
- async function handle(corpCode, options) {
779
+ var cli = cac("create-whale-env");
780
+ async function wrap(action) {
670
781
  try {
671
- await runInit(corpCode, options);
782
+ await action();
672
783
  } catch (error) {
673
784
  const message = error instanceof Error ? error.message : String(error);
674
785
  if (message.includes("User force closed") || message.includes("ExitPromptError")) {
675
- consola3.warn("\u5DF2\u53D6\u6D88");
786
+ consola4.warn("\u5DF2\u53D6\u6D88");
676
787
  process.exitCode = 1;
677
788
  return;
678
789
  }
679
- consola3.error(message);
790
+ consola4.error(message);
680
791
  process.exitCode = 1;
681
792
  }
682
793
  }
683
- cli.command("[corp-code]", "\u5FEB\u901F\u521B\u5EFA corp \u516C\u53F8\u73AF\u5883\u76EE\u5F55\uFF08\u53EF\u7701\u7565\uFF0C\u4EA4\u4E92\u8F93\u5165\u7F16\u7801\uFF09").option("--root <path>", "app-env \u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4ECE\u5F53\u524D\u76EE\u5F55\u5411\u4E0A\u67E5\u627E\uFF09").option("--force", "\u76EE\u5F55\u5DF2\u5B58\u5728\u65F6\u76F4\u63A5\u8986\u76D6", { default: false }).action(async (corpCode, options) => {
684
- await handle(corpCode, options);
685
- });
794
+ cli.command("uninstall [corp-code]", "\u5378\u8F7D\u516C\u53F8\u73AF\u5883\u76EE\u5F55\uFF08\u6574\u516C\u53F8\u6216\u6307\u5B9A\u4E1A\u52A1\u7AEF\uFF09").option("--root <path>", "app-env \u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4ECE\u5F53\u524D\u76EE\u5F55\u5411\u4E0A\u67E5\u627E\uFF09").option("--force", "\u8DF3\u8FC7\u786E\u8BA4\uFF0C\u76F4\u63A5\u5378\u8F7D\u6574\u4E2A\u516C\u53F8", { default: false }).action(
795
+ async (corpCode, options) => {
796
+ await wrap(() => runUninstall(corpCode, options));
797
+ }
798
+ );
799
+ cli.command("[corp-code]", "\u521B\u5EFA / \u8FFD\u52A0 / \u7F16\u8F91 corp \u516C\u53F8\u73AF\u5883\u76EE\u5F55").option("--root <path>", "app-env \u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4ECE\u5F53\u524D\u76EE\u5F55\u5411\u4E0A\u67E5\u627E\uFF09").option("--force", "\u516C\u53F8\u5DF2\u5B58\u5728\u65F6\u76F4\u63A5\u8D70\u7F16\u8F91\u6A21\u5F0F", { default: false }).action(
800
+ async (corpCode, options) => {
801
+ await wrap(() => runInit(corpCode, options));
802
+ }
803
+ );
686
804
  cli.help();
687
805
  cli.version(version);
688
806
  cli.parse();