mixcli 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.mjs ADDED
@@ -0,0 +1,1100 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __spreadValues = (a, b) => {
8
+ for (var prop in b || (b = {}))
9
+ if (__hasOwnProp.call(b, prop))
10
+ __defNormalProp(a, prop, b[prop]);
11
+ if (__getOwnPropSymbols)
12
+ for (var prop of __getOwnPropSymbols(b)) {
13
+ if (__propIsEnum.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ }
16
+ return a;
17
+ };
18
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
19
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
20
+ }) : x)(function(x) {
21
+ if (typeof require !== "undefined")
22
+ return require.apply(this, arguments);
23
+ throw Error('Dynamic require of "' + x + '" is not supported');
24
+ });
25
+ var __commonJS = (cb, mod) => function __require2() {
26
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
27
+ };
28
+ var __async = (__this, __arguments, generator) => {
29
+ return new Promise((resolve, reject) => {
30
+ var fulfilled = (value) => {
31
+ try {
32
+ step(generator.next(value));
33
+ } catch (e) {
34
+ reject(e);
35
+ }
36
+ };
37
+ var rejected = (value) => {
38
+ try {
39
+ step(generator.throw(value));
40
+ } catch (e) {
41
+ reject(e);
42
+ }
43
+ };
44
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
45
+ step((generator = generator.apply(__this, __arguments)).next());
46
+ });
47
+ };
48
+
49
+ // package.json
50
+ var require_package = __commonJS({
51
+ "package.json"(exports, module) {
52
+ module.exports = {
53
+ name: "mixcli",
54
+ version: "3.0.0",
55
+ description: "Develop command line tool scaffolding for monorepo",
56
+ repository: "https://github.com/zhangfisher/mixed-cli.git",
57
+ homepage: "https://zhangfisher.github.io/mixed-cli/",
58
+ main: "./dist/index.js",
59
+ module: "./dist/index.mjs",
60
+ types: "./dist/index.d.ts",
61
+ scripts: {
62
+ build: "tsup",
63
+ "build:watch": "tsup --watch",
64
+ release: "npm publish"
65
+ },
66
+ files: [
67
+ "dist",
68
+ "readme.md",
69
+ "package.json"
70
+ ],
71
+ keywords: [],
72
+ author: "",
73
+ license: "ISC",
74
+ dependencies: {
75
+ "@types/prompts": "^2.4.4",
76
+ "@voerkai18n/runtime": "^2.0.8",
77
+ "art-template": "^4.13.2",
78
+ commander: "^11.0.0",
79
+ "flex-tools": "^1.3.27",
80
+ "fs-extra": "^11.1.1",
81
+ glob: "^10.3.4",
82
+ logsets: "^1.3.7",
83
+ prompts: "^2.4.2",
84
+ "string.prototype.replaceall": "^1.0.7"
85
+ },
86
+ devDependencies: {
87
+ "@types/fs-extra": "^11.0.1",
88
+ "@types/node": "^20.5.7",
89
+ typescript: "^5.2.2"
90
+ }
91
+ };
92
+ }
93
+ });
94
+
95
+ // src/cli.ts
96
+ import "flex-tools/string";
97
+ import { LiteEvent } from "flex-tools/events/liteEvent";
98
+ import logsets2 from "logsets";
99
+ import { assignObject } from "flex-tools/object/assignObject";
100
+
101
+ // src/command.ts
102
+ import { Command } from "commander";
103
+ import prompts from "prompts";
104
+
105
+ // src/option.ts
106
+ import { Option } from "commander";
107
+
108
+ // src/utils.ts
109
+ import artTemplate from "art-template";
110
+ import fs from "fs-extra";
111
+ import path from "path";
112
+ import { promisify } from "flex-tools/func/promisify";
113
+ import logsets from "logsets";
114
+ function outputStr(str, vars) {
115
+ logsets.log(fixIndent(str), vars);
116
+ }
117
+ function fixIndent(text, indent) {
118
+ let indentValue = indent == void 0 || indent === true ? 0 : typeof indent == "number" ? indent : -1;
119
+ if (indentValue == -1)
120
+ return text;
121
+ let lines = text.split("\n");
122
+ let minSpaceCount = lines.reduce((minCount, line, index) => {
123
+ var _a;
124
+ if (index == 0)
125
+ return minCount;
126
+ const spaceCount = ((_a = line.match(/^\s*/)) == null ? void 0 : _a[0].length) || 0;
127
+ return Math.min(minCount, spaceCount);
128
+ }, 9999);
129
+ lines = lines.map((line) => line.substring(minSpaceCount));
130
+ return lines.join("\n");
131
+ }
132
+ function addBuiltInOptions(command) {
133
+ command.option("--work-dirs <values...>", "\u6307\u5B9A\u5DE5\u4F5C\u76EE\u5F55", { hidden: true, optional: true, required: true, prompt: false });
134
+ command.option("--disable-prompts", "\u7981\u7528\u6240\u6709\u4EA4\u4E92\u63D0\u793A", { hidden: true, prompt: false });
135
+ command.option("--debug-cli", "\u663E\u793A\u8C03\u8BD5\u4FE1\u606F", { hidden: true, prompt: false });
136
+ }
137
+ function isDebug() {
138
+ return process.argv.includes("--debug-cli");
139
+ }
140
+ function isEnablePrompts() {
141
+ return !process.argv.includes("--disable-prompts");
142
+ }
143
+ function outputDebug(message, ...args) {
144
+ let vars = args.length == 1 && typeof args[0] == "function" ? args[0]() : args;
145
+ if (isDebug())
146
+ logsets.log(`[MixCli] ${message}`, ...vars);
147
+ }
148
+ var fileExists = promisify(fs.exists, {
149
+ parseCallback: (results) => {
150
+ return results[0];
151
+ }
152
+ });
153
+ var readFile = promisify(fs.readFile);
154
+ var writeFile = promisify(fs.writeFile);
155
+ var mkdir = promisify(fs.mkdir);
156
+ function createFileByTemplate(_0, _1) {
157
+ return __async(this, arguments, function* (targetFile, tmplFile, vars = {}) {
158
+ tmplFile = path.isAbsolute(tmplFile) ? tmplFile : path.join(process.cwd(), tmplFile);
159
+ if (!fs.existsSync(tmplFile)) {
160
+ throw new Error("\u6A21\u677F\u6587\u4EF6\u4E0D\u5B58\u5728:" + tmplFile);
161
+ }
162
+ targetFile = path.isAbsolute(targetFile) ? targetFile : path.join(process.cwd(), targetFile);
163
+ const outPath = path.dirname(targetFile);
164
+ if (!(yield fileExists(outPath))) {
165
+ yield mkdir(outPath, { recursive: true });
166
+ }
167
+ const template = artTemplate(tmplFile, yield readFile(tmplFile, { encoding: "utf-8" }));
168
+ yield writeFile(targetFile, template(vars), { encoding: "utf-8" });
169
+ return targetFile;
170
+ });
171
+ }
172
+ function mkDirs(_0, _1) {
173
+ return __async(this, arguments, function* (dirs, { callback, base }) {
174
+ if (!Array.isArray(dirs))
175
+ throw new Error("dirs\u53C2\u6570\u5FC5\u987B\u4E3A\u5B57\u7B26\u4E32\u6570\u7EC4");
176
+ for (let dir of dirs) {
177
+ if (!path.isAbsolute(dir))
178
+ dir = path.join(base || process.cwd(), dir);
179
+ if (typeof callback == "function")
180
+ callback(dir);
181
+ yield mkdir(dir, { recursive: true });
182
+ }
183
+ });
184
+ }
185
+
186
+ // src/prompt.ts
187
+ var promptTypeMap = {
188
+ boolean: "confirm",
189
+ string: "text",
190
+ number: "number",
191
+ array: "list"
192
+ };
193
+ var supportedPromptTypes = ["text", "password", "invisible", "number", "confirm", "list", "toggle", "select", "multiselect", "autocomplete", "date", "autocompleteMultiselect"];
194
+ var PromptManager = class {
195
+ // 对应的FlexOption或FlexArgument
196
+ constructor(promptable, promptArgs) {
197
+ this._promptable = promptable;
198
+ this.args = promptArgs === void 0 ? "auto" : promptArgs;
199
+ }
200
+ /**
201
+ * 返回输入的是否是有效的prompt类型
202
+ * @param type
203
+ * @returns
204
+ */
205
+ isValid(type) {
206
+ return supportedPromptTypes.includes(String(type));
207
+ }
208
+ /**
209
+ * 推断是否需要提示
210
+ *
211
+ */
212
+ isNeed(input, defaultValue) {
213
+ const promptArg = this.args;
214
+ const inputValue = input || defaultValue;
215
+ const hasInput = !(inputValue === void 0);
216
+ if (promptArg === true)
217
+ return true;
218
+ if (promptArg === false)
219
+ return false;
220
+ if (typeof promptArg == "object") {
221
+ return !hasInput;
222
+ }
223
+ if (typeof promptArg == "string" && supportedPromptTypes.includes(promptArg)) {
224
+ return !hasInput;
225
+ }
226
+ if (this._promptable.argChoices && this._promptable.argChoices.indexOf(inputValue) == -1) {
227
+ return true;
228
+ }
229
+ return !hasInput;
230
+ }
231
+ /**
232
+ * 返回生成prompt对象
233
+ *
234
+ * @param inputValue 从命令行输入的值
235
+ */
236
+ get(inputValue) {
237
+ const { description, promptChoices, validate, defaultValue } = this._promptable;
238
+ let input = inputValue || defaultValue;
239
+ if (!this.isNeed(input, defaultValue))
240
+ return;
241
+ let promptType = this.infer(inputValue);
242
+ const prompt = __spreadValues({
243
+ type: promptType,
244
+ name: this._promptable.name(),
245
+ message: description,
246
+ initial: input
247
+ }, typeof this.args == "object" ? this.args : {});
248
+ prompt.validate = validate == null ? void 0 : validate.bind(this._promptable);
249
+ if (promptType == "multiselect")
250
+ prompt.instructions = false;
251
+ if (["select", "multiselect"].includes(promptType)) {
252
+ let index = promptChoices == null ? void 0 : promptChoices.findIndex((item) => item.value == input);
253
+ prompt.initial = index == -1 ? void 0 : index;
254
+ }
255
+ if (Array.isArray(promptChoices)) {
256
+ prompt.choices = promptChoices;
257
+ }
258
+ return prompt;
259
+ }
260
+ /**
261
+ * 推断prompt类型
262
+ *
263
+ * @param inputValue 从命令行输入的值
264
+ */
265
+ infer(inputValue) {
266
+ const { argChoices, variadic, defaultValue } = this._promptable;
267
+ let input = inputValue || defaultValue;
268
+ let promptType = /(\<[\w\.]+\>)|(\[[\w\.]+\])/.test(this._promptable.flags) ? "text" : "confirm";
269
+ let promptArg = this.args;
270
+ if (this.isValid(promptArg)) {
271
+ promptType = promptArg;
272
+ } else {
273
+ if (typeof promptArg == "object") {
274
+ promptType = promptArg.type;
275
+ } else {
276
+ if (argChoices) {
277
+ promptType = variadic ? "multiselect" : "select";
278
+ } else {
279
+ const datatype = Array.isArray(defaultValue) ? "array" : typeof defaultValue;
280
+ if (Array.isArray(input) || variadic) {
281
+ promptType = "list";
282
+ } else {
283
+ if (datatype in promptTypeMap) {
284
+ promptType = promptTypeMap[datatype];
285
+ }
286
+ }
287
+ }
288
+ }
289
+ }
290
+ outputDebug("\u9009\u9879<{}> -> \u63D0\u793A\u7C7B\u578B<{}>", [this._promptable.name(), promptType]);
291
+ return promptType;
292
+ }
293
+ };
294
+
295
+ // src/option.ts
296
+ var MixOption = class extends Option {
297
+ constructor(flags, description, optsOrDefault) {
298
+ super(flags, description);
299
+ let params = {};
300
+ if (arguments.length == 3 && typeof arguments[2] == "object") {
301
+ params = Object.assign({}, arguments[2]);
302
+ } else if (arguments.length == 3) {
303
+ params.default = arguments[2];
304
+ }
305
+ if (params.prompt === void 0)
306
+ params.prompt = "auto";
307
+ if (params.default)
308
+ this.default(params.default, params.defaultDescription);
309
+ if (params.choices)
310
+ this.choices(params.choices);
311
+ if (params.conflicts)
312
+ this.conflicts(params.conflicts);
313
+ if (params.env)
314
+ this.env(params.env);
315
+ if (params.argParser)
316
+ this.argParser(params.argParser);
317
+ if (params.hideHelp)
318
+ this.hideHelp(params.hideHelp);
319
+ if (params.hidden)
320
+ this.hidden = params.hidden;
321
+ if (params.mandatory)
322
+ this.makeOptionMandatory(params.mandatory);
323
+ if (params.implies)
324
+ this.implies(params.implies);
325
+ if (params.optional)
326
+ this.optional = params.optional;
327
+ if (typeof params.validate == "function")
328
+ this._validate = params.validate.bind(this);
329
+ if (params.required) {
330
+ this.required = params.required;
331
+ if (!this._validate)
332
+ this._validate = (value) => String(value).length > 0;
333
+ }
334
+ this.prompt = new PromptManager(this, params.prompt);
335
+ }
336
+ validate(value) {
337
+ if (typeof this._validate == "function") {
338
+ return this._validate(value);
339
+ } else {
340
+ return true;
341
+ }
342
+ }
343
+ // @ts-ignore
344
+ choices(values) {
345
+ if (!this.promptChoices) {
346
+ this.promptChoices = values.map((choice) => {
347
+ if (typeof choice == "object") {
348
+ return choice;
349
+ } else {
350
+ return { title: choice, value: choice };
351
+ }
352
+ });
353
+ }
354
+ super.choices(this.promptChoices.map((item) => item.value));
355
+ }
356
+ resetChoices() {
357
+ super.choices(this.promptChoices.map((item) => item.value));
358
+ }
359
+ addChoice(value) {
360
+ if (!this.promptChoices || !Array.isArray(this.promptChoices))
361
+ this.promptChoices = [];
362
+ this.promptChoices.push(typeof value == "string" ? { title: value, value } : value);
363
+ this.resetChoices();
364
+ }
365
+ removeChoice(value) {
366
+ var _a;
367
+ this.promptChoices = (_a = this.promptChoices) == null ? void 0 : _a.filter((choice) => choice.value !== value);
368
+ this.resetChoices();
369
+ }
370
+ clearChoice() {
371
+ this.promptChoices = [];
372
+ this.resetChoices();
373
+ }
374
+ /**
375
+ * 返回选项的提示对象
376
+ *
377
+ * @remarks
378
+ *
379
+ *
380
+ *
381
+ * @param inputValue
382
+ * @returns
383
+ */
384
+ getPrompt(inputValue) {
385
+ var _a;
386
+ return (_a = this.prompt) == null ? void 0 : _a.get(inputValue);
387
+ }
388
+ };
389
+
390
+ // src/command.ts
391
+ import path2 from "path";
392
+ import fs2 from "fs";
393
+ var BREAK = Symbol("BREAK_ACTION");
394
+ var MixCommand = class extends Command {
395
+ // 是否启用交互提示
396
+ constructor(name) {
397
+ super(name);
398
+ this._beforeHooks = [];
399
+ this._afterHooks = [];
400
+ this._customPrompts = [];
401
+ this._optionValues = {};
402
+ // 命令行输入的选项值
403
+ this._actions = [];
404
+ // 允许多个action
405
+ this._enable_prompts = true;
406
+ const self = this;
407
+ if (!this.isRoot)
408
+ addBuiltInOptions(this);
409
+ this.hook("preAction", function() {
410
+ return __async(this, arguments, function* () {
411
+ self._optionValues = self.getOptionValues(this.hookedCommand);
412
+ try {
413
+ yield self.preActionHook.apply(self, arguments);
414
+ } catch (e) {
415
+ }
416
+ });
417
+ });
418
+ }
419
+ /**
420
+ * 是否是根命令
421
+ */
422
+ get isRoot() {
423
+ return !!!this.parent;
424
+ }
425
+ get actions() {
426
+ return this._actions;
427
+ }
428
+ get beforeHooks() {
429
+ return this._beforeHooks;
430
+ }
431
+ get afterHooks() {
432
+ return this._afterHooks;
433
+ }
434
+ get fullname() {
435
+ let names = [this.name()];
436
+ let parent = this.parent;
437
+ while (parent) {
438
+ if (parent.name() !== "root") {
439
+ names.unshift(parent.name());
440
+ }
441
+ parent = parent.parent;
442
+ }
443
+ return names.join(".");
444
+ }
445
+ /**
446
+ * 返回根命令
447
+ */
448
+ root() {
449
+ let root = this;
450
+ while (root && root.parent != null) {
451
+ root = root.parent;
452
+ }
453
+ return root;
454
+ }
455
+ action(fn) {
456
+ const actionFunc = arguments[0];
457
+ if (arguments.length == 1 && typeof actionFunc == "function") {
458
+ this._actions.push({
459
+ id: Math.random().toString(36).substring(2),
460
+ enhance: false,
461
+ fn: actionFunc
462
+ });
463
+ } else if (arguments.length == 2 && typeof actionFunc == "function" && typeof arguments[1] == "object") {
464
+ const actionFn = arguments[0];
465
+ const actionOpts = Object.assign({ at: "append" }, arguments[1]);
466
+ if (actionOpts.at == "replace")
467
+ this._actions = [];
468
+ const actionItem = {
469
+ id: actionOpts.id || Math.random().toString(36).substring(2),
470
+ enhance: actionOpts.enhance == void 0 ? true : actionOpts.enhance,
471
+ fn: actionFn
472
+ };
473
+ if (typeof actionOpts.at == "number") {
474
+ this._actions.splice(Number(actionOpts.at), 0, actionItem);
475
+ } else if (["append", "before"].includes(actionOpts.at)) {
476
+ this._actions.push(actionItem);
477
+ } else if (["preappend", "after"].includes(actionOpts.at)) {
478
+ this._actions.splice(0, 0, actionItem);
479
+ } else {
480
+ this._actions.push(actionItem);
481
+ }
482
+ } else {
483
+ console.log("[mixed-cli] action params error");
484
+ }
485
+ return super.action(this.getWrapperedAction());
486
+ }
487
+ /**
488
+ * 读取命令配置值,包括父命令提供的配置选项
489
+ * @param command
490
+ */
491
+ getOptionValues(command) {
492
+ let opts = {};
493
+ let parent = command;
494
+ while (parent) {
495
+ Object.assign(opts, parent._optionValues);
496
+ parent = parent.parent;
497
+ }
498
+ return opts;
499
+ }
500
+ /**
501
+ * 本函数在运行时子类进行action生成该命令的action
502
+ */
503
+ getWrapperedAction() {
504
+ return this.wrapperWorkDirsAction(this.wrapperChainActions());
505
+ }
506
+ /**
507
+ * 向上查找所有祖先命令
508
+ */
509
+ getAncestorCommands() {
510
+ let cmds = [];
511
+ let cmd = this;
512
+ while (cmd) {
513
+ cmd = cmd.parent;
514
+ if (cmd) {
515
+ cmds.push(cmd);
516
+ }
517
+ }
518
+ return cmds;
519
+ }
520
+ /***
521
+ * 将所有actions包装成一个链式调用的函数
522
+ */
523
+ wrapperChainActions() {
524
+ const self = this;
525
+ return function() {
526
+ return __async(this, arguments, function* () {
527
+ const args = Array.from(arguments);
528
+ let preValue;
529
+ let actionOpts = {}, actionArgs = [], cmd;
530
+ if (args.length >= 2) {
531
+ cmd = args[args.length - 1];
532
+ actionOpts = args[args.length - 2];
533
+ actionArgs = args.slice(0, args.length - 2);
534
+ }
535
+ yield self.executeBeforeHooks({ args: actionArgs, options: actionOpts, command: cmd });
536
+ try {
537
+ for (let action of self._actions) {
538
+ try {
539
+ if (action.enhance) {
540
+ outputDebug("\u6267\u884C<{}>: args={}, options={}", () => [
541
+ self.name(),
542
+ actionArgs,
543
+ actionOpts
544
+ ]);
545
+ preValue = yield action.fn.call(this, {
546
+ command: cmd,
547
+ value: preValue,
548
+ args: actionArgs,
549
+ options: actionOpts
550
+ });
551
+ } else {
552
+ preValue = yield action.fn.apply(this, args);
553
+ }
554
+ if (preValue === BREAK)
555
+ break;
556
+ } catch (e) {
557
+ outputDebug("\u547D\u4EE4{}\u7684Action({})\u6267\u884C\u51FA\u9519:{}", [self.name, action.id, e]);
558
+ throw e;
559
+ }
560
+ }
561
+ } finally {
562
+ yield self.executeAfterHooks({
563
+ value: preValue,
564
+ args: actionArgs,
565
+ options: actionOpts,
566
+ command: cmd
567
+ });
568
+ }
569
+ });
570
+ };
571
+ }
572
+ /**
573
+ * 当传入--work-dirs时用来处理工作目录
574
+ */
575
+ wrapperWorkDirsAction(fn) {
576
+ const self = this;
577
+ return function() {
578
+ return __async(this, arguments, function* () {
579
+ let workDirs = self._optionValues.workDirs;
580
+ if (!workDirs) {
581
+ return yield fn.apply(this, Array.from(arguments));
582
+ }
583
+ if (!Array.isArray(workDirs))
584
+ workDirs = workDirs.split(",");
585
+ workDirs = workDirs.reduce((dirs, dir) => {
586
+ if (typeof dir == "string")
587
+ dirs.push(...dir.split(","));
588
+ return dirs;
589
+ }, []);
590
+ for (let workDir of workDirs) {
591
+ const cwd = process.cwd();
592
+ try {
593
+ if (!path2.isAbsolute(workDir))
594
+ workDir = path2.join(cwd, workDir);
595
+ if (fs2.existsSync(workDir) && fs2.statSync(workDir).isDirectory()) {
596
+ outputDebug("\u5207\u6362\u5230\u5DE5\u4F5C\u76EE\u5F55:{}", workDir);
597
+ process.chdir(workDir);
598
+ yield fn.apply(this, Array.from(arguments));
599
+ } else {
600
+ outputDebug("\u65E0\u6548\u7684\u5DE5\u4F5C\u76EE\u5F55:{}", workDir);
601
+ }
602
+ } catch (e) {
603
+ throw e;
604
+ } finally {
605
+ process.chdir(cwd);
606
+ }
607
+ }
608
+ });
609
+ };
610
+ }
611
+ getOption(name) {
612
+ return this.options.find((option) => option.name() == name);
613
+ }
614
+ /**
615
+ * 添加一个Before钩子
616
+ * @param listener
617
+ * @param scope =false时代表只在本命令执行,=true时代表在本命令及其子命令执行
618
+ * @returns
619
+ */
620
+ before(listener, scope = true) {
621
+ this._beforeHooks.push([listener, scope]);
622
+ return this;
623
+ }
624
+ executeBeforeHooks(args) {
625
+ return __async(this, null, function* () {
626
+ const hooks = this.beforeHooks.map(
627
+ ([hook, scope]) => [hook, scope, this]
628
+ );
629
+ this.getAncestorCommands().forEach((cmd) => {
630
+ hooks.unshift(
631
+ ...cmd.beforeHooks.map(([hook, scope]) => {
632
+ return [hook, scope, cmd];
633
+ })
634
+ );
635
+ });
636
+ for (let [hook, scope, cmd] of hooks) {
637
+ if (!scope)
638
+ continue;
639
+ yield hook.call(cmd, args);
640
+ }
641
+ });
642
+ }
643
+ /**
644
+ * 添加一个After钩子
645
+ * @param listener
646
+ * @param scope =false时代表只在本命令执行,=true时代表在本命令及其子命令执行
647
+ * @returns
648
+ */
649
+ after(listener, scope = true) {
650
+ this._afterHooks.push([listener, scope]);
651
+ return this;
652
+ }
653
+ executeAfterHooks(args) {
654
+ return __async(this, null, function* () {
655
+ const hooks = this.afterHooks.map(
656
+ ([hook, scope]) => [hook, scope, this]
657
+ );
658
+ this.getAncestorCommands().forEach((cmd) => {
659
+ hooks.push(
660
+ ...cmd.afterHooks.map(([hook, scope]) => {
661
+ return [hook, scope, cmd];
662
+ })
663
+ );
664
+ });
665
+ for (let [hook, scope, cmd] of hooks) {
666
+ if (!scope)
667
+ continue;
668
+ yield hook.call(cmd, args);
669
+ }
670
+ });
671
+ }
672
+ preActionHook(thisCommand, actionCommand) {
673
+ return __async(this, null, function* () {
674
+ if (this.isEnablePrompts()) {
675
+ const questions = [
676
+ ...this.generateAutoPrompts(),
677
+ ...this._customPrompts
678
+ ];
679
+ if (questions.length > 0) {
680
+ const results = yield prompts(questions);
681
+ Object.entries(results).forEach(([key, value]) => {
682
+ thisCommand.setOptionValue(key, value);
683
+ });
684
+ }
685
+ }
686
+ });
687
+ }
688
+ isEnablePrompts() {
689
+ if (isEnablePrompts() === false) {
690
+ return false;
691
+ } else {
692
+ return this._enable_prompts;
693
+ }
694
+ }
695
+ /**
696
+ * 生成选项自动提示
697
+ *
698
+ * @remarks
699
+ * FlexCli要求所有未提供默认值的Option自动生成提示
700
+ *
701
+ * - 未提供默认值,并且是必选的参数Option
702
+ * - 指定了choices但未提供有效值的Option
703
+ *
704
+ */
705
+ generateAutoPrompts() {
706
+ const options = this.options;
707
+ const optionPromports = options.filter((option) => !option.hidden && option instanceof MixOption).map((option) => option.getPrompt(this._optionValues[option.name()])).filter((prompt) => prompt);
708
+ outputDebug("\u547D\u4EE4<{}>\u81EA\u52A8\u751F\u6210{}\u4E2A\u9009\u9879\u63D0\u793A:{}", [
709
+ this.name(),
710
+ optionPromports.length,
711
+ optionPromports.map((prompt) => `${prompt.name}(${prompt.type})`).join(",")
712
+ ]);
713
+ return optionPromports;
714
+ }
715
+ option(flags, description, options) {
716
+ const option = new MixOption(...arguments);
717
+ if (option.required && !this.isEnablePrompts())
718
+ option.mandatory = true;
719
+ return this.addOption(option);
720
+ }
721
+ /**
722
+ * 添加提示
723
+ *
724
+ * @remarks
725
+ *
726
+ * 添加一些自定义提示
727
+ *
728
+ *
729
+ * @param questions
730
+ * @param show 是否显示提示信息,auto表示只有在用户没有提供option的值时才显示提示信息,always表示总是显示提示信息,never表示不显示提示信息
731
+ * @returns
732
+ */
733
+ prompt(questions) {
734
+ this._customPrompts.push(...Array.isArray(questions) ? questions : [questions]);
735
+ return this;
736
+ }
737
+ /**
738
+ *
739
+ * 选择命令并执行
740
+ *
741
+ * @remorks
742
+ *
743
+ * 当命令具有多个子命令时,并且没有提供默认子命令时,提示用户选择一个子命令
744
+ *
745
+ */
746
+ selectCommands() {
747
+ return __async(this, null, function* () {
748
+ const choices = this.commands.map((command2) => ({
749
+ title: `${command2.description()}(${command2.name()})`,
750
+ value: command2.name()
751
+ }));
752
+ const result = yield prompts({
753
+ type: "select",
754
+ name: "command",
755
+ message: "\u8BF7\u9009\u62E9\u547D\u4EE4:",
756
+ choices
757
+ });
758
+ const command = this.commands.find((command2) => command2.name() === result.command);
759
+ yield command == null ? void 0 : command.parseAsync([result.command], { from: "user" });
760
+ });
761
+ }
762
+ /**
763
+ * 禁用/启用所有提示
764
+ */
765
+ disablePrompts() {
766
+ this._enable_prompts = false;
767
+ return this;
768
+ }
769
+ enablePrompts() {
770
+ this._enable_prompts = true;
771
+ return this;
772
+ }
773
+ };
774
+
775
+ // src/finder.ts
776
+ import { getPackageRootPath } from "flex-tools";
777
+ import { globSync } from "glob";
778
+ import fs3 from "fs";
779
+ import path3 from "path";
780
+ import { getPackageJson } from "flex-tools/package/getPackageJson";
781
+ function getMatchedDependencies(entry) {
782
+ const pacakgeMacher = this.options.include;
783
+ if (!(pacakgeMacher instanceof RegExp))
784
+ return [];
785
+ const { dependencies = {}, devDependencies = {}, peerDependencies = {}, optionalDependencies = {}, bundleDependencies = {} } = getPackageJson(entry);
786
+ const packageNames = [
787
+ ...Object.keys(dependencies),
788
+ ...Object.keys(devDependencies),
789
+ ...Object.keys(peerDependencies),
790
+ ...Object.keys(optionalDependencies),
791
+ ...Object.keys(bundleDependencies)
792
+ ];
793
+ return packageNames.filter((name) => name !== "@voerka/cli" && pacakgeMacher.test(name));
794
+ }
795
+ function isMatched(str, reg) {
796
+ const regexps = reg ? Array.isArray(reg) ? reg : [reg] : [];
797
+ return regexps.some((regexp) => {
798
+ if (typeof regexp === "string") {
799
+ return new RegExp(regexp).test(str);
800
+ } else if (regexp instanceof RegExp) {
801
+ return regexp.test(str);
802
+ } else {
803
+ return false;
804
+ }
805
+ });
806
+ }
807
+ function findCliPaths(packageName, entry) {
808
+ const includeMacher = this.options.include;
809
+ const excludeMacher = this.options.exclude;
810
+ if (!includeMacher)
811
+ return [];
812
+ const packageRoot = getPackageRootPath(entry || process.cwd());
813
+ const packagePath = packageName ? path3.dirname(__require.resolve(packageName, { paths: [packageRoot] })) : packageName;
814
+ const packageNames = getMatchedDependencies.call(this, packagePath);
815
+ const cliDirs = [];
816
+ if (entry !== void 0)
817
+ cliDirs.push(path3.join(packagePath, this.options.cliDir));
818
+ packageNames.filter((name) => {
819
+ return isMatched(name, includeMacher) && !isMatched(name, excludeMacher);
820
+ }).forEach((name) => {
821
+ outputDebug("\u5339\u914D\u5305:{}", `${packageName ? name + " <- " + packageName : name}`);
822
+ try {
823
+ const packageEntry = path3.dirname(__require.resolve(name, { paths: packagePath ? [packagePath] : [process.cwd()] }));
824
+ const packageCliDir = path3.join(packageEntry, this.options.cliDir);
825
+ let dependencies = getMatchedDependencies.call(this, packageEntry);
826
+ cliDirs.push(...dependencies.reduce((result, dependencie) => {
827
+ outputDebug("\u5339\u914D\u5305:{}", `${dependencie} <- ${name}`);
828
+ result.push(...findCliPaths.call(this, dependencie, packageEntry));
829
+ return result;
830
+ }, []));
831
+ if (fs3.existsSync(packageCliDir)) {
832
+ cliDirs.push(packageCliDir);
833
+ }
834
+ } catch (e) {
835
+ outputDebug("\u89E3\u6790\u5305<{}>\u8DEF\u5F84\u51FA\u9519\uFF1A{}", [name, e.stack]);
836
+ }
837
+ });
838
+ return [...new Set(cliDirs)];
839
+ }
840
+ function showError(e) {
841
+ if (isDebug()) {
842
+ outputDebug("\u5BFC\u5165\u547D\u4EE4<>\u51FA\u9519:{}", e.stack);
843
+ } else {
844
+ console.error(e);
845
+ }
846
+ }
847
+ function findCommands(cli) {
848
+ return __async(this, null, function* () {
849
+ const cliDirs = findCliPaths.call(cli);
850
+ const commands = [];
851
+ const files = [];
852
+ cliDirs.forEach((dir) => {
853
+ files.push(...globSync("*", {
854
+ cwd: dir,
855
+ absolute: true
856
+ }).filter((file) => (file.endsWith(".js") || file.endsWith(".cjs") || file.endsWith(".mjs")) && !fs3.statSync(file).isDirectory()));
857
+ });
858
+ for (let file of files) {
859
+ try {
860
+ outputDebug("\u5BFC\u5165\u547D\u4EE4:{}", file);
861
+ if (file.endsWith(".cjs") || file.endsWith(".js")) {
862
+ commands.push(__require(file));
863
+ } else if (file.endsWith(".mjs")) {
864
+ const cmd = yield import(`file://${file}`);
865
+ commands.push(cmd.default);
866
+ }
867
+ } catch (e) {
868
+ if (e.code === "ERR_REQUIRE_ESM") {
869
+ try {
870
+ const cmd = yield import(`file://${file.replace(".js", ".mjs")}`);
871
+ commands.push(cmd.default);
872
+ } catch (err) {
873
+ showError(err);
874
+ }
875
+ } else {
876
+ showError(e);
877
+ }
878
+ }
879
+ }
880
+ return commands;
881
+ });
882
+ }
883
+
884
+ // src/cli.ts
885
+ import { asyncSignal } from "flex-tools/async/asyncSignal";
886
+ import replaceAll from "string.prototype.replaceall";
887
+ replaceAll.shim();
888
+ var MixCli = class extends LiteEvent {
889
+ constructor(options) {
890
+ super();
891
+ this.findSignals = [];
892
+ this.options = assignObject({
893
+ name: "mixcli",
894
+ package: null,
895
+ cliDir: "cli",
896
+ prompt: "auto"
897
+ }, options);
898
+ this.createRootCommand();
899
+ }
900
+ get context() {
901
+ return this.options.context;
902
+ }
903
+ get name() {
904
+ return this.options.name;
905
+ }
906
+ /**
907
+ * 是否禁用了所有的交互提示
908
+ */
909
+ get isDisabledPrompts() {
910
+ return this.root.rawArgs.includes("--no-prompts");
911
+ }
912
+ /**
913
+ * 扫描当前工程的依赖,加载匹配include的依赖下的命令
914
+ */
915
+ installCommands() {
916
+ return __async(this, null, function* () {
917
+ const cmders = yield findCommands(this);
918
+ for (let cmder of cmders) {
919
+ try {
920
+ if (typeof cmder === "function") {
921
+ let cmds = cmder(this);
922
+ cmds = cmds ? Array.isArray(cmds) ? cmds : [cmds] : [];
923
+ this.register(() => cmds);
924
+ }
925
+ } catch (e) {
926
+ }
927
+ }
928
+ });
929
+ }
930
+ /**
931
+ * 创建根命令
932
+ *
933
+ */
934
+ createRootCommand() {
935
+ this.root = new MixCommand(this.name);
936
+ this.root.helpOption("-h, --help").version(require_package().version, "-v, --version").action(() => {
937
+ if (this.options.logo)
938
+ logsets2.log(fixIndent(this.options.logo, 2));
939
+ console.log();
940
+ let title = this.options.title || this.options.name;
941
+ if (Array.isArray(title)) {
942
+ logsets2.log(String(title[0]).firstUpper(), [...title.slice(1)]);
943
+ } else {
944
+ logsets2.log(`${title.firstUpper()} Version: {}`, this.options.version);
945
+ }
946
+ if (this.options.description)
947
+ logsets2.log(logsets2.colors.darkGray(this.options.description));
948
+ console.log();
949
+ this.root.help();
950
+ });
951
+ addBuiltInOptions(this.root);
952
+ if (this.options.before)
953
+ this.root.hook("preAction", this.options.before);
954
+ if (this.options.after)
955
+ this.root.hook("postAction", this.options.after);
956
+ }
957
+ /**
958
+ * 添加帮助选项
959
+ *
960
+ * @param text 帮助文本
961
+ * @param position 显示位置,可选值:before|after|beforeAll|afterAll
962
+ * @param fixIndent 是否自动修正缩进,如果为true,则会自动修正缩进,当显示多行时文本时,会自动修正缩进
963
+ *
964
+ */
965
+ addHelp(text, { pos = "beforeAll", alignIndent = true }) {
966
+ if (alignIndent)
967
+ text = fixIndent(text, alignIndent);
968
+ this.root.addHelpText(pos, text);
969
+ }
970
+ /**
971
+ * 注册一个命令
972
+ * @param cmd
973
+ */
974
+ register(cmd) {
975
+ if (typeof cmd == "function") {
976
+ let result = cmd(this);
977
+ let cmds = result instanceof Array ? result : result == void 0 ? [] : [result];
978
+ for (let cmd2 of cmds) {
979
+ if (cmd2 instanceof MixCommand) {
980
+ if (this.hasCommand(cmd2.name())) {
981
+ logsets2.error(`Command <${cmd2.name()}> has been registered!`);
982
+ } else {
983
+ this.root.addCommand(cmd2);
984
+ cmd2._cli = this;
985
+ this.emit("register", cmd2.fullname, true);
986
+ }
987
+ }
988
+ }
989
+ } else {
990
+ logsets2.error("Invalid command");
991
+ }
992
+ }
993
+ hasCommand(name) {
994
+ return this.root.commands.some((c) => c.name() == name);
995
+ }
996
+ /**
997
+ * 根据命令名称查找命令
998
+ *
999
+ * @remarks
1000
+ *
1001
+ * find("dev")
1002
+ * find("dev.microservice") 支持多级命令
1003
+ * find("abc",DevCommand) 允许指定从DevCommand下开始查找abc命令
1004
+ *
1005
+ * @param name
1006
+ */
1007
+ get(name) {
1008
+ const names = name.split(".");
1009
+ let curCmd = this.root;
1010
+ let resultCmd;
1011
+ while (names.length > 0) {
1012
+ const topName = names.shift();
1013
+ const r = curCmd.commands.find((c) => c.name() == topName);
1014
+ if (r && names.length == 0) {
1015
+ resultCmd = r;
1016
+ }
1017
+ curCmd = r;
1018
+ }
1019
+ return resultCmd;
1020
+ }
1021
+ /**
1022
+ * 查找一个命令
1023
+ *
1024
+ * 如果命令不存在,则等待命令注册后再返回
1025
+ *
1026
+ * 在多包场景下,如果命令在其他包中注册并且该包中的命令还没注册,则会等待命令注册后再返回
1027
+ *
1028
+ * @param name
1029
+ * @returns
1030
+ */
1031
+ find(name) {
1032
+ const cmd = this.get(name);
1033
+ if (cmd) {
1034
+ return Promise.resolve(cmd);
1035
+ } else {
1036
+ const signal = asyncSignal();
1037
+ this.findSignals.push(signal);
1038
+ return new Promise((resolve) => {
1039
+ let listener;
1040
+ listener = this.on("register", (fullname) => {
1041
+ if (fullname == `${this.name}.${name}`) {
1042
+ listener.off();
1043
+ signal.resolve();
1044
+ this.findSignals = this.findSignals.filter((s) => s != signal);
1045
+ resolve(this.get(name));
1046
+ }
1047
+ }, { objectify: true });
1048
+ });
1049
+ }
1050
+ }
1051
+ /**
1052
+ * 判断命令是否存在
1053
+ *
1054
+ * @param name
1055
+ * @returns
1056
+ */
1057
+ exists(name) {
1058
+ if (name in this.root.commands) {
1059
+ return true;
1060
+ } else {
1061
+ return this.get(name) != void 0;
1062
+ }
1063
+ }
1064
+ /**
1065
+ * 运行命令行程序
1066
+ */
1067
+ run() {
1068
+ this.installCommands().then(() => {
1069
+ return Promise.all(this.findSignals.map((signal) => signal(1e4))).then(() => {
1070
+ this.root.parseAsync(process.argv);
1071
+ });
1072
+ });
1073
+ }
1074
+ /**
1075
+ * 创建一个命令
1076
+ *
1077
+ *
1078
+ */
1079
+ create() {
1080
+ }
1081
+ };
1082
+ export {
1083
+ BREAK,
1084
+ MixCli,
1085
+ MixCommand,
1086
+ MixOption,
1087
+ addBuiltInOptions,
1088
+ createFileByTemplate,
1089
+ fileExists,
1090
+ fixIndent,
1091
+ isDebug,
1092
+ isEnablePrompts,
1093
+ mkDirs,
1094
+ mkdir,
1095
+ outputDebug,
1096
+ outputStr,
1097
+ readFile,
1098
+ writeFile
1099
+ };
1100
+ //# sourceMappingURL=index.mjs.map