@cabloy/cli 3.0.67 → 3.0.69

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/index.js CHANGED
@@ -1,6 +1,1615 @@
1
- export * from "./config.js";
2
- export * from "./lib/index.js";
3
- export * from "./registry.js";
4
- export * from "./start.js";
5
- export * from "./types/index.js";
6
- export * from "./utils.js";
1
+ import { glob } from '@cabloy/module-glob';
2
+ import { createRequire } from 'node:module';
3
+ import boxen from 'boxen';
4
+ import chalk from 'chalk';
5
+ import semver from 'semver';
6
+ import urllib from 'urllib';
7
+ import NPMConfig from '@npmcli/config';
8
+ import npmDefinitions from '@npmcli/config/lib/definitions/index.js';
9
+ import path from 'node:path';
10
+ import fse from 'fs-extra';
11
+ import { pathToFileURL } from 'node:url';
12
+ import * as ModuleInfo from '@cabloy/module-info';
13
+ import { ProcessHelper } from '@cabloy/process-helper';
14
+ import { combineResourceName, catchError, evaluate } from '@cabloy/utils';
15
+ import TableClass from 'cli-table3';
16
+ import gogocode from 'gogocode';
17
+ import tmp from 'tmp';
18
+ import fs from 'node:fs';
19
+ import ejs from '@zhennann/ejs';
20
+ import { globby } from 'globby';
21
+ import isTextOrBinary from 'istextorbinary';
22
+ import BaseCommand from '@zhennann/common-bin';
23
+ import enquirer from 'enquirer';
24
+ import is from 'is-type-of';
25
+
26
+ const commandsConfig = {
27
+ sets: {
28
+ zova: {
29
+ front: 'zova-cli-set-front'
30
+ },
31
+ vona: {
32
+ api: 'vona-cli-set-api'
33
+ }
34
+ },
35
+ helper: {
36
+ chalk: {
37
+ options: {
38
+ level: 2
39
+ }
40
+ },
41
+ boxen: {
42
+ options: {
43
+ padding: 1,
44
+ margin: 1,
45
+ align: 'center',
46
+ borderColor: 'yellow',
47
+ borderStyle: 'round'
48
+ }
49
+ }
50
+ },
51
+ template: {
52
+ render: {
53
+ fileMapping: {
54
+ 'gitignore': '.gitignore',
55
+ '_gitignore': '.gitignore',
56
+ '_.gitignore': '.gitignore',
57
+ '_package.json': 'package.json',
58
+ '_.eslintrc': '.eslintrc',
59
+ '_.eslintignore': '.eslintignore',
60
+ '_.npmignore': '.npmignore',
61
+ '_.npmrc': '.npmrc',
62
+ '_.eslintrc.js': '.eslintrc.js',
63
+ '_jsconfig.json': 'jsconfig.json',
64
+ '_tsconfig.json': 'tsconfig.json',
65
+ '_tsconfig.base.json': 'tsconfig.base.json',
66
+ '_tsconfig.build.json': 'tsconfig.build.json'
67
+ },
68
+ ignore: ['.DS_Store']
69
+ }
70
+ }
71
+ };
72
+
73
+ let __registry;
74
+ async function getRegistry() {
75
+ if (!__registry) {
76
+ const npmConfig = new NPMConfig(Object.assign({
77
+ npmPath: ''
78
+ }, npmDefinitions));
79
+ await npmConfig.load();
80
+ __registry = npmConfig.get('registry') || 'https://registry.npmjs.org/';
81
+ if (!__registry.endsWith('/')) {
82
+ __registry = `${__registry}/`;
83
+ }
84
+ }
85
+ return __registry;
86
+ }
87
+
88
+ const boxenOptions = {
89
+ padding: 1,
90
+ margin: 1,
91
+ align: 'center',
92
+ borderColor: 'yellow',
93
+ borderStyle: 'round'
94
+ };
95
+ async function checkForUpdates(packageName) {
96
+ try {
97
+ // version old
98
+ const require = createRequire(import.meta.url);
99
+ const pkg = require(`${packageName}/package.json`);
100
+ const versionOld = pkg.version;
101
+ // version new
102
+ const info = await getPackageInfo(packageName);
103
+ const versionNew = info.version;
104
+ // check
105
+ const lt = semver.lt(versionOld, versionNew);
106
+ if (!lt) return;
107
+ // log
108
+ let message = `[${chalk.keyword('cyan')(packageName)}] new version available: ${chalk.keyword('yellow')(versionOld)} → ${chalk.keyword('orange')(versionNew)}`;
109
+ message += `\nRun ${chalk.keyword('orange')(`> pnpm add -g ${packageName} <`)} to update!`;
110
+ // eslint-disable-next-line
111
+ console.log('\n' + boxen(message, boxenOptions));
112
+ } catch (_err) {
113
+ // donothing
114
+ }
115
+ }
116
+ async function getPackageInfo(packageName) {
117
+ const registry = await getRegistry();
118
+ const result = await urllib.request(`${registry}${packageName}/latest`, {
119
+ dataType: 'json',
120
+ followRedirect: true,
121
+ maxRedirects: 5
122
+ });
123
+ if (result.status !== 200) {
124
+ const message = `npm info ${packageName} got error: ${result.status}, ${result.data.reason}`;
125
+ throw new Error(message);
126
+ }
127
+ return result.data;
128
+ }
129
+ function patchFlavor(flavor) {
130
+ return Array.isArray(flavor) ? flavor[flavor.length - 1] : flavor;
131
+ }
132
+
133
+ class LocalCommon {
134
+ constructor(cli) {
135
+ this.cli = void 0;
136
+ this.cli = cli;
137
+ }
138
+ async _generateTypeModulesFile(projectPath) {
139
+ const pathName = this.cli.context.brandName === 'zova' ? 'front' : 'backend';
140
+ const typeFile = path.join(projectPath, `src/${pathName}/typing/modules.d.ts`);
141
+ let content = '';
142
+ // // all suites
143
+ // for (const key in this.modulesMeta.suites) {
144
+ // const suite = this.modulesMeta.suites[key];
145
+ // content += `import '${suite.package.name}';\n`;
146
+ // }
147
+ // all modules
148
+ this.cli.modulesMeta.modulesArray.forEach(module => {
149
+ content += `import '${module.package.name}';\n`;
150
+ });
151
+ await fse.writeFile(typeFile, content);
152
+ const typeFileStat = await fse.stat(typeFile);
153
+ // all modules: type file
154
+ const promises = [];
155
+ for (const module of this.cli.modulesMeta.modulesArray) {
156
+ if (module.info.node_modules) continue;
157
+ const moduleTypeFile = path.join(module.root, 'src/.metadata/modules.d.ts');
158
+ promises.push(this._generateTypeModulesFileInner(typeFile, typeFileStat, moduleTypeFile));
159
+ }
160
+ await Promise.all(promises);
161
+ }
162
+ async _generateTypeModulesFileInner(typeFile, typeFileStat, moduleTypeFile) {
163
+ const win = true; // process.platform.startsWith('win');
164
+ let needCreate = true;
165
+ const exists = await fse.exists(moduleTypeFile);
166
+ if (exists) {
167
+ try {
168
+ if (win) {
169
+ const stat = await fse.stat(moduleTypeFile);
170
+ if (stat.size === typeFileStat.size) {
171
+ needCreate = false;
172
+ }
173
+ }
174
+ } catch (_err) {}
175
+ }
176
+ if (needCreate) {
177
+ await fse.remove(moduleTypeFile);
178
+ {
179
+ await fse.copy(typeFile, moduleTypeFile);
180
+ }
181
+ }
182
+ }
183
+ async _generatePackageJson(projectPath) {
184
+ const pkgFile = path.join(projectPath, 'package.json');
185
+ const pkgOriginalFile = path.join(projectPath, 'package.original.json');
186
+ // check original
187
+ if (!fse.existsSync(pkgOriginalFile)) {
188
+ await fse.copyFile(pkgFile, pkgOriginalFile);
189
+ }
190
+ // prepare deps
191
+ const {
192
+ deps,
193
+ depsDev
194
+ } = await this._generatePackageJson_prepareDeps(projectPath);
195
+ // pkg/pkgOriginal
196
+ const pkgOriginal = await this.cli.helper.loadJSONFile(pkgOriginalFile);
197
+ if (fse.existsSync(pkgFile)) {
198
+ const pkg = await this.cli.helper.loadJSONFile(pkgFile);
199
+ // save back
200
+ await this._generatePackageJson_saveBack(pkg, pkgOriginal, pkgOriginalFile, deps, depsDev);
201
+ }
202
+ // generate pkg from pkgOriginal
203
+ await this._generatePackageJson_pkgFromPkgOriginal(pkgOriginal, pkgFile, deps, depsDev);
204
+ }
205
+ async _generatePackageJson_prepareDeps(_projectPath) {
206
+ const deps = {};
207
+ const depsDev = {};
208
+ // all modules
209
+ this.cli.modulesMeta.modulesArray.forEach(module => {
210
+ const onlyDev = _checkIfModuleOnlyDev(module);
211
+ const version = module.info.node_modules ? `^${module.package.version}` : 'workspace:^';
212
+ if (onlyDev) {
213
+ depsDev[module.package.name] = version;
214
+ } else {
215
+ deps[module.package.name] = version;
216
+ }
217
+ });
218
+ // all globalDependencies of modules
219
+ this.cli.modulesMeta.modulesArray.forEach(module => {
220
+ _collectModuleDevs(module, deps, 'dependencies', 'globalDependencies');
221
+ _collectModuleDevs(module, depsDev, 'devDependencies', 'globalDependenciesDev');
222
+ });
223
+ return {
224
+ deps,
225
+ depsDev
226
+ };
227
+ }
228
+ async _generatePackageJson_pkgFromPkgOriginal(pkgOriginal, pkgFile, deps, depsDev) {
229
+ function _handleDeps(nameDependencies, deps) {
230
+ for (const key in deps) {
231
+ const version = deps[key];
232
+ if (!pkgOriginal[nameDependencies][key]) {
233
+ pkgOriginal[nameDependencies][key] = version;
234
+ }
235
+ }
236
+ }
237
+ _handleDeps('dependencies', deps);
238
+ _handleDeps('devDependencies', depsDev);
239
+ await this.cli.helper.saveJSONFile(pkgFile, pkgOriginal);
240
+ }
241
+ async _generatePackageJson_saveBack(pkg, pkgOriginal, pkgOriginalFile, deps, depsDev) {
242
+ let changed = false;
243
+ for (const key of ['version', 'gitHead']) {
244
+ if (pkgOriginal[key] !== pkg[key]) {
245
+ pkgOriginal[key] = pkg[key];
246
+ changed = true;
247
+ }
248
+ }
249
+ function _handleDeps(nameDependencies, deps) {
250
+ const moduleDeps = pkg[nameDependencies];
251
+ const moduleDepsOriginal = pkgOriginal[nameDependencies];
252
+ for (const key in moduleDeps) {
253
+ const version = moduleDeps[key];
254
+ if (moduleDepsOriginal[key] && moduleDepsOriginal[key] === version) continue;
255
+ const isModule = key.includes('vona-module-') || key.includes('zova-module-');
256
+ const isModuleWorkspace = isModule && version.startsWith('workspace:');
257
+ if (isModuleWorkspace) continue;
258
+ if (deps[key] && !isModule) continue;
259
+ moduleDepsOriginal[key] = version;
260
+ changed = true;
261
+ }
262
+ }
263
+ _handleDeps('dependencies', deps);
264
+ _handleDeps('devDependencies', depsDev);
265
+ if (changed) {
266
+ await this.cli.helper.saveJSONFile(pkgOriginalFile, pkgOriginal);
267
+ }
268
+ }
269
+ }
270
+ function _checkIfModuleOnlyDev(module) {
271
+ const meta = module.package.vonaModule?.capabilities?.meta || module.package.zovaModule?.capabilities?.meta;
272
+ if (!meta || !meta.mode) return false;
273
+ const modes = Array.isArray(meta.mode) ? meta.mode : [meta.mode];
274
+ return !modes.some(mode => ['prod', 'production'].includes(mode));
275
+ }
276
+ function _collectModuleDevs(module, deps, nameDependencies, nameGlobalDependencies) {
277
+ const moduleDeps = module.package[nameDependencies];
278
+ const globalDependencies = module.package.vonaModule?.[nameGlobalDependencies] || module.package.zovaModule?.[nameGlobalDependencies];
279
+ if (globalDependencies) {
280
+ for (const key in globalDependencies) {
281
+ let version = globalDependencies[key];
282
+ if (version !== false) {
283
+ if (version === true) version = moduleDeps[key];
284
+ deps[key] = version;
285
+ }
286
+ }
287
+ }
288
+ return deps;
289
+ }
290
+
291
+ class LocalConsole {
292
+ constructor(cli) {
293
+ this.cli = void 0;
294
+ this.cli = cli;
295
+ }
296
+ get options() {
297
+ return this.cli.options;
298
+ }
299
+ get context() {
300
+ return this.cli.options.context;
301
+ }
302
+ async log(data, options = {}) {
303
+ if (!data) return;
304
+ // data
305
+ if (typeof data !== 'object') {
306
+ data = {
307
+ text: String(data)
308
+ };
309
+ }
310
+ let {
311
+ /* progressNo, */total,
312
+ progress,
313
+ text
314
+ } = data;
315
+ // logPrefix
316
+ const logPrefix = options.logPrefix;
317
+ if (logPrefix) {
318
+ text = this._adjustText(logPrefix, text);
319
+ }
320
+ // fallback
321
+ if (!this.cli.terminal) {
322
+ if (total !== undefined && progress !== undefined) {
323
+ const progressValid = progress >= 0;
324
+ const progressText = `(${progressValid ? progress + 1 : '-'}/${total})`;
325
+ if (progressValid) {
326
+ text = this._adjustText(`${progressText}=> `, text);
327
+ }
328
+ }
329
+ // eslint-disable-next-line
330
+ console.log(text);
331
+ }
332
+ }
333
+ _adjustText(prefix, text) {
334
+ return String(text).split('\n').map(item => item ? prefix + item : item).join('\n');
335
+ }
336
+ }
337
+
338
+ class LocalHelper {
339
+ constructor(cli) {
340
+ this.cli = void 0;
341
+ this.processHelper = void 0;
342
+ this.cli = cli;
343
+ this.processHelper = new ProcessHelper(this.cwd, this.console);
344
+ }
345
+ get options() {
346
+ return this.cli.options;
347
+ }
348
+ get context() {
349
+ return this.cli.options.context;
350
+ }
351
+ get console() {
352
+ return this.cli.console;
353
+ }
354
+ get template() {
355
+ return this.cli.template;
356
+ }
357
+ get moduleConfig() {
358
+ return commandsConfig;
359
+ }
360
+ get chalk() {
361
+ return this.newChalk();
362
+ }
363
+ get Table() {
364
+ return TableClass;
365
+ }
366
+ get cwd() {
367
+ return this.context.argv.projectPath;
368
+ }
369
+ newChalk(options) {
370
+ if (!options) {
371
+ options = this.moduleConfig.helper.chalk.options;
372
+ }
373
+ return new chalk.Instance(options);
374
+ }
375
+ newTable(options) {
376
+ return new TableClass(options);
377
+ }
378
+ boxen({
379
+ text,
380
+ options
381
+ }) {
382
+ if (!options) {
383
+ options = this.moduleConfig.helper.boxen.options;
384
+ }
385
+ return boxen(text, options);
386
+ }
387
+ gogocode(sourceCode, options) {
388
+ return gogocode(sourceCode, options);
389
+ }
390
+ firstCharToLowerCase(name) {
391
+ return name.charAt(0).toLowerCase() + name.substring(1);
392
+ }
393
+ firstCharToUpperCase(name) {
394
+ return name.charAt(0).toUpperCase() + name.substring(1);
395
+ }
396
+ stringToCapitalize(str, separator) {
397
+ if (typeof str === 'string') str = str.split(separator);
398
+ return str.map(name => {
399
+ return this.firstCharToUpperCase(name);
400
+ }).join('');
401
+ }
402
+ slashPrefixForPath(count) {
403
+ if (count === 0) return './';
404
+ return '../'.repeat(count);
405
+ }
406
+ parseNameMeta(name, ignores) {
407
+ const original = name;
408
+ const parts = original.split('/');
409
+ const directory = parts.slice(0, parts.length - 1).join('/');
410
+ const short = parts[parts.length - 1];
411
+ const shortCapitalize = this.firstCharToUpperCase(short);
412
+ let partsFull;
413
+ if (ignores && parts.length > 1 && ignores.includes(parts[0])) {
414
+ partsFull = parts.slice(1);
415
+ } else {
416
+ partsFull = parts;
417
+ }
418
+ if (partsFull.length > 1 && partsFull[0] === partsFull[1]) {
419
+ partsFull = partsFull.slice(1);
420
+ }
421
+ const fullCapitalize = this.stringToCapitalize(partsFull, '/');
422
+ const full = this.firstCharToLowerCase(fullCapitalize);
423
+ return {
424
+ original,
425
+ parts,
426
+ directory,
427
+ short,
428
+ shortCapitalize,
429
+ full,
430
+ fullCapitalize
431
+ };
432
+ }
433
+ parseModuleInfo(moduleName) {
434
+ const moduleInfo = ModuleInfo.parseInfoPro(moduleName, process.env.CabloyCliBrandName, 'module');
435
+ if (!moduleInfo) throw new Error(`module name is not valid: ${moduleName}`);
436
+ return moduleInfo;
437
+ }
438
+ findModule(moduleName) {
439
+ const moduleInfo = this.parseModuleInfo(moduleName);
440
+ return this.cli.modulesMeta.modules[moduleInfo.relativeName];
441
+ }
442
+ parseSuiteInfo(suiteName) {
443
+ const suiteInfo = ModuleInfo.parseInfoPro(suiteName, process.env.CabloyCliBrandName, 'suite');
444
+ if (!suiteInfo) throw new Error(`suite name is not valid: ${suiteName}`);
445
+ return suiteInfo;
446
+ }
447
+ findSuite(suiteName) {
448
+ const suiteInfo = this.parseSuiteInfo(suiteName);
449
+ return this.cli.modulesMeta.suites[suiteInfo.relativeName];
450
+ }
451
+ async ensureDir(dir) {
452
+ await fse.ensureDir(dir);
453
+ return dir;
454
+ }
455
+ async pnpmInstall() {
456
+ // args
457
+ const args = ['install'];
458
+ // log
459
+ await this.console.log('===> pnpm install');
460
+ // spawn
461
+ await this.spawnCmd({
462
+ cmd: 'pnpm',
463
+ args
464
+ });
465
+ }
466
+ async formatFile({
467
+ fileName,
468
+ logPrefix
469
+ }) {
470
+ if (_formatFileDisable(fileName)) return;
471
+ return await this.processHelper.formatFile({
472
+ fileName,
473
+ logPrefix
474
+ });
475
+ }
476
+ async spawnBin({
477
+ cmd,
478
+ args,
479
+ options
480
+ }) {
481
+ return await this.processHelper.spawnBin({
482
+ cmd,
483
+ args,
484
+ options
485
+ });
486
+ }
487
+ async spawnCmd({
488
+ cmd,
489
+ args,
490
+ options
491
+ }) {
492
+ return await this.processHelper.spawnCmd({
493
+ cmd,
494
+ args,
495
+ options
496
+ });
497
+ }
498
+ async spawnExe({
499
+ cmd,
500
+ args,
501
+ options
502
+ }) {
503
+ return await this.processHelper.spawnExe({
504
+ cmd,
505
+ args,
506
+ options
507
+ });
508
+ }
509
+ async spawn({
510
+ cmd,
511
+ args = [],
512
+ options = {}
513
+ }) {
514
+ return await this.processHelper.spawn({
515
+ cmd,
516
+ args,
517
+ options
518
+ });
519
+ }
520
+ async gitCommit({
521
+ cwd,
522
+ message
523
+ }) {
524
+ return await this.processHelper.gitCommit(message, {
525
+ cwd
526
+ });
527
+ }
528
+ async getRegistry() {
529
+ return await getRegistry();
530
+ }
531
+ parseBrandPath() {
532
+ const require = createRequire(import.meta.url);
533
+ const modulePath = require.resolve(`${process.env.CabloyCliBrandName}-cli/package.json`);
534
+ // ts or js
535
+ let file = path.join(path.dirname(modulePath), `src/bin/${process.env.CabloyCliBrandName}.ts`);
536
+ if (!fse.existsSync(file)) {
537
+ file = path.join(path.dirname(modulePath), `dist/bin/${process.env.CabloyCliBrandName}.js`);
538
+ }
539
+ return file;
540
+ }
541
+ async invokeCli(args, options) {
542
+ // tsx: spawnCmd, node: spawnExe
543
+ await this.processHelper.spawnCmd({
544
+ cmd: 'tsx',
545
+ args: [this.parseBrandPath()].concat(args),
546
+ // cmd: 'node',
547
+ // args: ['--experimental-transform-types', getImportEsm(), this.parseBrandPath()].concat(args),
548
+ options
549
+ });
550
+ }
551
+ async loadJSONFile(fileName) {
552
+ const pkgContent = (await fse.readFile(fileName)).toString();
553
+ return JSON.parse(pkgContent);
554
+ }
555
+ async saveJSONFile(fileName, json, format) {
556
+ await fse.writeFile(fileName, `${JSON.stringify(json, null, 2)}\n`);
557
+ if (format !== false) {
558
+ await this.formatFile({
559
+ fileName
560
+ });
561
+ }
562
+ }
563
+ safeSplit(str, sep = ',') {
564
+ let left = 0;
565
+ let start = 0;
566
+ const result = [];
567
+ while (start < str.length) {
568
+ let end = start;
569
+ while (end < str.length) {
570
+ if (str[end] === sep && left === 0) {
571
+ result.push(str.substring(start, end));
572
+ start = end + 1;
573
+ break;
574
+ }
575
+ if (str[end] === '<') left++;
576
+ if (str[end] === '>') left--;
577
+ end++;
578
+ }
579
+ if (start < end) {
580
+ result.push(str.substring(start, end));
581
+ start = end + 1;
582
+ }
583
+ }
584
+ if (start <= str.length) {
585
+ result.push(str.substring(start, str.length));
586
+ }
587
+ return result;
588
+ }
589
+ async removeGitkeep(parentPath) {
590
+ const gitkeep = path.join(parentPath, '.gitkeep');
591
+ if (fse.existsSync(gitkeep)) {
592
+ await fse.remove(gitkeep);
593
+ }
594
+ }
595
+ combineModuleNameAndResource(moduleName, resourceName) {
596
+ return combineResourceName(resourceName, moduleName, true, true);
597
+ }
598
+ async importDynamic(fileName, fn) {
599
+ // load
600
+ const instance = await import(this.pathToHref(fileName));
601
+ if (!fn) return instance;
602
+ return await fn(instance);
603
+ }
604
+ requireDynamic(file) {
605
+ if (!file) throw new Error('file should not empty');
606
+ const require = createRequire(import.meta.url);
607
+ let instance = require(file);
608
+ const mtime = this._requireDynamic_getFileTime(file);
609
+ if (instance.__requireDynamic_mtime === undefined) {
610
+ instance.__requireDynamic_mtime = mtime;
611
+ } else if (instance.__requireDynamic_mtime !== mtime) {
612
+ delete require.cache[require.resolve(file)];
613
+ instance = require(file);
614
+ instance.__requireDynamic_mtime = mtime;
615
+ }
616
+ return instance;
617
+ }
618
+ _requireDynamic_getFileTime(file) {
619
+ if (!path.isAbsolute(file)) return null;
620
+ const exists = fse.pathExistsSync(file);
621
+ if (!exists) return null;
622
+ // stat
623
+ const stat = fse.statSync(file);
624
+ return stat.mtime.valueOf();
625
+ }
626
+ async tempFile(fn, options) {
627
+ // temp
628
+ const fileTempObj = tmp.fileSync(options);
629
+ const fileTemp = fileTempObj.name;
630
+ try {
631
+ return await fn(fileTemp);
632
+ } finally {
633
+ // delete temp
634
+ fileTempObj.removeCallback();
635
+ }
636
+ }
637
+ pathToHref(fileName) {
638
+ return pathToFileURL(fileName).href;
639
+ }
640
+ }
641
+ function _formatFileDisable(fileName) {
642
+ const baseName = path.basename(fileName);
643
+ if (/.env\..*$/.test(baseName)) return true;
644
+ if (['.env', 'docker-compose-dockerfile-app', 'docker-compose.yml'].includes(baseName)) return true;
645
+ return false;
646
+ }
647
+
648
+ // async importDynamic<RESULT>(fileName: string, fn: (instance: any) => Promise<RESULT>): Promise<RESULT> {
649
+ // return await this.tempFile(
650
+ // async fileTemp => {
651
+ // // build
652
+ // const esBuildConfig = this._createEsbuildConfig(fileName, fileTemp);
653
+ // await esBuild(esBuildConfig as any);
654
+ // // load
655
+ // const instance = await import(this._pathToHref(fileTemp));
656
+ // return await fn(instance);
657
+ // },
658
+ // {
659
+ // tmpdir: path.dirname(fileName),
660
+ // prefix: '.temp-dynamic-',
661
+ // postfix: '.mjs',
662
+ // },
663
+ // );
664
+ // }
665
+
666
+ // private _createEsbuildConfig(fileSrc: string, fileDest: string) {
667
+ // return {
668
+ // platform: 'node',
669
+ // format: 'esm',
670
+ // bundle: true,
671
+ // packages: 'external',
672
+ // resolveExtensions: ['.mjs', '.js', '.mts', '.ts', '.json'],
673
+ // entryPoints: [fileSrc],
674
+ // outfile: fileDest,
675
+ // };
676
+ // }
677
+
678
+ class LocalTemplate {
679
+ constructor(cli) {
680
+ this.cli = void 0;
681
+ this.cli = cli;
682
+ }
683
+ get options() {
684
+ return this.cli.options;
685
+ }
686
+ get context() {
687
+ return this.cli.options.context;
688
+ }
689
+ get console() {
690
+ return this.cli.console;
691
+ }
692
+ get helper() {
693
+ return this.cli.helper;
694
+ }
695
+ get moduleConfig() {
696
+ return commandsConfig;
697
+ }
698
+ get fileMapping() {
699
+ return this.moduleConfig.template.render.fileMapping;
700
+ }
701
+ get filesIgnore() {
702
+ return this.moduleConfig.template.render.ignore;
703
+ }
704
+ resolveTemplatePath(setName, _path) {
705
+ if (path.isAbsolute(_path)) return _path;
706
+ const sets = this.moduleConfig.sets;
707
+ const require = createRequire(import.meta.url);
708
+ const modulePath = require.resolve(`${sets[process.env.CabloyCliBrandName][setName]}/package.json`);
709
+ return path.join(path.dirname(modulePath), 'cli/templates', _path);
710
+ }
711
+ async renderBoilerplateAndSnippets({
712
+ targetDir,
713
+ setName,
714
+ snippetsPath,
715
+ boilerplatePath
716
+ }) {
717
+ await this.helper.ensureDir(targetDir);
718
+ // first
719
+ if (snippetsPath) {
720
+ const snippetsDir = this.resolveTemplatePath(setName, snippetsPath);
721
+ await this.applySnippets(targetDir, snippetsDir);
722
+ }
723
+ // then
724
+ if (boilerplatePath) {
725
+ const templateDir = this.resolveTemplatePath(setName, boilerplatePath);
726
+ await this.renderDir(targetDir, templateDir);
727
+ }
728
+ }
729
+ async renderDir(targetDir, templateDir) {
730
+ const {
731
+ argv
732
+ } = this.context;
733
+ // files
734
+ const files = await globby('**/*', {
735
+ cwd: templateDir,
736
+ dot: true,
737
+ onlyFiles: false
738
+ });
739
+ // loop
740
+ for (const file of files) {
741
+ const {
742
+ dir: dirname,
743
+ base: basename
744
+ } = path.parse(file);
745
+ if (this.filesIgnore.includes(basename)) continue;
746
+ const templateFile = path.join(templateDir, file);
747
+ const fileName = this.parseFileBaseName(basename);
748
+ const parentPath = path.join(targetDir, dirname);
749
+ const targetFile = path.join(parentPath, this.replaceTemplate(fileName, argv));
750
+ await this.renderFile({
751
+ targetFile,
752
+ templateFile
753
+ });
754
+ if (fileName !== '.gitkeep') {
755
+ await this.helper.removeGitkeep(parentPath);
756
+ }
757
+ }
758
+ return files;
759
+ }
760
+ replaceTemplate(content, scope) {
761
+ if (!content) return null;
762
+ return content.toString().replace(/(\\)?\{\{ *([\w.]+) *\}\}/g, (block, skip, key) => {
763
+ if (skip) {
764
+ return block.substring(skip.length);
765
+ }
766
+ const value = this.getProperty(scope, key);
767
+ return value !== undefined ? value : '';
768
+ });
769
+ }
770
+ getProperty(obj, name, sep) {
771
+ return this._getProperty(obj, name, sep, false);
772
+ }
773
+ _getProperty(obj, name, sep, forceObject) {
774
+ if (!obj) return undefined;
775
+ const names = name.split(sep || '.');
776
+ // loop
777
+ for (const name of names) {
778
+ if (obj[name] === undefined || obj[name] === null) {
779
+ if (forceObject) {
780
+ obj[name] = {};
781
+ } else {
782
+ obj = obj[name];
783
+ break;
784
+ }
785
+ }
786
+ obj = obj[name];
787
+ }
788
+ return obj;
789
+ }
790
+ parseFileBaseName(basename) {
791
+ let fileName = this.fileMapping[basename] || basename;
792
+ if (fileName.lastIndexOf('_') === fileName.length - 1) {
793
+ fileName = fileName.substring(0, fileName.length - 1);
794
+ }
795
+ return fileName;
796
+ }
797
+ async renderFile({
798
+ targetFile,
799
+ templateFile
800
+ }) {
801
+ const stats = fs.lstatSync(templateFile);
802
+ if (stats.isSymbolicLink()) {
803
+ const target = fs.readlinkSync(templateFile);
804
+ fs.symlinkSync(target, targetFile);
805
+ await this.console.log(`${targetFile} link to ${target}`);
806
+ } else if (stats.isDirectory()) {
807
+ await this.helper.ensureDir(targetFile);
808
+ } else if (stats.isFile()) {
809
+ const content = fs.readFileSync(templateFile);
810
+ await this.console.log(`write to ${targetFile}`);
811
+ // check if content is a text file
812
+ let result;
813
+ let changed;
814
+ if (!isTextOrBinary.isTextSync(templateFile, content)) {
815
+ result = content;
816
+ } else {
817
+ const _content = content.toString('utf8');
818
+ result = await this.renderContent({
819
+ content: _content
820
+ });
821
+ changed = _content !== result;
822
+ }
823
+ // save
824
+ fs.writeFileSync(targetFile, result);
825
+ // format
826
+ if (changed && !this.context.argv.noformat) {
827
+ await catchError(() => {
828
+ return this.helper.formatFile({
829
+ fileName: targetFile,
830
+ logPrefix: 'format: '
831
+ });
832
+ });
833
+ }
834
+ } else {
835
+ await this.console.log(`ignore ${templateFile}, only support file, dir, symlink`);
836
+ }
837
+ }
838
+ async renderContent({
839
+ content
840
+ }) {
841
+ if (!content) return content;
842
+ const data = this.getEjsData();
843
+ const options = this.getEjsOptions();
844
+ return await ejs.render(content, data, options);
845
+ }
846
+ getEjsOptions() {
847
+ return {
848
+ async: true,
849
+ cache: false,
850
+ compileDebug: true,
851
+ outputFunctionName: 'echo',
852
+ rmWhitespace: false
853
+ };
854
+ }
855
+ getEjsData() {
856
+ return {
857
+ cli: this.cli,
858
+ ...this.context
859
+ };
860
+ }
861
+ getAstData(ast, snippet) {
862
+ return {
863
+ cli: this.cli,
864
+ ast,
865
+ snippet,
866
+ ...this.context
867
+ };
868
+ }
869
+ getInitData(targetFile) {
870
+ return {
871
+ cli: this.cli,
872
+ targetFile,
873
+ ...this.context
874
+ };
875
+ }
876
+ async applySnippets(targetDir, snippetsDir) {
877
+ // snippets
878
+ let files = await globby('*.{cjs,ts}', {
879
+ cwd: snippetsDir,
880
+ onlyFiles: true
881
+ });
882
+ // snippets sort
883
+ files = files.filter(item => item[0] !== '-').sort((a, b) => this._parseSnippetFilePrefix(a) - this._parseSnippetFilePrefix(b));
884
+ // for
885
+ for (const file of files) {
886
+ const snippetTemplatePath = path.join(snippetsDir, file);
887
+ await this._loadSnippetInstance(snippetTemplatePath, async snippet => {
888
+ if (!snippet.file) {
889
+ throw new Error(`should provider file path for: ${file}`);
890
+ }
891
+ let fileName;
892
+ if (typeof snippet.file === 'function') {
893
+ fileName = snippet.file(this.getEjsData());
894
+ } else {
895
+ fileName = await this.renderContent({
896
+ content: snippet.file
897
+ });
898
+ }
899
+ if (!fileName) ; else {
900
+ const targetFile = path.join(targetDir, fileName);
901
+ await this.applySnippet(targetFile, snippet);
902
+ }
903
+ });
904
+ }
905
+ }
906
+ async _loadSnippetInstance(snippetTemplatePath, fn) {
907
+ return await this.helper.importDynamic(snippetTemplatePath, instance => {
908
+ return fn(instance.default);
909
+ });
910
+ }
911
+ async applySnippet(targetFile, snippet) {
912
+ await this.console.log(`apply changes to ${targetFile}`);
913
+ // source code
914
+ let sourceCode;
915
+ if (fs.existsSync(targetFile)) {
916
+ sourceCode = fs.readFileSync(targetFile);
917
+ sourceCode = sourceCode.toString('utf8');
918
+ } else {
919
+ if (!snippet.init) {
920
+ throw new Error(`should provider init content for: ${targetFile}`);
921
+ }
922
+ let content;
923
+ if (typeof snippet.init === 'function') {
924
+ content = await snippet.init(this.getInitData(targetFile));
925
+ } else {
926
+ content = snippet.init;
927
+ }
928
+ sourceCode = await this.renderContent({
929
+ content
930
+ });
931
+ }
932
+ // language
933
+ const language = snippet.language;
934
+ // transform
935
+ let outputCode;
936
+ if (language === 'plain') {
937
+ const ast = sourceCode;
938
+ const outAst = await snippet.transform(this.getAstData(ast, snippet));
939
+ outputCode = outAst;
940
+ } else if (language === 'json') {
941
+ const ast = JSON.parse(sourceCode);
942
+ const outAst = await snippet.transform(this.getAstData(ast, snippet));
943
+ outputCode = outAst === undefined ? outAst : JSON.stringify(outAst, null, 2);
944
+ } else {
945
+ const ast = gogocode(sourceCode, {
946
+ parseOptions: snippet.parseOptions
947
+ });
948
+ const outAst = await snippet.transform(this.getAstData(ast, snippet));
949
+ outputCode = outAst === undefined ? outAst : outAst.root().generate();
950
+ }
951
+ if (outputCode !== undefined) {
952
+ // save
953
+ fs.writeFileSync(targetFile, outputCode);
954
+ // format
955
+ if (snippet.format || !this.context.argv.noformat) {
956
+ await catchError(() => {
957
+ return this.helper.formatFile({
958
+ fileName: targetFile,
959
+ logPrefix: 'format: '
960
+ });
961
+ });
962
+ }
963
+ }
964
+ }
965
+ _parseSnippetFilePrefix(fileName) {
966
+ const num = fileName.split('-')[0];
967
+ if (!num || Number.isNaN(num)) return 10000;
968
+ return Number.parseInt(num);
969
+ }
970
+ }
971
+
972
+ class BeanCliBase {
973
+ constructor(options) {
974
+ this.options = void 0;
975
+ this.terminal = void 0;
976
+ this.__console = void 0;
977
+ this.__helper = void 0;
978
+ this.__template = void 0;
979
+ this.__common = void 0;
980
+ this.modulesMeta = void 0;
981
+ this.options = options;
982
+ this.terminal = options.terminal !== false;
983
+ }
984
+ get console() {
985
+ if (!this.__console) {
986
+ this.__console = new LocalConsole(this);
987
+ }
988
+ return this.__console;
989
+ }
990
+ get helper() {
991
+ if (!this.__helper) {
992
+ this.__helper = new LocalHelper(this);
993
+ }
994
+ return this.__helper;
995
+ }
996
+ get template() {
997
+ if (!this.__template) {
998
+ this.__template = new LocalTemplate(this);
999
+ }
1000
+ return this.__template;
1001
+ }
1002
+ get common() {
1003
+ if (!this.__common) {
1004
+ this.__common = new LocalCommon(this);
1005
+ }
1006
+ return this.__common;
1007
+ }
1008
+ get context() {
1009
+ return this.options.context;
1010
+ }
1011
+ get cliFullName() {
1012
+ return this.options.context.argv.cliFullName;
1013
+ }
1014
+ async meta() {
1015
+ await this._loadModulesMeta();
1016
+ const metaLocale = this._commandMeta();
1017
+ return metaLocale;
1018
+ }
1019
+ async execute() {
1020
+ const {
1021
+ argv
1022
+ } = this.context;
1023
+ if (argv.flavor) {
1024
+ argv.flavor = patchFlavor(argv.flavor);
1025
+ }
1026
+ await this._loadModulesMeta();
1027
+ }
1028
+ async _loadModulesMeta() {
1029
+ //
1030
+ if (this.modulesMeta) return;
1031
+ // all modules
1032
+ this.modulesMeta = await glob({
1033
+ projectPath: this.context.argv.projectPath,
1034
+ disabledModules: undefined,
1035
+ disabledSuites: undefined,
1036
+ log: false,
1037
+ projectMode: process.env.CabloyCliBrandName
1038
+ });
1039
+ }
1040
+ _commandMeta() {
1041
+ const {
1042
+ command
1043
+ } = this.options;
1044
+ const {
1045
+ argv
1046
+ } = this.context;
1047
+ const meta = {};
1048
+ meta.info = this._commandMeta_info({
1049
+ info: command.info,
1050
+ argv
1051
+ });
1052
+ meta.options = this._commandMeta_options({
1053
+ options: command.options,
1054
+ argv
1055
+ });
1056
+ meta.groups = this._commandMeta_groups({
1057
+ groups: command.groups,
1058
+ argv
1059
+ });
1060
+ return meta;
1061
+ }
1062
+ _commandMeta_groups({
1063
+ groups
1064
+ }) {
1065
+ const metaGroups = {};
1066
+ if (groups) {
1067
+ for (const groupName in groups) {
1068
+ const group = groups[groupName];
1069
+ metaGroups[groupName] = this._commandMeta_group({
1070
+ group
1071
+ });
1072
+ }
1073
+ }
1074
+ return metaGroups;
1075
+ }
1076
+ _commandMeta_group({
1077
+ group
1078
+ }) {
1079
+ const metaGroup = {
1080
+ description: group.description,
1081
+ condition: group.condition,
1082
+ questions: {}
1083
+ };
1084
+ for (const key in group.questions) {
1085
+ const question = group.questions[key];
1086
+ metaGroup.questions[key] = {
1087
+ ...question,
1088
+ message: question.message
1089
+ };
1090
+ }
1091
+ return metaGroup;
1092
+ }
1093
+ _commandMeta_options({
1094
+ options
1095
+ }) {
1096
+ const metaOptions = {};
1097
+ if (options) {
1098
+ for (const key in options) {
1099
+ const option = options[key];
1100
+ metaOptions[key] = {
1101
+ ...option,
1102
+ description: option.description
1103
+ };
1104
+ }
1105
+ }
1106
+ return metaOptions;
1107
+ }
1108
+ _commandMeta_info({
1109
+ info,
1110
+ argv
1111
+ }) {
1112
+ // info
1113
+ const metaInfo = {
1114
+ version: info.version,
1115
+ title: info.title,
1116
+ usage: info.usage
1117
+ };
1118
+ // usage
1119
+ if (!metaInfo.usage) {
1120
+ metaInfo.usage = `${'Usage'}: ${process.env.CabloyCliBrandName} ${argv.cliFullName} [options] [-h] [-v]`;
1121
+ }
1122
+ // welcomes
1123
+ metaInfo.welcomes = this._commandMeta_info_welcomes({
1124
+ info
1125
+ });
1126
+ // ok
1127
+ return metaInfo;
1128
+ }
1129
+ _commandMeta_info_welcomes({
1130
+ info
1131
+ }) {
1132
+ let welcomes = info.welcomes || [];
1133
+ if (!Array.isArray(welcomes)) welcomes = [welcomes];
1134
+ welcomes = welcomes.map(item => item);
1135
+ return welcomes;
1136
+ }
1137
+ }
1138
+
1139
+ let __commandsMeta;
1140
+ async function getCommandsMeta() {
1141
+ await collectCommands();
1142
+ return __commandsMeta;
1143
+ }
1144
+ function findCommand(cliFullName) {
1145
+ return __commandsMeta.map[cliFullName];
1146
+ }
1147
+ async function collectCommands() {
1148
+ await _collectCommands();
1149
+ }
1150
+ async function _collectCommands() {
1151
+ if (__commandsMeta) return;
1152
+ const _commandsMap = {};
1153
+ const _commandsAll = {};
1154
+ const sets = commandsConfig.sets[process.env.CabloyCliBrandName];
1155
+ for (const setName in sets) {
1156
+ const setModuleName = sets[setName];
1157
+ const setModule = await import(setModuleName); // 270ms
1158
+ const commands = setModule.commands;
1159
+ if (!commands) continue;
1160
+ const _commandsSet = _commandsAll[setName] = {};
1161
+ for (const groupName in commands) {
1162
+ const group = commands[groupName];
1163
+ const _commandsGroup = _commandsSet[groupName] = {};
1164
+ for (const key in group) {
1165
+ const command = group[key];
1166
+ const fullKey = `${setName}:${groupName}:${key}`;
1167
+ // command BeanClass
1168
+ const BeanClass = setModule.beans[command.bean];
1169
+ // ok
1170
+ _commandsMap[fullKey] = _commandsGroup[key] = {
1171
+ command,
1172
+ BeanClass
1173
+ };
1174
+ }
1175
+ }
1176
+ }
1177
+ // ok
1178
+ __commandsMeta = {
1179
+ map: _commandsMap,
1180
+ all: _commandsAll
1181
+ };
1182
+ }
1183
+
1184
+ class BeanCli {
1185
+ async meta({
1186
+ context
1187
+ }) {
1188
+ // command
1189
+ const {
1190
+ argv
1191
+ } = context;
1192
+ const cliFullName = argv.cliFullName;
1193
+ const {
1194
+ command,
1195
+ BeanClass
1196
+ } = await this._findCliCommand({
1197
+ cliFullName
1198
+ });
1199
+ // command bean
1200
+ const beanCommand = new BeanClass({
1201
+ command,
1202
+ context,
1203
+ terminal: false
1204
+ });
1205
+ if (!beanCommand) throw new Error(`cli command bean not found: ${command.beanFullName}`);
1206
+ // meta
1207
+ return await beanCommand.meta();
1208
+ }
1209
+ async execute({
1210
+ context
1211
+ }) {
1212
+ // command
1213
+ const {
1214
+ argv
1215
+ } = context;
1216
+ const cliFullName = argv.cliFullName;
1217
+ const {
1218
+ command,
1219
+ BeanClass
1220
+ } = await this._findCliCommand({
1221
+ cliFullName
1222
+ });
1223
+ // command bean
1224
+ const beanCommand = new BeanClass({
1225
+ command,
1226
+ context,
1227
+ terminal: false
1228
+ });
1229
+ if (!beanCommand) throw new Error(`cli command bean not found: ${command.beanFullName}`);
1230
+ // execute
1231
+ await beanCommand.execute();
1232
+ }
1233
+ _findCliCommand({
1234
+ cliFullName
1235
+ }) {
1236
+ const commandInfo = findCommand(cliFullName);
1237
+ if (!commandInfo || !commandInfo.command) throw new Error(`cli command not found: ${cliFullName}`);
1238
+ return commandInfo;
1239
+ }
1240
+ }
1241
+
1242
+ const __envFields = ['TERM', 'TERM_PROGRAM', 'TERM_PROGRAM_VERSION', 'SHELL', 'COLOR', 'LANG', 'npm_config_registry'];
1243
+ const __comment_seperator = '====================================================================';
1244
+ class CliCommand extends BaseCommand {
1245
+ constructor(rawArgv, {
1246
+ meta,
1247
+ argv
1248
+ }) {
1249
+ super(rawArgv);
1250
+ this.__meta = void 0;
1251
+ this.__groups = void 0;
1252
+ this.__argv = void 0;
1253
+ this.usage = meta.info.usage; // readonly
1254
+ this.options = meta.options; // readonly
1255
+ this.version = meta.info.version;
1256
+ this.__meta = meta;
1257
+ this.__groups = meta.groups;
1258
+ this.__argv = argv;
1259
+ }
1260
+ async run(options) {
1261
+ let {
1262
+ argv,
1263
+ cwd,
1264
+ env,
1265
+ rawArgv
1266
+ } = options;
1267
+ // argv
1268
+ argv = this._prepareArgv(argv);
1269
+ // context
1270
+ const context = {
1271
+ brandName: process.env.CabloyCliBrandName,
1272
+ argv,
1273
+ cwd,
1274
+ env: this._adjustEnv({
1275
+ env
1276
+ }),
1277
+ rawArgv
1278
+ };
1279
+ // log start
1280
+ if (!argv.dummy) {
1281
+ // eslint-disable-next-line no-console
1282
+ console.log(`${process.env.CabloyCliBrandName} ${chalk.cyan(argv.cliFullName)} at %s\n`, cwd);
1283
+ }
1284
+ // log meta welcomes
1285
+ if (!argv.dummy) {
1286
+ this._logMetaWelcomes();
1287
+ }
1288
+ // prompt
1289
+ await this._promptGroups({
1290
+ context,
1291
+ groups: this.__groups
1292
+ });
1293
+ // execute
1294
+ const beanCli = new BeanCli();
1295
+ await beanCli.execute({
1296
+ context
1297
+ });
1298
+ // done: log cli docs
1299
+ if (!argv.dummy) {
1300
+ this._logCliDocs();
1301
+ }
1302
+ // done
1303
+ // console.log(chalk.cyan('\n cli successfully!\n'));
1304
+ }
1305
+ _getMetaWelcomes() {
1306
+ let welcomes = this.__meta.info.welcomes;
1307
+ if (!welcomes) return null;
1308
+ if (!Array.isArray(welcomes)) welcomes = [welcomes];
1309
+ if (welcomes.length === 0) return null;
1310
+ return welcomes;
1311
+ }
1312
+ _logMetaWelcomes() {
1313
+ const welcomes = this._getMetaWelcomes();
1314
+ if (!welcomes) return;
1315
+ // eslint-disable-next-line no-console
1316
+ console.log(__comment_seperator);
1317
+ for (const welcome of welcomes) {
1318
+ // eslint-disable-next-line no-console
1319
+ console.log(welcome);
1320
+ }
1321
+ // eslint-disable-next-line no-console
1322
+ console.log(__comment_seperator);
1323
+ // eslint-disable-next-line no-console
1324
+ console.log('');
1325
+ }
1326
+ _logCliDocs() {
1327
+ const welcomes = this._getMetaWelcomes();
1328
+ if (!welcomes) return;
1329
+ const welcome = welcomes[0];
1330
+ if (!welcome || !welcome.includes('articles/cli-introduce.html')) return;
1331
+ // eslint-disable-next-line no-console
1332
+ console.log('');
1333
+ // eslint-disable-next-line no-console
1334
+ console.log(__comment_seperator);
1335
+ // eslint-disable-next-line no-console
1336
+ console.log(welcome);
1337
+ // eslint-disable-next-line no-console
1338
+ console.log(__comment_seperator);
1339
+ }
1340
+ _adjustEnv({
1341
+ env
1342
+ }) {
1343
+ const res = {};
1344
+ for (const field of __envFields) {
1345
+ if (env[field]) res[field] = env[field];
1346
+ }
1347
+ return res;
1348
+ }
1349
+ async _promptGroups({
1350
+ context,
1351
+ groups
1352
+ }) {
1353
+ for (const groupName in groups) {
1354
+ const group = groups[groupName];
1355
+ await this._promptGroup({
1356
+ group,
1357
+ context
1358
+ });
1359
+ }
1360
+ }
1361
+ async _promptGroup({
1362
+ group,
1363
+ context
1364
+ }) {
1365
+ const {
1366
+ argv
1367
+ } = context;
1368
+ // check
1369
+ const check = this._checkGroupCondition({
1370
+ group,
1371
+ context
1372
+ });
1373
+ if (!check) return;
1374
+ // prepare
1375
+ const varsWant = [];
1376
+ for (const key in group.questions) {
1377
+ const value = argv[key];
1378
+ if (value !== undefined) continue;
1379
+ const question = group.questions[key];
1380
+ const varWant = this._prepareQuestion({
1381
+ group,
1382
+ question,
1383
+ key,
1384
+ context
1385
+ });
1386
+ if (varWant) {
1387
+ varsWant.push(varWant);
1388
+ }
1389
+ }
1390
+ if (varsWant.length === 0) return;
1391
+ // log description
1392
+ if (group.description) {
1393
+ // eslint-disable-next-line no-console
1394
+ console.log('===>', group.description);
1395
+ }
1396
+ // prompt
1397
+ await enquirer.prompt(varsWant);
1398
+ }
1399
+ _prepareQuestionPropertyExpression({
1400
+ group,
1401
+ question,
1402
+ key,
1403
+ context,
1404
+ propName
1405
+ }) {
1406
+ // expression
1407
+ const expression = question[propName] && question[propName].expression;
1408
+ if (!expression) return null;
1409
+ return function (value) {
1410
+ return evaluate(expression, {
1411
+ value,
1412
+ group,
1413
+ question,
1414
+ key,
1415
+ context
1416
+ });
1417
+ };
1418
+ }
1419
+ _prepareQuestion({
1420
+ group,
1421
+ question,
1422
+ key,
1423
+ context
1424
+ }) {
1425
+ const {
1426
+ argv
1427
+ } = context;
1428
+ // want
1429
+ const varWant = {
1430
+ name: key,
1431
+ ...question
1432
+ };
1433
+ // message/skip/initial/format/validate
1434
+ for (const propName of ['message', 'skip', 'initial', 'format', 'validate']) {
1435
+ const propFunction = this._prepareQuestionPropertyExpression({
1436
+ group,
1437
+ question,
1438
+ key,
1439
+ context,
1440
+ propName
1441
+ });
1442
+ if (propFunction) {
1443
+ varWant[propName] = propFunction;
1444
+ }
1445
+ }
1446
+ // special check initial
1447
+ let initial = varWant.initial;
1448
+ if (initial && is.function(initial)) {
1449
+ initial = initial();
1450
+ if (initial !== undefined) {
1451
+ argv[key] = initial;
1452
+ return null;
1453
+ }
1454
+ }
1455
+ // result
1456
+ varWant.result = value => {
1457
+ const propFunction = this._prepareQuestionPropertyExpression({
1458
+ group,
1459
+ question,
1460
+ key,
1461
+ context,
1462
+ propName: 'result'
1463
+ });
1464
+ if (propFunction) {
1465
+ value = propFunction(value);
1466
+ }
1467
+ argv[key] = value;
1468
+ return value;
1469
+ };
1470
+ // required
1471
+ if (question.required) {
1472
+ varWant.validate = value => {
1473
+ if (!value) return 'Required';
1474
+ return true;
1475
+ };
1476
+ }
1477
+ // ok
1478
+ return varWant;
1479
+ }
1480
+ _checkGroupCondition({
1481
+ group,
1482
+ context
1483
+ }) {
1484
+ const expression = group.condition && group.condition.expression;
1485
+ if (!expression) return true;
1486
+ return evaluate(expression, {
1487
+ group,
1488
+ context
1489
+ });
1490
+ }
1491
+ _prepareArgv(argv) {
1492
+ argv = Object.assign({}, argv, this.__argv);
1493
+ delete argv.$0;
1494
+ // alias
1495
+ const options = this.__meta.options;
1496
+ if (options) {
1497
+ for (const key in options) {
1498
+ const option = options[key];
1499
+ if (option.alias && argv[key] === undefined) {
1500
+ argv[key] = argv[option.alias];
1501
+ }
1502
+ }
1503
+ }
1504
+ return argv;
1505
+ }
1506
+ }
1507
+
1508
+ const DISPATCH = Symbol.for('eb:Command#dispatch');
1509
+ const PARSE = Symbol.for('eb:Command#parse');
1510
+ class CabloyCommand extends BaseCommand {
1511
+ constructor(brandName, rawArgv) {
1512
+ super(rawArgv);
1513
+ this.brandName = void 0;
1514
+ this.defaultSetName = void 0;
1515
+ this.usage = `Usage: ${brandName} [command] [options]`;
1516
+ this.defaultSetName = brandName === 'zova' ? 'front' : 'api';
1517
+ this.brandName = brandName;
1518
+ process.env.CabloyCliBrandName = brandName;
1519
+ }
1520
+ async [DISPATCH]() {
1521
+ const parsed = await this[PARSE](this.rawArgv);
1522
+ if (parsed._.length === 0) {
1523
+ await super[DISPATCH]();
1524
+ return;
1525
+ }
1526
+ // checkForUpdates
1527
+ // checkForUpdates(`${this.brandName}-cli`);
1528
+ // collectCommands
1529
+ await collectCommands();
1530
+ // cli
1531
+ await this._handleCli();
1532
+ }
1533
+ async _handleCli() {
1534
+ // get parsed argument without handling helper and version
1535
+ const parsed = await this[PARSE](this.rawArgv);
1536
+ // argv
1537
+ const argv = {
1538
+ projectPath: process.cwd()
1539
+ };
1540
+ // indexBrandName
1541
+ const indexBrandName = this.rawArgv.indexOf(this.brandName);
1542
+ // cli
1543
+ const indexCommand = indexBrandName > -1 ? indexBrandName + 1 : 0;
1544
+ Object.assign(argv, this._prepareCliFullName(parsed._[indexCommand]));
1545
+ // cli meta
1546
+ const context = {
1547
+ brandName: this.brandName,
1548
+ argv
1549
+ };
1550
+ const beanCli = new BeanCli();
1551
+ const meta = await beanCli.meta({
1552
+ context
1553
+ });
1554
+ // cli run
1555
+ const rawArgv = this.rawArgv.slice();
1556
+ if (indexBrandName > -1) {
1557
+ rawArgv.splice(0, indexBrandName + 2);
1558
+ } else {
1559
+ rawArgv.splice(0, 1);
1560
+ }
1561
+ const command = new CliCommand(rawArgv, {
1562
+ meta,
1563
+ argv
1564
+ });
1565
+ await command[DISPATCH]();
1566
+ // should not force exit, let app shutdown gracefully
1567
+ // process.exit(0);
1568
+ }
1569
+ _prepareCliFullName(cliName) {
1570
+ if (!cliName) {
1571
+ return {
1572
+ cliFullName: `${this.defaultSetName}:default:list`
1573
+ };
1574
+ // throw new Error('Please specify the cli name');
1575
+ }
1576
+ const parts = cliName.split(':');
1577
+ if (parts.length === 1) {
1578
+ // means show module's commands
1579
+ parts[1] = '';
1580
+ }
1581
+ if (parts.length === 2) {
1582
+ if (parts[1]) {
1583
+ // means show group's commands
1584
+ parts[2] = '';
1585
+ } else {
1586
+ // means show module's commands
1587
+ if (!parts[0]) parts[0] = this.defaultSetName;
1588
+ return {
1589
+ cliFullName: `${this.defaultSetName}:default:list`,
1590
+ set: parts[0]
1591
+ };
1592
+ }
1593
+ }
1594
+ if (!parts[0]) parts[0] = this.defaultSetName;
1595
+ if (!parts[1]) parts[1] = 'default';
1596
+ if (!parts[2]) {
1597
+ // means show group's commands
1598
+ return {
1599
+ cliFullName: `${this.defaultSetName}:default:list`,
1600
+ set: parts[0],
1601
+ group: parts[1]
1602
+ };
1603
+ }
1604
+ // default
1605
+ return {
1606
+ cliFullName: parts.join(':')
1607
+ };
1608
+ }
1609
+ }
1610
+
1611
+ function metadataCustomSnippet(snippet) {
1612
+ return snippet;
1613
+ }
1614
+
1615
+ export { BeanCli, BeanCliBase, CabloyCommand, CliCommand, LocalConsole, LocalHelper, LocalTemplate, checkForUpdates, collectCommands, commandsConfig, findCommand, getCommandsMeta, getPackageInfo, getRegistry, metadataCustomSnippet, patchFlavor };