mixcli 3.0.0

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