mixcli 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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