@cabloy/cli 3.0.66 → 3.0.68
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 +1615 -6
- package/dist/lib/local.common.d.ts +2 -0
- package/dist/types/template.d.ts +7 -0
- package/package.json +7 -8
- package/dist/config.js +0 -38
- package/dist/lib/bean.cli.base.js +0 -144
- package/dist/lib/bean.cli.js +0 -33
- package/dist/lib/cli.js +0 -217
- package/dist/lib/commands.js +0 -52
- package/dist/lib/index.js +0 -7
- package/dist/lib/local.common.js +0 -149
- package/dist/lib/local.console.js +0 -44
- package/dist/lib/local.helper.js +0 -329
- package/dist/lib/local.template.js +0 -291
- package/dist/registry.js +0 -14
- package/dist/start.js +0 -93
- package/dist/types/argv.js +0 -1
- package/dist/types/console.js +0 -1
- package/dist/types/helper.js +0 -1
- package/dist/types/index.js +0 -4
- package/dist/types/template.js +0 -3
- package/dist/utils.js +0 -46
package/dist/index.js
CHANGED
|
@@ -1,6 +1,1615 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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 };
|