create-whale-env 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js ADDED
@@ -0,0 +1,689 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { cac } from "cac";
5
+ import { consola as consola3 } from "consola";
6
+
7
+ // src/commands/init.ts
8
+ import { checkbox, input, select } from "@inquirer/prompts";
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";
13
+
14
+ // src/constants.ts
15
+ var PROJECTS = [
16
+ { name: "\u3010\u79FB\u52A8OA\u3011", value: "officeAutomatic", client: "EMPLOYEE" },
17
+ { name: "\u3010\u53F8\u673A\u7AEF\u3011", value: "driver", client: "DRIVER" },
18
+ { name: "\u3010\u51B6\u91D1\u4ED3\u50A8\u3011", value: "steelWare", client: "EMPLOYEE" },
19
+ { name: "\u3010\u4F9B\u5E94\u5546\u7AEF\u3011", value: "vendor", client: "VENDOR" },
20
+ { name: "\u3010\u5BA2\u6237\u7AEF\u3011", value: "customer", client: "CUSTOMER" },
21
+ { name: "\u3010\u751F\u4EA7\u5546\u7AEF\u3011", value: "manufactUser", client: "MANUFACTURER" },
22
+ { name: "\u3010\u81EA\u5B9A\u4E49\u7AEF\u3011", value: "portal", client: "" }
23
+ ];
24
+ var CLIENT_PLATFORMS = ["wechat", "android", "ios"];
25
+ var ENV_NAMES = ["dev", "test", "pro"];
26
+ var GROUP_ENV_NAMES = ["groupDev", "groupTest", "groupPro"];
27
+ var GROUP_DOMAIN_PLACEHOLDER = "https://xxxxxx.com";
28
+ function getProjectMeta(value) {
29
+ return PROJECTS.find((p) => p.value === value);
30
+ }
31
+
32
+ // src/generate/corp.ts
33
+ import fs2 from "fs/promises";
34
+ import path2 from "path";
35
+ import { consola } from "consola";
36
+
37
+ // src/utils/paths.ts
38
+ import fs from "fs";
39
+ import path from "path";
40
+ function resolveAppEnvRoot(cwd = process.cwd()) {
41
+ let dir = path.resolve(cwd);
42
+ while (true) {
43
+ const corpDir = path.join(dir, "src", "corp");
44
+ if (fs.existsSync(corpDir) && fs.statSync(corpDir).isDirectory()) {
45
+ return dir;
46
+ }
47
+ const parent = path.dirname(dir);
48
+ if (parent === dir) break;
49
+ dir = parent;
50
+ }
51
+ throw new Error(
52
+ `\u672A\u627E\u5230 app-env \u6839\u76EE\u5F55\uFF08\u9700\u5305\u542B src/corp\uFF09\u3002\u8BF7\u5728 app-env \u4E0B\u6267\u884C\uFF0C\u6216\u4F20 --root <path>`
53
+ );
54
+ }
55
+ function getCorpDir(appEnvRoot, corpCode) {
56
+ return path.join(appEnvRoot, "src", "corp", corpCode);
57
+ }
58
+ function assertValidCorpCode(corpCode) {
59
+ if (!/^[a-z][a-z0-9_-]*$/i.test(corpCode)) {
60
+ throw new Error(
61
+ `\u516C\u53F8\u7F16\u7801\u65E0\u6548: "${corpCode}"\u3002\u8BF7\u4F7F\u7528\u5B57\u6BCD\u5F00\u5934\uFF0C\u4EC5\u542B\u5B57\u6BCD/\u6570\u5B57/_/-`
62
+ );
63
+ }
64
+ if (corpCode.startsWith("_")) {
65
+ throw new Error(`\u516C\u53F8\u7F16\u7801\u4E0D\u80FD\u4EE5\u4E0B\u5212\u7EBF\u5F00\u5934: "${corpCode}"`);
66
+ }
67
+ }
68
+ function validateCorpCode(corpCode) {
69
+ try {
70
+ assertValidCorpCode(corpCode.trim());
71
+ return true;
72
+ } catch (error) {
73
+ return error instanceof Error ? error.message : String(error);
74
+ }
75
+ }
76
+
77
+ // src/generate/envFiles.ts
78
+ function platformFactoryName(platform) {
79
+ if (platform === "wechat") return "WechatBaseEnv";
80
+ if (platform === "android") return "AndroidBaseEnv";
81
+ return "IOSBaseEnv";
82
+ }
83
+ function envImportName(env) {
84
+ const map = {
85
+ dev: "BaseDevEnv",
86
+ test: "BaseTestEnv",
87
+ pro: "BaseProEnv",
88
+ groupDev: "BaseGroupDevEnv",
89
+ groupTest: "BaseGroupTestEnv",
90
+ groupPro: "BaseGroupProEnv"
91
+ };
92
+ return map[env] ?? `Base${env}Env`;
93
+ }
94
+ function renderTypesTs() {
95
+ return `export type EnvOptions = {
96
+ [key:string]:any
97
+ }
98
+ `;
99
+ }
100
+ function renderBaseTs(input2) {
101
+ const lines = [
102
+ `import { getAppConfEnv } from '../../../../app/config/${input2.project}'`,
103
+ "",
104
+ "export default{",
105
+ " ...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`
110
+ ];
111
+ if (input2.wechatServiceAppId) {
112
+ lines.push(` WECHAT_SERVICE_APPID:'${input2.wechatServiceAppId}',//\u516C\u4F17\u53F7/\u670D\u52A1\u53F7appid`);
113
+ }
114
+ lines.push("}");
115
+ lines.push("");
116
+ return lines.join("\n");
117
+ }
118
+ function renderRootEnvTs(env, domainOverride) {
119
+ const comment = env.startsWith("group") ? "\u96C6\u56E2\u73AF\u5883\u914D\u7F6E" : `${env} \u73AF\u5883\u914D\u7F6E`;
120
+ const domainLine = domainOverride ? ` DOMAIN:'${domainOverride}',//\u57DF\u540D
121
+ ` : "";
122
+ return `import BaseEnv from './base'
123
+
124
+ /**
125
+ * ${comment}
126
+ */
127
+ export default{
128
+ ...BaseEnv,
129
+ ${domainLine}}
130
+ `;
131
+ }
132
+ function renderPlatformBaseTs(platform, input2) {
133
+ const factory = platformFactoryName(platform);
134
+ if (platform === "wechat") {
135
+ const body = [
136
+ " ...options,",
137
+ ` WECHAT_APPID:'${input2.wechatAppId}',//\u5FAE\u4FE1`,
138
+ ` WORK_WECHAT_APPID:'${input2.workWechatAppId}',//\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528ID`
139
+ ];
140
+ if (input2.wechatServiceAppId) {
141
+ body.push(
142
+ ` WECHAT_SERVICE_APPID:'${input2.wechatServiceAppId}',//\u516C\u4F17\u53F7/\u670D\u52A1\u53F7appid`
143
+ );
144
+ }
145
+ return `import type { EnvOptions } from '../types'
146
+
147
+ export const ${factory} = (options:EnvOptions)=>{
148
+ return {
149
+ ${body.join("\n")}
150
+ }
151
+ }
152
+ export default ${factory}
153
+ `;
154
+ }
155
+ return `import type { EnvOptions } from '../types'
156
+
157
+ export const ${factory} = (options:EnvOptions)=>{
158
+ return {
159
+ DCLOUD_APP_KEY_IOS:'${input2.dcloudAppKey}',
160
+ ...options,
161
+ }
162
+ }
163
+ export default ${factory}
164
+ `;
165
+ }
166
+ function renderPlatformEnvTs(platform, env) {
167
+ const factory = platformFactoryName(platform);
168
+ const importName = envImportName(env);
169
+ return `import ${factory} from './base'
170
+ import ${importName} from '../${env}'
171
+ export default ${factory}({
172
+ ...${importName},
173
+ })
174
+ `;
175
+ }
176
+ function buildEnvFileMap(input2) {
177
+ const files = {
178
+ "types.ts": renderTypesTs(),
179
+ "base.ts": renderBaseTs(input2)
180
+ };
181
+ for (const env of ENV_NAMES) {
182
+ files[`${env}.ts`] = renderRootEnvTs(env);
183
+ }
184
+ for (const env of GROUP_ENV_NAMES) {
185
+ files[`${env}.ts`] = renderRootEnvTs(env, input2.groupDomain);
186
+ }
187
+ for (const platform of CLIENT_PLATFORMS) {
188
+ files[`${platform}/base.ts`] = renderPlatformBaseTs(platform, input2);
189
+ for (const env of [...ENV_NAMES, ...GROUP_ENV_NAMES]) {
190
+ files[`${platform}/${env}.ts`] = renderPlatformEnvTs(platform, env);
191
+ }
192
+ }
193
+ return files;
194
+ }
195
+
196
+ // src/generate/manifest.ts
197
+ function fallbackUniAppId(project) {
198
+ const seed = project.replace(/[^a-zA-Z0-9]/g, "").toUpperCase().slice(0, 7).padEnd(7, "0");
199
+ return `__UNI__${seed}`;
200
+ }
201
+ function renderManifest(input2) {
202
+ const uniAppId = input2.uniAppId || fallbackUniAppId(input2.project);
203
+ const wechatAppId = input2.wechatAppId || "";
204
+ return `{
205
+ "name" : ${JSON.stringify(input2.appName)},
206
+ "appid" : ${JSON.stringify(uniAppId)},
207
+ "description" : "",
208
+ "versionName" : "1.0.0",
209
+ "versionCode" : "100",
210
+ "transformPx" : false,
211
+ /* 5+App\u7279\u6709\u76F8\u5173 */
212
+ "app-plus" : {
213
+ "usingComponents" : true,
214
+ "nvueStyleCompiler" : "uni-app",
215
+ "compilerVersion" : 3,
216
+ "splashscreen" : {
217
+ "alwaysShowBeforeRender" : true,
218
+ "waiting" : true,
219
+ "autoclose" : true,
220
+ "delay" : 0
221
+ },
222
+ /* \u6A21\u5757\u914D\u7F6E */
223
+ "modules" : {
224
+ "Barcode" : {},
225
+ "Camera" : {},
226
+ "VideoPlayer" : {}
227
+ },
228
+ /* \u5E94\u7528\u53D1\u5E03\u4FE1\u606F */
229
+ "distribute" : {
230
+ /* android\u6253\u5305\u914D\u7F6E */
231
+ "android" : {
232
+ "permissions" : [
233
+ "<uses-permission android:name=\\"android.permission.CHANGE_NETWORK_STATE\\"/>",
234
+ "<uses-permission android:name=\\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\\"/>",
235
+ "<uses-permission android:name=\\"android.permission.VIBRATE\\"/>",
236
+ "<uses-permission android:name=\\"android.permission.READ_LOGS\\"/>",
237
+ "<uses-permission android:name=\\"android.permission.ACCESS_WIFI_STATE\\"/>",
238
+ "<uses-feature android:name=\\"android.hardware.camera.autofocus\\"/>",
239
+ "<uses-permission android:name=\\"android.permission.ACCESS_NETWORK_STATE\\"/>",
240
+ "<uses-permission android:name=\\"android.permission.CAMERA\\"/>",
241
+ "<uses-permission android:name=\\"android.permission.GET_ACCOUNTS\\"/>",
242
+ "<uses-permission android:name=\\"android.permission.READ_PHONE_STATE\\"/>",
243
+ "<uses-permission android:name=\\"android.permission.CHANGE_WIFI_STATE\\"/>",
244
+ "<uses-permission android:name=\\"android.permission.WAKE_LOCK\\"/>",
245
+ "<uses-permission android:name=\\"android.permission.FLASHLIGHT\\"/>",
246
+ "<uses-feature android:name=\\"android.hardware.camera\\"/>",
247
+ "<uses-permission android:name=\\"android.permission.WRITE_SETTINGS\\"/>"
248
+ ]
249
+ },
250
+ /* ios\u6253\u5305\u914D\u7F6E */
251
+ "ios" : {},
252
+ /* SDK\u914D\u7F6E */
253
+ "sdkConfigs" : {}
254
+ }
255
+ },
256
+ /* \u5FEB\u5E94\u7528\u7279\u6709\u76F8\u5173 */
257
+ "quickapp" : {},
258
+ /* \u5C0F\u7A0B\u5E8F\u7279\u6709\u76F8\u5173 */
259
+ "mp-weixin" : {
260
+ "appid" : ${JSON.stringify(wechatAppId)},
261
+ "setting" : {
262
+ "urlCheck" : false
263
+ },
264
+ "usingComponents" : true
265
+ },
266
+ "mp-alipay" : {
267
+ "usingComponents" : true
268
+ },
269
+ "mp-baidu" : {
270
+ "usingComponents" : true
271
+ },
272
+ "mp-toutiao" : {
273
+ "usingComponents" : true
274
+ },
275
+ "uniStatistics": {
276
+ "enable": false
277
+ },
278
+ "vueVersion" : "3"
279
+ }
280
+ `;
281
+ }
282
+ function buildManifestFileMap(input2) {
283
+ const content = renderManifest(input2);
284
+ const files = {};
285
+ for (const platform of CLIENT_PLATFORMS) {
286
+ files[`${platform}.manifest.json`] = content;
287
+ }
288
+ return files;
289
+ }
290
+
291
+ // src/generate/corp.ts
292
+ async function writeFile(filePath, content) {
293
+ await fs2.mkdir(path2.dirname(filePath), { recursive: true });
294
+ await fs2.writeFile(filePath, content, "utf8");
295
+ }
296
+ async function generateCorp(appEnvRoot, input2) {
297
+ const corpDir = getCorpDir(appEnvRoot, input2.corpCode);
298
+ await fs2.mkdir(corpDir, { recursive: true });
299
+ const buildJson = JSON.stringify(
300
+ {
301
+ name: input2.corpName,
302
+ value: input2.corpCode
303
+ },
304
+ null,
305
+ 2
306
+ );
307
+ await writeFile(path2.join(corpDir, "build.json"), `${buildJson}
308
+ `);
309
+ consola.success(`\u5199\u5165 ${path2.relative(appEnvRoot, path2.join(corpDir, "build.json"))}`);
310
+ for (const project of input2.projects) {
311
+ const projectDir = path2.join(corpDir, project.project);
312
+ const envDir = path2.join(projectDir, "env");
313
+ const manifestDir = path2.join(projectDir, "manifest");
314
+ const envFiles = buildEnvFileMap(project);
315
+ for (const [rel, content] of Object.entries(envFiles)) {
316
+ await writeFile(path2.join(envDir, rel), content);
317
+ }
318
+ const manifestFiles = buildManifestFileMap(project);
319
+ for (const [rel, content] of Object.entries(manifestFiles)) {
320
+ await writeFile(path2.join(manifestDir, rel), content);
321
+ }
322
+ consola.success(
323
+ `\u751F\u6210\u4E1A\u52A1\u7AEF ${project.project}\uFF08env ${Object.keys(envFiles).length} + manifest ${Object.keys(manifestFiles).length}\uFF09`
324
+ );
325
+ }
326
+ return corpDir;
327
+ }
328
+
329
+ // src/utils/noticeBox.ts
330
+ import stringWidth from "string-width";
331
+ import { colors } from "consola/utils";
332
+ function padVisible(text, width) {
333
+ const visible = stringWidth(text);
334
+ return visible >= width ? text : `${text}${" ".repeat(width - visible)}`;
335
+ }
336
+ function printNoticeBox(title, bodyLines, options = {}) {
337
+ const padX = 2;
338
+ const minWidth = options.minWidth ?? 16;
339
+ const paint = colors[options.borderColor ?? "yellow"];
340
+ const contentWidth = Math.max(
341
+ stringWidth(title) + 2,
342
+ ...bodyLines.map((line) => stringWidth(line)),
343
+ minWidth
344
+ );
345
+ const innerWidth = contentWidth + padX * 2;
346
+ const top = paint("\u256D") + paint("\u2500".repeat(padX)) + paint(colors.bold(` ${title} `)) + paint(
347
+ "\u2500".repeat(Math.max(1, innerWidth - padX - stringWidth(` ${title} `)))
348
+ ) + paint("\u256E");
349
+ const empty = paint("\u2502") + " ".repeat(innerWidth) + paint("\u2502");
350
+ const bottom = paint("\u2570") + paint("\u2500".repeat(innerWidth)) + paint("\u256F");
351
+ console.log();
352
+ console.log(top);
353
+ if (bodyLines.length === 0) {
354
+ console.log(empty);
355
+ } else {
356
+ console.log(empty);
357
+ for (const line of bodyLines) {
358
+ const padded = padVisible(line, contentWidth);
359
+ console.log(
360
+ `${paint("\u2502")}${" ".repeat(padX)}${padded}${" ".repeat(padX)}${paint("\u2502")}`
361
+ );
362
+ }
363
+ console.log(empty);
364
+ }
365
+ console.log(bottom);
366
+ console.log();
367
+ }
368
+ function printSectionBox(message) {
369
+ printNoticeBox(message, [], { minWidth: 36, borderColor: "cyan" });
370
+ }
371
+
372
+ // src/utils/readExistingProject.ts
373
+ import fs3 from "fs";
374
+ import path3 from "path";
375
+ function readText(filePath) {
376
+ if (!fs3.existsSync(filePath)) return "";
377
+ return fs3.readFileSync(filePath, "utf8");
378
+ }
379
+ function pickString(source, key) {
380
+ const re = new RegExp(
381
+ `${key}\\s*:\\s*['"]([^'"]*)['"]`,
382
+ "m"
383
+ );
384
+ const matched = source.match(re);
385
+ return matched?.[1] ?? "";
386
+ }
387
+ function pickJsonString(source, key) {
388
+ try {
389
+ const data = JSON.parse(source);
390
+ const value = data[key];
391
+ return typeof value === "string" ? value : "";
392
+ } catch {
393
+ const re = new RegExp(`"${key}"\\s*:\\s*"([^"]*)"`, "m");
394
+ return source.match(re)?.[1] ?? "";
395
+ }
396
+ }
397
+ 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"));
404
+ const manifestRaw = readText(
405
+ path3.join(projectDir, "manifest/wechat.manifest.json")
406
+ );
407
+ return {
408
+ domain: pickString(baseTs, "DOMAIN"),
409
+ wechatAppId: pickString(baseTs, "WECHAT_APPID") || pickString(wechatBaseTs, "WECHAT_APPID"),
410
+ workWechatAppId: pickString(baseTs, "WORK_WECHAT_APPID") || pickString(wechatBaseTs, "WORK_WECHAT_APPID"),
411
+ wechatServiceAppId: pickString(baseTs, "WECHAT_SERVICE_APPID") || pickString(wechatBaseTs, "WECHAT_SERVICE_APPID"),
412
+ dcloudAppKey: pickString(androidBaseTs, "DCLOUD_APP_KEY_IOS"),
413
+ groupDomain: pickString(groupDevTs, "DOMAIN") || GROUP_DOMAIN_PLACEHOLDER,
414
+ appName: pickJsonString(manifestRaw, "name"),
415
+ uniAppId: pickJsonString(manifestRaw, "appid")
416
+ };
417
+ }
418
+
419
+ // 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
+ function optionalHint(current) {
456
+ const value = current?.trim();
457
+ return value ? `\uFF08\u56DE\u8F66\u4FDD\u7559: ${value}\uFF09` : "\uFF08\u56DE\u8F66\u8DF3\u8FC7\uFF09";
458
+ }
459
+ async function promptProjectConfig(corpName, projectValue, defaults = {}) {
460
+ const meta = getProjectMeta(projectValue);
461
+ if (!meta) {
462
+ throw new Error(`\u672A\u77E5\u4E1A\u52A1\u7AEF: ${projectValue}`);
463
+ }
464
+ const editing = Boolean(defaults.domain || defaults.appName);
465
+ printSectionBox(
466
+ editing ? `\u7F16\u8F91\u4E1A\u52A1\u7AEF\uFF1A${meta.name}` : `\u914D\u7F6E\u4E1A\u52A1\u7AEF\uFF1A${meta.name}`
467
+ );
468
+ const domainDefault = defaults.domain?.trim() || "";
469
+ const domain = await input({
470
+ message: domainDefault ? `API \u57DF\u540D DOMAIN${optionalHint(domainDefault)}` : "API \u57DF\u540D DOMAIN",
471
+ default: domainDefault || void 0,
472
+ validate: (v) => v.trim() ? true : "\u57DF\u540D\u5FC5\u586B"
473
+ });
474
+ const appNameDefault = defaults.appName?.trim() || `${corpName}${meta.name.replace(/[【】]/g, "")}`;
475
+ const appName = await input({
476
+ message: `App \u663E\u793A\u540D\uFF08manifest.name\uFF09${optionalHint(appNameDefault)}`,
477
+ default: appNameDefault
478
+ });
479
+ const wechatAppId = await input({
480
+ message: `\u5FAE\u4FE1\u5C0F\u7A0B\u5E8F WECHAT_APPID${optionalHint(defaults.wechatAppId)}`,
481
+ default: defaults.wechatAppId ?? ""
482
+ });
483
+ const workWechatAppId = await input({
484
+ message: `\u4F01\u4E1A\u5FAE\u4FE1 WORK_WECHAT_APPID${optionalHint(defaults.workWechatAppId)}`,
485
+ default: defaults.workWechatAppId ?? ""
486
+ });
487
+ const wechatServiceAppId = await input({
488
+ message: `\u670D\u52A1\u53F7 WECHAT_SERVICE_APPID${optionalHint(defaults.wechatServiceAppId)}`,
489
+ default: defaults.wechatServiceAppId ?? ""
490
+ });
491
+ const uniAppId = await input({
492
+ message: `uni-app appid${optionalHint(defaults.uniAppId)}`,
493
+ default: defaults.uniAppId ?? ""
494
+ });
495
+ const dcloudAppKey = await input({
496
+ message: `DCloud AppKey${optionalHint(defaults.dcloudAppKey)}`,
497
+ default: defaults.dcloudAppKey ?? ""
498
+ });
499
+ const groupDefault = defaults.groupDomain?.trim() || GROUP_DOMAIN_PLACEHOLDER;
500
+ const groupDomain = await input({
501
+ message: `\u96C6\u56E2\u57DF\u540D group DOMAIN${optionalHint(groupDefault)}`,
502
+ default: groupDefault
503
+ });
504
+ return {
505
+ project: meta.value,
506
+ client: meta.client,
507
+ domain: domain.trim(),
508
+ appName: appName.trim(),
509
+ wechatAppId: wechatAppId.trim(),
510
+ workWechatAppId: workWechatAppId.trim(),
511
+ wechatServiceAppId: wechatServiceAppId.trim(),
512
+ uniAppId: uniAppId.trim(),
513
+ dcloudAppKey: dcloudAppKey.trim(),
514
+ groupDomain: groupDomain.trim() || GROUP_DOMAIN_PLACEHOLDER
515
+ };
516
+ }
517
+ async function runInit(corpCodeArg, options = {}) {
518
+ let corpCode = corpCodeArg?.trim() || "";
519
+ if (!corpCode) {
520
+ corpCode = (await input({
521
+ message: "\u516C\u53F8\u7F16\u7801\uFF08\u76EE\u5F55\u540D / build.json.value\uFF09",
522
+ validate: validateCorpCode
523
+ })).trim();
524
+ } else {
525
+ assertValidCorpCode(corpCode);
526
+ }
527
+ const appEnvRoot = resolveAppEnvRoot(options.root ?? process.cwd());
528
+ const corpDir = getCorpDir(appEnvRoot, corpCode);
529
+ 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);
532
+ let mode = "create";
533
+ const existingProjects = listExistingProjects(corpDir);
534
+ if (exists && !options.force) {
535
+ printExistingCorpNotice(corpCode, existingProjects);
536
+ }
537
+ const currentName = readCorpName(corpDir);
538
+ let corpName = currentName;
539
+ if (exists && currentName) {
540
+ const renameAction = await select({
541
+ message: `\u516C\u53F8\u4E2D\u6587\u540D\uFF08\u5F53\u524D: ${currentName}\uFF09`,
542
+ choices: [
543
+ { name: "\u4FDD\u6301\u539F\u540D", value: "keep" },
544
+ { name: "\u91CD\u547D\u540D", value: "rename" }
545
+ ]
546
+ });
547
+ if (renameAction === "rename") {
548
+ corpName = (await input({
549
+ message: "\u8BF7\u8F93\u5165\u65B0\u7684\u516C\u53F8\u4E2D\u6587\u540D",
550
+ default: currentName,
551
+ validate: (v) => v.trim() ? true : "\u516C\u53F8\u540D\u5FC5\u586B"
552
+ })).trim();
553
+ }
554
+ } else {
555
+ corpName = (await input({
556
+ message: "\u516C\u53F8\u4E2D\u6587\u540D\uFF08build.json.name\uFF09",
557
+ default: currentName || void 0,
558
+ validate: (v) => v.trim() ? true : "\u516C\u53F8\u540D\u5FC5\u586B"
559
+ })).trim();
560
+ }
561
+ if (exists) {
562
+ if (options.force) {
563
+ mode = "overwrite";
564
+ } else {
565
+ const canAppend = PROJECTS.some((p) => !existingProjects.includes(p.value));
566
+ const choices = [
567
+ ...canAppend ? [
568
+ {
569
+ name: "\u8FFD\u52A0\u65B0\u7AEF\uFF08\u4FDD\u7559\u5DF2\u6709\uFF0C\u53EA\u751F\u6210\u5C1A\u672A\u5F00\u901A\u7684\u7AEF\uFF09",
570
+ value: "append"
571
+ }
572
+ ] : [],
573
+ {
574
+ name: "\u7F16\u8F91\u5DF2\u6709\u7684\u7AEF",
575
+ value: "overwrite"
576
+ },
577
+ { name: "\u53D6\u6D88", value: "cancel" }
578
+ ];
579
+ if (!canAppend) {
580
+ consola2.info("\u5168\u90E8\u4E1A\u52A1\u7AEF\u5747\u5DF2\u5F00\u901A\uFF0C\u4EC5\u53EF\u7F16\u8F91\u5DF2\u6709\u7AEF\u6216\u53D6\u6D88");
581
+ }
582
+ const picked = await select({
583
+ message: "\u8BF7\u9009\u62E9\u5199\u5165\u65B9\u5F0F",
584
+ choices
585
+ });
586
+ if (picked === "cancel") {
587
+ consola2.warn("\u5DF2\u53D6\u6D88");
588
+ return;
589
+ }
590
+ mode = picked;
591
+ }
592
+ }
593
+ const projectChoices = PROJECTS.map((p) => {
594
+ const already = existingProjects.includes(p.value);
595
+ if (mode === "append") {
596
+ return {
597
+ name: p.name,
598
+ value: p.value,
599
+ checked: false,
600
+ disabled: already ? "\u5DF2\u5B58\u5728" : false
601
+ };
602
+ }
603
+ return {
604
+ name: p.name,
605
+ value: p.value,
606
+ checked: !exists && p.value === "officeAutomatic"
607
+ };
608
+ });
609
+ if (mode === "append" && projectChoices.every((c) => c.disabled)) {
610
+ consola2.warn("\u6240\u6709\u4E1A\u52A1\u7AEF\u90FD\u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u8FFD\u52A0\u3002\u5982\u9700\u4FEE\u6539\u8BF7\u9009\u300C\u7F16\u8F91\u5DF2\u6709\u7684\u7AEF\u300D\u3002");
611
+ return;
612
+ }
613
+ const selected = await checkbox({
614
+ message: "\u9009\u62E9\u4E1A\u52A1\u7AEF\uFF08\u7A7A\u683C\u591A\u9009\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF1B\u53EF\u4E00\u6B21\u52FE N \u4E2A\uFF09",
615
+ required: true,
616
+ choices: projectChoices
617
+ });
618
+ if (!selected.length) {
619
+ throw new Error("\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u4E1A\u52A1\u7AEF");
620
+ }
621
+ if (mode === "overwrite") {
622
+ const willEdit = selected.filter((p) => existingProjects.includes(p));
623
+ if (willEdit.length) {
624
+ const editAction = await select({
625
+ message: `\u5C06\u7F16\u8F91\u8FD9\u4E9B\u5DF2\u6709\u7AEF: ${formatProjectLabels(willEdit)}`,
626
+ choices: [
627
+ { name: "\u786E\u8BA4\u7F16\u8F91", value: "yes" },
628
+ { name: "\u53D6\u6D88", value: "no" }
629
+ ]
630
+ });
631
+ if (editAction === "no") {
632
+ consola2.warn("\u5DF2\u53D6\u6D88");
633
+ return;
634
+ }
635
+ }
636
+ }
637
+ const projects = [];
638
+ for (const projectValue of selected) {
639
+ const defaults = mode === "overwrite" || existingProjects.includes(projectValue) ? readExistingProjectConfig(corpDir, projectValue) : {};
640
+ projects.push(
641
+ await promptProjectConfig(corpName.trim(), projectValue, defaults)
642
+ );
643
+ }
644
+ const payload = {
645
+ corpCode,
646
+ corpName: corpName.trim(),
647
+ projects
648
+ };
649
+ const created = await generateCorp(appEnvRoot, payload);
650
+ const action = mode === "append" ? "\u8FFD\u52A0" : mode === "overwrite" ? "\u7F16\u8F91" : "\u521B\u5EFA";
651
+ consola2.success(`\u516C\u53F8\u73AF\u5883\u5DF2${action}: ${created}`);
652
+ consola2.info(
653
+ `\u672C\u6B21\u5904\u7406 ${projects.length} \u4E2A\u7AEF: ${formatProjectLabels(projects.map((p) => p.project))}`
654
+ );
655
+ if (mode === "append" || mode === "overwrite") {
656
+ const selectedSet = new Set(selected);
657
+ const remain = listExistingProjects(corpDir).filter((p) => !selectedSet.has(p));
658
+ if (remain.length) {
659
+ consola2.info(`\u672A\u6539\u52A8\u7684\u5DF2\u6709\u7AEF: ${formatProjectLabels(remain)}`);
660
+ }
661
+ }
662
+ }
663
+
664
+ // src/index.ts
665
+ var version = "1.0.0";
666
+
667
+ // src/cli.ts
668
+ var cli = cac("pnpm create whale-env");
669
+ async function handle(corpCode, options) {
670
+ try {
671
+ await runInit(corpCode, options);
672
+ } catch (error) {
673
+ const message = error instanceof Error ? error.message : String(error);
674
+ if (message.includes("User force closed") || message.includes("ExitPromptError")) {
675
+ consola3.warn("\u5DF2\u53D6\u6D88");
676
+ process.exitCode = 1;
677
+ return;
678
+ }
679
+ consola3.error(message);
680
+ process.exitCode = 1;
681
+ }
682
+ }
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
+ });
686
+ cli.help();
687
+ cli.version(version);
688
+ cli.parse();
689
+ //# sourceMappingURL=cli.js.map