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 +150 -0
- package/dist/cli.js +208 -90
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +192 -80
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
|
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
|
|
11
|
-
import
|
|
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(
|
|
99
|
+
function renderBaseTs(input3) {
|
|
101
100
|
const lines = [
|
|
102
|
-
`import { getAppConfEnv } from '../../../../app/config/${
|
|
101
|
+
`import { getAppConfEnv } from '../../../../app/config/${input3.project}'`,
|
|
103
102
|
"",
|
|
104
103
|
"export default{",
|
|
105
104
|
" ...getAppConfEnv(),",
|
|
106
|
-
` CLIENT: '${
|
|
107
|
-
` DOMAIN:'${
|
|
108
|
-
` WECHAT_APPID:'${
|
|
109
|
-
` WORK_WECHAT_APPID:'${
|
|
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 (
|
|
112
|
-
lines.push(` WECHAT_SERVICE_APPID:'${
|
|
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,
|
|
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:'${
|
|
138
|
-
` WORK_WECHAT_APPID:'${
|
|
136
|
+
` WECHAT_APPID:'${input3.wechatAppId}',//\u5FAE\u4FE1`,
|
|
137
|
+
` WORK_WECHAT_APPID:'${input3.workWechatAppId}',//\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528ID`
|
|
139
138
|
];
|
|
140
|
-
if (
|
|
139
|
+
if (input3.wechatServiceAppId) {
|
|
141
140
|
body.push(
|
|
142
|
-
` WECHAT_SERVICE_APPID:'${
|
|
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:'${
|
|
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(
|
|
175
|
+
function buildEnvFileMap(input3) {
|
|
177
176
|
const files = {
|
|
178
177
|
"types.ts": renderTypesTs(),
|
|
179
|
-
"base.ts": renderBaseTs(
|
|
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,
|
|
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,
|
|
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(
|
|
202
|
-
const uniAppId =
|
|
203
|
-
const 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(
|
|
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(
|
|
283
|
-
const content = renderManifest(
|
|
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,
|
|
297
|
-
const corpDir = getCorpDir(appEnvRoot,
|
|
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:
|
|
302
|
-
value:
|
|
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
|
|
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
|
|
374
|
-
import
|
|
418
|
+
import fs4 from "fs";
|
|
419
|
+
import path4 from "path";
|
|
375
420
|
function readText(filePath) {
|
|
376
|
-
if (!
|
|
377
|
-
return
|
|
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 =
|
|
399
|
-
if (!
|
|
400
|
-
const baseTs = readText(
|
|
401
|
-
const wechatBaseTs = readText(
|
|
402
|
-
const androidBaseTs = readText(
|
|
403
|
-
const groupDevTs = readText(
|
|
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
|
-
|
|
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: ${
|
|
531
|
-
const exists =
|
|
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
|
-
|
|
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("
|
|
669
|
-
async function
|
|
779
|
+
var cli = cac("create-whale-env");
|
|
780
|
+
async function wrap(action) {
|
|
670
781
|
try {
|
|
671
|
-
await
|
|
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
|
-
|
|
786
|
+
consola4.warn("\u5DF2\u53D6\u6D88");
|
|
676
787
|
process.exitCode = 1;
|
|
677
788
|
return;
|
|
678
789
|
}
|
|
679
|
-
|
|
790
|
+
consola4.error(message);
|
|
680
791
|
process.exitCode = 1;
|
|
681
792
|
}
|
|
682
793
|
}
|
|
683
|
-
cli.command("[corp-code]", "\
|
|
684
|
-
|
|
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();
|