breadc 0.4.4 → 0.5.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/README.md +1 -1
- package/dist/index.cjs +179 -125
- package/dist/index.d.ts +28 -16
- package/dist/index.mjs +177 -121
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Yet another Command Line Application Framework powered by [minimist](https://www
|
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
+ ⚡️ **Light-weight**: Only
|
|
9
|
+
+ ⚡️ **Light-weight**: Only 40 kB (Unpacked).
|
|
10
10
|
+ 📖 **East to Learn**: Breadc is basically compatible with [cac](https://github.com/cacjs/cac) and there are only 4 APIs for building a CLI application: `command`, `option`, `action`, `run`.
|
|
11
11
|
+ 💻 **TypeScript Infer**: IDE will automatically infer the type of your command action function.
|
|
12
12
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
const kolorist = require('kolorist');
|
|
6
3
|
const minimist = require('minimist');
|
|
4
|
+
const kolorist = require('kolorist');
|
|
7
5
|
|
|
8
6
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
|
|
9
7
|
|
|
10
|
-
const kolorist__default = /*#__PURE__*/_interopDefaultLegacy(kolorist);
|
|
11
8
|
const minimist__default = /*#__PURE__*/_interopDefaultLegacy(minimist);
|
|
9
|
+
const kolorist__default = /*#__PURE__*/_interopDefaultLegacy(kolorist);
|
|
10
|
+
|
|
11
|
+
function twoColumn(texts, split = " ") {
|
|
12
|
+
const left = padRight(texts.map((t) => t[0]));
|
|
13
|
+
return left.map((l, idx) => l + split + texts[idx][1]);
|
|
14
|
+
}
|
|
15
|
+
function padRight(texts, fill = " ") {
|
|
16
|
+
const length = texts.map((t) => t.length).reduce((max, l) => Math.max(max, l), 0);
|
|
17
|
+
return texts.map((t) => t + fill.repeat(length - t.length));
|
|
18
|
+
}
|
|
12
19
|
|
|
13
20
|
function createDefaultLogger(name, logger) {
|
|
14
21
|
const println = !!logger && typeof logger === "function" ? logger : logger?.println ?? ((message, ...args) => {
|
|
@@ -59,20 +66,31 @@ const _Option = class {
|
|
|
59
66
|
}
|
|
60
67
|
};
|
|
61
68
|
let Option = _Option;
|
|
62
|
-
Option.OptionRE = /^(-[a-zA-
|
|
69
|
+
Option.OptionRE = /^(-[a-zA-Z0-9], )?--([a-zA-Z0-9\-]+)( \[[a-zA-Z0-9]+\]| <[a-zA-Z0-9]+>)?$/;
|
|
63
70
|
|
|
64
71
|
const _Command = class {
|
|
65
72
|
constructor(format, config) {
|
|
66
73
|
this.options = [];
|
|
67
|
-
this.format =
|
|
68
|
-
|
|
74
|
+
this.format = format;
|
|
75
|
+
const pieces = format.split(" ").map((t) => t.trim()).filter(Boolean);
|
|
76
|
+
const prefix = pieces.filter((p) => !isArg(p));
|
|
77
|
+
this.default = prefix.length === 0;
|
|
78
|
+
this.prefix = this.default ? [] : [prefix];
|
|
79
|
+
this.arguments = pieces.filter(isArg);
|
|
69
80
|
this.description = config.description ?? "";
|
|
70
|
-
this.conditionFn = config.condition;
|
|
71
81
|
this.logger = config.logger;
|
|
72
|
-
if (
|
|
82
|
+
if (pieces.length > _Command.MaxDep) {
|
|
73
83
|
this.logger.warn(`Command format string "${format}" is too long`);
|
|
74
84
|
}
|
|
75
85
|
}
|
|
86
|
+
get isInternal() {
|
|
87
|
+
return this instanceof InternalCommand;
|
|
88
|
+
}
|
|
89
|
+
alias(command) {
|
|
90
|
+
const pieces = command.split(" ").map((t) => t.trim()).filter(Boolean);
|
|
91
|
+
this.prefix.push(pieces);
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
76
94
|
option(format, configOrDescription = "", otherConfig = {}) {
|
|
77
95
|
const config = typeof configOrDescription === "object" ? configOrDescription : { ...otherConfig, description: configOrDescription };
|
|
78
96
|
try {
|
|
@@ -83,59 +101,56 @@ const _Command = class {
|
|
|
83
101
|
}
|
|
84
102
|
return this;
|
|
85
103
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (this.conditionFn) {
|
|
91
|
-
return this.conditionFn(args);
|
|
104
|
+
hasPrefix(parsedArgs) {
|
|
105
|
+
const argv = parsedArgs["_"];
|
|
106
|
+
if (argv.length === 0) {
|
|
107
|
+
return this.default;
|
|
92
108
|
} else {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const isCmd = (t) => t[0] !== "[" && t[0] !== "<";
|
|
96
|
-
for (let i = 0; i < this.format.length; i++) {
|
|
97
|
-
if (!isCmd(this.format[i])) {
|
|
109
|
+
for (const prefix of this.prefix) {
|
|
110
|
+
if (prefix.length > 0 && prefix[0] === argv[0]) {
|
|
98
111
|
return true;
|
|
99
112
|
}
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
shouldRun(parsedArgs) {
|
|
118
|
+
const args = parsedArgs["_"];
|
|
119
|
+
for (const prefix of this.prefix) {
|
|
120
|
+
let match = true;
|
|
121
|
+
for (let i = 0; match && i < prefix.length; i++) {
|
|
122
|
+
if (args[i] !== prefix[i]) {
|
|
123
|
+
match = false;
|
|
102
124
|
}
|
|
103
125
|
}
|
|
104
|
-
|
|
126
|
+
if (match) {
|
|
127
|
+
args.splice(0, prefix.length);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
105
130
|
}
|
|
131
|
+
if (this.default)
|
|
132
|
+
return true;
|
|
133
|
+
return false;
|
|
106
134
|
}
|
|
107
135
|
parseArgs(args, globalOptions) {
|
|
108
|
-
if (this.conditionFn) {
|
|
109
|
-
const argumentss2 = args["_"];
|
|
110
|
-
const options2 = args;
|
|
111
|
-
delete options2["_"];
|
|
112
|
-
return {
|
|
113
|
-
command: this,
|
|
114
|
-
arguments: argumentss2,
|
|
115
|
-
options: args
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
const isCmd = (t) => t[0] !== "[" && t[0] !== "<";
|
|
119
136
|
const argumentss = [];
|
|
120
|
-
for (let i = 0; i < this.
|
|
121
|
-
if (isCmd(this.format[i]))
|
|
122
|
-
continue;
|
|
137
|
+
for (let i = 0; i < this.arguments.length; i++) {
|
|
123
138
|
if (i < args["_"].length) {
|
|
124
|
-
if (this.
|
|
139
|
+
if (this.arguments[i].startsWith("[...")) {
|
|
125
140
|
argumentss.push(args["_"].slice(i).map(String));
|
|
126
141
|
} else {
|
|
127
142
|
argumentss.push(String(args["_"][i]));
|
|
128
143
|
}
|
|
129
144
|
} else {
|
|
130
|
-
if (this.
|
|
131
|
-
this.logger.warn(`You should provide the argument "${this.
|
|
132
|
-
argumentss.push(
|
|
133
|
-
} else if (this.
|
|
145
|
+
if (this.arguments[i].startsWith("<")) {
|
|
146
|
+
this.logger.warn(`You should provide the argument "${this.arguments[i]}"`);
|
|
147
|
+
argumentss.push("");
|
|
148
|
+
} else if (this.arguments[i].startsWith("[...")) {
|
|
134
149
|
argumentss.push([]);
|
|
135
|
-
} else if (this.
|
|
150
|
+
} else if (this.arguments[i].startsWith("[")) {
|
|
136
151
|
argumentss.push(void 0);
|
|
137
152
|
} else {
|
|
138
|
-
this.logger.warn(`unknown format string ("${this.
|
|
153
|
+
this.logger.warn(`unknown format string ("${this.arguments[i]}")`);
|
|
139
154
|
}
|
|
140
155
|
}
|
|
141
156
|
}
|
|
@@ -159,13 +174,14 @@ const _Command = class {
|
|
|
159
174
|
options[name] = void 0;
|
|
160
175
|
}
|
|
161
176
|
}
|
|
162
|
-
if (rawOption.
|
|
163
|
-
options[name]
|
|
164
|
-
} else if (rawOption.default) {
|
|
165
|
-
if (!options[name]) {
|
|
177
|
+
if (rawOption.default !== void 0) {
|
|
178
|
+
if (options[name] === void 0 || options[name] === false) {
|
|
166
179
|
options[name] = rawOption.default;
|
|
167
180
|
}
|
|
168
181
|
}
|
|
182
|
+
if (rawOption.construct !== void 0) {
|
|
183
|
+
options[name] = rawOption.construct(options[name]);
|
|
184
|
+
}
|
|
169
185
|
}
|
|
170
186
|
for (const key of Object.keys(options)) {
|
|
171
187
|
if (!fullOptions.has(key)) {
|
|
@@ -180,88 +196,115 @@ const _Command = class {
|
|
|
180
196
|
}
|
|
181
197
|
action(fn) {
|
|
182
198
|
this.actionFn = fn;
|
|
183
|
-
return this;
|
|
184
199
|
}
|
|
185
200
|
async run(...args) {
|
|
186
201
|
if (this.actionFn) {
|
|
187
|
-
return await this.actionFn(...args, { logger: this.logger });
|
|
202
|
+
return await this.actionFn(...args, { logger: this.logger, color: kolorist__default });
|
|
188
203
|
} else {
|
|
189
|
-
this.logger.warn(`You may miss action function in "${this.format}"`);
|
|
204
|
+
this.logger.warn(`You may miss action function in ${this.format ? `"${this.format}"` : "<default command>"}`);
|
|
190
205
|
}
|
|
191
206
|
}
|
|
192
207
|
};
|
|
193
208
|
let Command = _Command;
|
|
194
209
|
Command.MaxDep = 5;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
210
|
+
class InternalCommand extends Command {
|
|
211
|
+
hasPrefix(_args) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
parseArgs(args, _globalOptions) {
|
|
215
|
+
const argumentss = args["_"];
|
|
216
|
+
const options = args;
|
|
217
|
+
delete options["_"];
|
|
218
|
+
delete options["help"];
|
|
219
|
+
delete options["version"];
|
|
220
|
+
return {
|
|
221
|
+
command: this,
|
|
222
|
+
arguments: argumentss,
|
|
223
|
+
options: args
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
class HelpCommand extends InternalCommand {
|
|
228
|
+
constructor(commands, help, logger) {
|
|
229
|
+
super("-h, --help", { description: "Display this message", logger });
|
|
230
|
+
this.runCommands = [];
|
|
231
|
+
this.helpCommands = [];
|
|
232
|
+
this.commands = commands;
|
|
233
|
+
this.help = help;
|
|
234
|
+
}
|
|
235
|
+
shouldRun(args) {
|
|
236
|
+
const isRestEmpty = !args["--"]?.length;
|
|
237
|
+
if ((args.help || args.h) && isRestEmpty) {
|
|
238
|
+
if (args["_"].length > 0) {
|
|
239
|
+
for (const cmd of this.commands) {
|
|
240
|
+
if (!cmd.default && !cmd.isInternal) {
|
|
241
|
+
if (cmd.shouldRun(args)) {
|
|
242
|
+
this.runCommands.push(cmd);
|
|
243
|
+
} else if (cmd.hasPrefix(args)) {
|
|
244
|
+
this.helpCommands.push(cmd);
|
|
206
245
|
}
|
|
207
246
|
}
|
|
208
247
|
}
|
|
209
|
-
return true;
|
|
210
|
-
} else {
|
|
211
|
-
return false;
|
|
212
248
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
for (const line of breadc.help(helpCommand)) {
|
|
217
|
-
breadc.logger.println(line);
|
|
249
|
+
return true;
|
|
250
|
+
} else {
|
|
251
|
+
return false;
|
|
218
252
|
}
|
|
219
|
-
}
|
|
253
|
+
}
|
|
254
|
+
async run() {
|
|
255
|
+
const shouldHelp = this.runCommands.length > 0 ? this.runCommands : this.helpCommands;
|
|
256
|
+
for (const line of this.help(shouldHelp)) {
|
|
257
|
+
this.logger.println(line);
|
|
258
|
+
}
|
|
259
|
+
this.runCommands.splice(0);
|
|
260
|
+
this.helpCommands.splice(0);
|
|
261
|
+
}
|
|
220
262
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
263
|
+
class VersionCommand extends InternalCommand {
|
|
264
|
+
constructor(version, logger) {
|
|
265
|
+
super("-v, --version", { description: "Display version number", logger });
|
|
266
|
+
this.version = version;
|
|
267
|
+
}
|
|
268
|
+
shouldRun(args) {
|
|
269
|
+
const isEmpty = !args["_"].length && !args["--"]?.length;
|
|
270
|
+
if (args.version && isEmpty) {
|
|
271
|
+
return true;
|
|
272
|
+
} else if (args.v && isEmpty) {
|
|
273
|
+
return true;
|
|
274
|
+
} else {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
async run() {
|
|
279
|
+
this.logger.println(this.version);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function isArg(arg) {
|
|
283
|
+
return arg[0] === "[" && arg[arg.length - 1] === "]" || arg[0] === "<" && arg[arg.length - 1] === ">";
|
|
237
284
|
}
|
|
238
285
|
|
|
239
286
|
class Breadc {
|
|
240
287
|
constructor(name, option) {
|
|
241
288
|
this.options = [];
|
|
242
289
|
this.commands = [];
|
|
290
|
+
this.callbacks = {
|
|
291
|
+
pre: [],
|
|
292
|
+
post: []
|
|
293
|
+
};
|
|
243
294
|
this.name = name;
|
|
244
295
|
this._version = option.version ?? "unknown";
|
|
245
296
|
this.description = option.description;
|
|
246
297
|
this.logger = createDefaultLogger(name, option.logger);
|
|
247
|
-
|
|
248
|
-
name: this.name,
|
|
249
|
-
version: () => this.version.call(this),
|
|
250
|
-
help: (command) => this.help.call(this, command),
|
|
251
|
-
logger: this.logger,
|
|
252
|
-
options: this.options,
|
|
253
|
-
commands: this.commands
|
|
254
|
-
};
|
|
255
|
-
this.commands.push(createVersionCommand(breadc), createHelpCommand(breadc));
|
|
298
|
+
this.commands.push(new VersionCommand(this.version(), this.logger), new HelpCommand(this.commands, this.help.bind(this), this.logger));
|
|
256
299
|
}
|
|
257
300
|
version() {
|
|
258
301
|
return `${this.name}/${this._version}`;
|
|
259
302
|
}
|
|
260
|
-
help(
|
|
303
|
+
help(commands = []) {
|
|
261
304
|
const output = [];
|
|
262
305
|
const println = (msg) => output.push(msg);
|
|
263
306
|
println(this.version());
|
|
264
|
-
if (
|
|
307
|
+
if (commands.length === 0) {
|
|
265
308
|
if (this.description) {
|
|
266
309
|
println("");
|
|
267
310
|
if (Array.isArray(this.description)) {
|
|
@@ -272,27 +315,26 @@ class Breadc {
|
|
|
272
315
|
println(this.description);
|
|
273
316
|
}
|
|
274
317
|
}
|
|
275
|
-
} else {
|
|
276
|
-
if (command.description) {
|
|
277
|
-
println("");
|
|
278
|
-
println(command.description);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
if (!command) {
|
|
282
318
|
if (this.defaultCommand) {
|
|
283
319
|
println(``);
|
|
284
320
|
println(`Usage:`);
|
|
285
|
-
println(` $ ${this.name} ${this.defaultCommand.format
|
|
321
|
+
println(` $ ${this.name} ${this.defaultCommand.format}`);
|
|
322
|
+
}
|
|
323
|
+
} else if (commands.length === 1) {
|
|
324
|
+
const command = commands[0];
|
|
325
|
+
if (command.description) {
|
|
326
|
+
println("");
|
|
327
|
+
println(command.description);
|
|
286
328
|
}
|
|
287
|
-
} else {
|
|
288
329
|
println(``);
|
|
289
330
|
println(`Usage:`);
|
|
290
|
-
println(` $ ${this.name} ${command.format
|
|
331
|
+
println(` $ ${this.name} ${command.format}`);
|
|
291
332
|
}
|
|
292
|
-
if (
|
|
333
|
+
if (commands.length !== 1) {
|
|
334
|
+
const cmdList = (commands.length === 0 ? this.commands : commands).filter((c) => !c.isInternal);
|
|
293
335
|
println(``);
|
|
294
336
|
println(`Commands:`);
|
|
295
|
-
const commandHelps =
|
|
337
|
+
const commandHelps = cmdList.map((c) => [` $ ${this.name} ${c.format}`, c.description]);
|
|
296
338
|
for (const line of twoColumn(commandHelps)) {
|
|
297
339
|
println(line);
|
|
298
340
|
}
|
|
@@ -300,7 +342,7 @@ class Breadc {
|
|
|
300
342
|
println(``);
|
|
301
343
|
println(`Options:`);
|
|
302
344
|
const optionHelps = [].concat([
|
|
303
|
-
...
|
|
345
|
+
...commands.length > 0 ? commands.flatMap((cmd) => cmd.options.map((o) => [` ${o.format}`, o.description])) : [],
|
|
304
346
|
...this.options.map((o) => [` ${o.format}`, o.description]),
|
|
305
347
|
[` -h, --help`, `Display this message`],
|
|
306
348
|
[` -v, --version`, `Display version number`]
|
|
@@ -338,15 +380,28 @@ class Breadc {
|
|
|
338
380
|
...this.options,
|
|
339
381
|
...this.commands.flatMap((c) => c.options)
|
|
340
382
|
];
|
|
383
|
+
{
|
|
384
|
+
const names = /* @__PURE__ */ new Map();
|
|
385
|
+
for (const option of allowOptions) {
|
|
386
|
+
if (names.has(option.name)) {
|
|
387
|
+
const otherOption = names.get(option.name);
|
|
388
|
+
if (otherOption.type !== option.type) {
|
|
389
|
+
this.logger.warn(`Option "${option.name}" encounters conflict`);
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
names.set(option.name, option);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
341
396
|
const alias = allowOptions.reduce((map, o) => {
|
|
342
397
|
if (o.shortcut) {
|
|
343
398
|
map[o.shortcut] = o.name;
|
|
344
399
|
}
|
|
345
400
|
return map;
|
|
346
|
-
}, {});
|
|
401
|
+
}, { h: "help", v: "version" });
|
|
347
402
|
const argv = minimist__default(args, {
|
|
348
403
|
string: allowOptions.filter((o) => o.type === "string").map((o) => o.name),
|
|
349
|
-
boolean: allowOptions.filter((o) => o.type === "boolean").map((o) => o.name),
|
|
404
|
+
boolean: allowOptions.filter((o) => o.type === "boolean").map((o) => o.name).concat(["help", "version"]),
|
|
350
405
|
alias,
|
|
351
406
|
unknown: (t) => {
|
|
352
407
|
if (t[0] !== "-")
|
|
@@ -370,37 +425,36 @@ class Breadc {
|
|
|
370
425
|
}
|
|
371
426
|
}
|
|
372
427
|
if (this.defaultCommand) {
|
|
428
|
+
this.defaultCommand.shouldRun(argv);
|
|
373
429
|
return this.defaultCommand.parseArgs(argv, this.options);
|
|
374
430
|
}
|
|
375
431
|
const argumentss = argv["_"];
|
|
376
432
|
const options = argv;
|
|
377
433
|
delete options["_"];
|
|
434
|
+
delete options["help"];
|
|
435
|
+
delete options["version"];
|
|
378
436
|
return {
|
|
379
437
|
command: void 0,
|
|
380
438
|
arguments: argumentss,
|
|
381
439
|
options
|
|
382
440
|
};
|
|
383
441
|
}
|
|
442
|
+
on(event, fn) {
|
|
443
|
+
this.callbacks[event].push(fn);
|
|
444
|
+
}
|
|
384
445
|
async run(args) {
|
|
385
446
|
const parsed = this.parse(args);
|
|
386
447
|
if (parsed.command) {
|
|
387
|
-
|
|
448
|
+
await Promise.all(this.callbacks.pre.map((fn) => fn(parsed.options)));
|
|
449
|
+
const returnValue = await parsed.command.run(...parsed.arguments, parsed.options);
|
|
450
|
+
await Promise.all(this.callbacks.post.map((fn) => fn(parsed.options)));
|
|
451
|
+
return returnValue;
|
|
388
452
|
}
|
|
389
453
|
}
|
|
390
454
|
}
|
|
391
|
-
function twoColumn(texts, split = " ") {
|
|
392
|
-
const left = padRight(texts.map((t) => t[0]));
|
|
393
|
-
return left.map((l, idx) => l + split + texts[idx][1]);
|
|
394
|
-
}
|
|
395
|
-
function padRight(texts, fill = " ") {
|
|
396
|
-
const length = texts.map((t) => t.length).reduce((max, l) => Math.max(max, l), 0);
|
|
397
|
-
return texts.map((t) => t + fill.repeat(length - t.length));
|
|
398
|
-
}
|
|
399
455
|
|
|
400
456
|
function breadc(name, option = {}) {
|
|
401
457
|
return new Breadc(name, option);
|
|
402
458
|
}
|
|
403
459
|
|
|
404
|
-
exports
|
|
405
|
-
exports.minimist = minimist__default;
|
|
406
|
-
exports["default"] = breadc;
|
|
460
|
+
module.exports = breadc;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
import kolorist from 'kolorist';
|
|
1
2
|
import { ParsedArgs } from 'minimist';
|
|
2
|
-
export { default as minimist } from 'minimist';
|
|
3
|
-
export { default as kolorist } from 'kolorist';
|
|
4
3
|
|
|
5
4
|
interface OptionConfig<F extends string, T = never> {
|
|
5
|
+
/**
|
|
6
|
+
* Option description
|
|
7
|
+
*/
|
|
6
8
|
description?: string;
|
|
7
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Option string default value
|
|
11
|
+
*/
|
|
12
|
+
default?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Transform option text
|
|
15
|
+
*/
|
|
8
16
|
construct?: (rawText: ExtractOptionType<F>) => T;
|
|
9
17
|
}
|
|
10
18
|
/**
|
|
@@ -20,7 +28,7 @@ declare class Option<T extends string = string, F = string> {
|
|
|
20
28
|
private static OptionRE;
|
|
21
29
|
readonly name: string;
|
|
22
30
|
readonly shortcut?: string;
|
|
23
|
-
readonly default?:
|
|
31
|
+
readonly default?: string;
|
|
24
32
|
readonly format: string;
|
|
25
33
|
readonly description: string;
|
|
26
34
|
readonly type: 'string' | 'boolean';
|
|
@@ -29,29 +37,30 @@ declare class Option<T extends string = string, F = string> {
|
|
|
29
37
|
constructor(format: T, config?: OptionConfig<T, F>);
|
|
30
38
|
}
|
|
31
39
|
|
|
32
|
-
declare type ConditionFn = (args: ParsedArgs) => boolean;
|
|
33
40
|
interface CommandConfig {
|
|
34
41
|
description?: string;
|
|
35
42
|
}
|
|
36
43
|
declare class Command<F extends string = string, CommandOption extends object = {}> {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
readonly format: string[];
|
|
41
|
-
readonly default: boolean;
|
|
44
|
+
protected static MaxDep: number;
|
|
45
|
+
protected readonly logger: Logger;
|
|
46
|
+
readonly format: string;
|
|
42
47
|
readonly description: string;
|
|
48
|
+
readonly prefix: string[][];
|
|
49
|
+
readonly arguments: string[];
|
|
50
|
+
readonly default: boolean;
|
|
43
51
|
readonly options: Option[];
|
|
44
52
|
private actionFn?;
|
|
45
53
|
constructor(format: F, config: CommandConfig & {
|
|
46
|
-
condition?: ConditionFn;
|
|
47
54
|
logger: Logger;
|
|
48
55
|
});
|
|
56
|
+
get isInternal(): boolean;
|
|
57
|
+
alias(command: string): this;
|
|
49
58
|
option<OF extends string, T = undefined>(format: OF, description: string, config?: Omit<OptionConfig<OF, T>, 'description'>): Command<F, CommandOption & ExtractOption<OF, T>>;
|
|
50
59
|
option<OF extends string, T = undefined>(format: OF, config?: OptionConfig<OF, T>): Command<F, CommandOption & ExtractOption<OF, T>>;
|
|
51
|
-
|
|
52
|
-
shouldRun(
|
|
60
|
+
hasPrefix(parsedArgs: ParsedArgs): boolean;
|
|
61
|
+
shouldRun(parsedArgs: ParsedArgs): boolean;
|
|
53
62
|
parseArgs(args: ParsedArgs, globalOptions: Option[]): ParseResult;
|
|
54
|
-
action(fn: ActionFn<ExtractCommand<F>, CommandOption>):
|
|
63
|
+
action(fn: ActionFn<ExtractCommand<F>, CommandOption>): void;
|
|
55
64
|
run(...args: any[]): Promise<any>;
|
|
56
65
|
}
|
|
57
66
|
|
|
@@ -92,6 +101,7 @@ declare type Letter = Lowercase | Uppercase;
|
|
|
92
101
|
declare type Push<T extends any[], U, R> = [...T, U, R];
|
|
93
102
|
declare type Context = {
|
|
94
103
|
logger: Logger;
|
|
104
|
+
color: typeof kolorist;
|
|
95
105
|
};
|
|
96
106
|
declare type ActionFn<T extends any[], Option extends object = {}, R = any> = (...arg: Push<T, Option, Context>) => R | Promise<R>;
|
|
97
107
|
/**
|
|
@@ -105,18 +115,20 @@ declare class Breadc<GlobalOption extends object = {}> {
|
|
|
105
115
|
private readonly name;
|
|
106
116
|
private readonly _version;
|
|
107
117
|
private readonly description?;
|
|
118
|
+
readonly logger: Logger;
|
|
108
119
|
private readonly options;
|
|
109
120
|
private readonly commands;
|
|
110
121
|
private defaultCommand?;
|
|
111
|
-
readonly logger: Logger;
|
|
112
122
|
constructor(name: string, option: AppOption);
|
|
113
123
|
version(): string;
|
|
114
|
-
help(
|
|
124
|
+
help(commands?: Command[]): string[];
|
|
115
125
|
option<F extends string, T = undefined>(format: F, description: string, config?: Omit<OptionConfig<F, T>, 'description'>): Breadc<GlobalOption & ExtractOption<F, T>>;
|
|
116
126
|
option<F extends string, T = undefined>(format: F, config?: OptionConfig<F, T>): Breadc<GlobalOption & ExtractOption<F, T>>;
|
|
117
127
|
command<F extends string>(format: F, description: string, config?: Omit<CommandConfig, 'description'>): Command<F, GlobalOption>;
|
|
118
128
|
command<F extends string>(format: F, config?: CommandConfig): Command<F, GlobalOption>;
|
|
119
129
|
parse(args: string[]): ParseResult;
|
|
130
|
+
private readonly callbacks;
|
|
131
|
+
on(event: 'pre' | 'post', fn: (option: GlobalOption) => void | Promise<void>): void;
|
|
120
132
|
run(args: string[]): Promise<any>;
|
|
121
133
|
}
|
|
122
134
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import { blue, yellow, red, gray } from 'kolorist';
|
|
2
|
-
export { default as kolorist } from 'kolorist';
|
|
3
1
|
import minimist from 'minimist';
|
|
4
|
-
|
|
2
|
+
import kolorist, { blue, yellow, red, gray } from 'kolorist';
|
|
3
|
+
|
|
4
|
+
function twoColumn(texts, split = " ") {
|
|
5
|
+
const left = padRight(texts.map((t) => t[0]));
|
|
6
|
+
return left.map((l, idx) => l + split + texts[idx][1]);
|
|
7
|
+
}
|
|
8
|
+
function padRight(texts, fill = " ") {
|
|
9
|
+
const length = texts.map((t) => t.length).reduce((max, l) => Math.max(max, l), 0);
|
|
10
|
+
return texts.map((t) => t + fill.repeat(length - t.length));
|
|
11
|
+
}
|
|
5
12
|
|
|
6
13
|
function createDefaultLogger(name, logger) {
|
|
7
14
|
const println = !!logger && typeof logger === "function" ? logger : logger?.println ?? ((message, ...args) => {
|
|
@@ -52,20 +59,31 @@ const _Option = class {
|
|
|
52
59
|
}
|
|
53
60
|
};
|
|
54
61
|
let Option = _Option;
|
|
55
|
-
Option.OptionRE = /^(-[a-zA-
|
|
62
|
+
Option.OptionRE = /^(-[a-zA-Z0-9], )?--([a-zA-Z0-9\-]+)( \[[a-zA-Z0-9]+\]| <[a-zA-Z0-9]+>)?$/;
|
|
56
63
|
|
|
57
64
|
const _Command = class {
|
|
58
65
|
constructor(format, config) {
|
|
59
66
|
this.options = [];
|
|
60
|
-
this.format =
|
|
61
|
-
|
|
67
|
+
this.format = format;
|
|
68
|
+
const pieces = format.split(" ").map((t) => t.trim()).filter(Boolean);
|
|
69
|
+
const prefix = pieces.filter((p) => !isArg(p));
|
|
70
|
+
this.default = prefix.length === 0;
|
|
71
|
+
this.prefix = this.default ? [] : [prefix];
|
|
72
|
+
this.arguments = pieces.filter(isArg);
|
|
62
73
|
this.description = config.description ?? "";
|
|
63
|
-
this.conditionFn = config.condition;
|
|
64
74
|
this.logger = config.logger;
|
|
65
|
-
if (
|
|
75
|
+
if (pieces.length > _Command.MaxDep) {
|
|
66
76
|
this.logger.warn(`Command format string "${format}" is too long`);
|
|
67
77
|
}
|
|
68
78
|
}
|
|
79
|
+
get isInternal() {
|
|
80
|
+
return this instanceof InternalCommand;
|
|
81
|
+
}
|
|
82
|
+
alias(command) {
|
|
83
|
+
const pieces = command.split(" ").map((t) => t.trim()).filter(Boolean);
|
|
84
|
+
this.prefix.push(pieces);
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
69
87
|
option(format, configOrDescription = "", otherConfig = {}) {
|
|
70
88
|
const config = typeof configOrDescription === "object" ? configOrDescription : { ...otherConfig, description: configOrDescription };
|
|
71
89
|
try {
|
|
@@ -76,59 +94,56 @@ const _Command = class {
|
|
|
76
94
|
}
|
|
77
95
|
return this;
|
|
78
96
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (this.conditionFn) {
|
|
84
|
-
return this.conditionFn(args);
|
|
97
|
+
hasPrefix(parsedArgs) {
|
|
98
|
+
const argv = parsedArgs["_"];
|
|
99
|
+
if (argv.length === 0) {
|
|
100
|
+
return this.default;
|
|
85
101
|
} else {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const isCmd = (t) => t[0] !== "[" && t[0] !== "<";
|
|
89
|
-
for (let i = 0; i < this.format.length; i++) {
|
|
90
|
-
if (!isCmd(this.format[i])) {
|
|
102
|
+
for (const prefix of this.prefix) {
|
|
103
|
+
if (prefix.length > 0 && prefix[0] === argv[0]) {
|
|
91
104
|
return true;
|
|
92
105
|
}
|
|
93
|
-
|
|
94
|
-
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
shouldRun(parsedArgs) {
|
|
111
|
+
const args = parsedArgs["_"];
|
|
112
|
+
for (const prefix of this.prefix) {
|
|
113
|
+
let match = true;
|
|
114
|
+
for (let i = 0; match && i < prefix.length; i++) {
|
|
115
|
+
if (args[i] !== prefix[i]) {
|
|
116
|
+
match = false;
|
|
95
117
|
}
|
|
96
118
|
}
|
|
97
|
-
|
|
119
|
+
if (match) {
|
|
120
|
+
args.splice(0, prefix.length);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
98
123
|
}
|
|
124
|
+
if (this.default)
|
|
125
|
+
return true;
|
|
126
|
+
return false;
|
|
99
127
|
}
|
|
100
128
|
parseArgs(args, globalOptions) {
|
|
101
|
-
if (this.conditionFn) {
|
|
102
|
-
const argumentss2 = args["_"];
|
|
103
|
-
const options2 = args;
|
|
104
|
-
delete options2["_"];
|
|
105
|
-
return {
|
|
106
|
-
command: this,
|
|
107
|
-
arguments: argumentss2,
|
|
108
|
-
options: args
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
const isCmd = (t) => t[0] !== "[" && t[0] !== "<";
|
|
112
129
|
const argumentss = [];
|
|
113
|
-
for (let i = 0; i < this.
|
|
114
|
-
if (isCmd(this.format[i]))
|
|
115
|
-
continue;
|
|
130
|
+
for (let i = 0; i < this.arguments.length; i++) {
|
|
116
131
|
if (i < args["_"].length) {
|
|
117
|
-
if (this.
|
|
132
|
+
if (this.arguments[i].startsWith("[...")) {
|
|
118
133
|
argumentss.push(args["_"].slice(i).map(String));
|
|
119
134
|
} else {
|
|
120
135
|
argumentss.push(String(args["_"][i]));
|
|
121
136
|
}
|
|
122
137
|
} else {
|
|
123
|
-
if (this.
|
|
124
|
-
this.logger.warn(`You should provide the argument "${this.
|
|
125
|
-
argumentss.push(
|
|
126
|
-
} else if (this.
|
|
138
|
+
if (this.arguments[i].startsWith("<")) {
|
|
139
|
+
this.logger.warn(`You should provide the argument "${this.arguments[i]}"`);
|
|
140
|
+
argumentss.push("");
|
|
141
|
+
} else if (this.arguments[i].startsWith("[...")) {
|
|
127
142
|
argumentss.push([]);
|
|
128
|
-
} else if (this.
|
|
143
|
+
} else if (this.arguments[i].startsWith("[")) {
|
|
129
144
|
argumentss.push(void 0);
|
|
130
145
|
} else {
|
|
131
|
-
this.logger.warn(`unknown format string ("${this.
|
|
146
|
+
this.logger.warn(`unknown format string ("${this.arguments[i]}")`);
|
|
132
147
|
}
|
|
133
148
|
}
|
|
134
149
|
}
|
|
@@ -152,13 +167,14 @@ const _Command = class {
|
|
|
152
167
|
options[name] = void 0;
|
|
153
168
|
}
|
|
154
169
|
}
|
|
155
|
-
if (rawOption.
|
|
156
|
-
options[name]
|
|
157
|
-
} else if (rawOption.default) {
|
|
158
|
-
if (!options[name]) {
|
|
170
|
+
if (rawOption.default !== void 0) {
|
|
171
|
+
if (options[name] === void 0 || options[name] === false) {
|
|
159
172
|
options[name] = rawOption.default;
|
|
160
173
|
}
|
|
161
174
|
}
|
|
175
|
+
if (rawOption.construct !== void 0) {
|
|
176
|
+
options[name] = rawOption.construct(options[name]);
|
|
177
|
+
}
|
|
162
178
|
}
|
|
163
179
|
for (const key of Object.keys(options)) {
|
|
164
180
|
if (!fullOptions.has(key)) {
|
|
@@ -173,88 +189,115 @@ const _Command = class {
|
|
|
173
189
|
}
|
|
174
190
|
action(fn) {
|
|
175
191
|
this.actionFn = fn;
|
|
176
|
-
return this;
|
|
177
192
|
}
|
|
178
193
|
async run(...args) {
|
|
179
194
|
if (this.actionFn) {
|
|
180
|
-
return await this.actionFn(...args, { logger: this.logger });
|
|
195
|
+
return await this.actionFn(...args, { logger: this.logger, color: kolorist });
|
|
181
196
|
} else {
|
|
182
|
-
this.logger.warn(`You may miss action function in "${this.format}"`);
|
|
197
|
+
this.logger.warn(`You may miss action function in ${this.format ? `"${this.format}"` : "<default command>"}`);
|
|
183
198
|
}
|
|
184
199
|
}
|
|
185
200
|
};
|
|
186
201
|
let Command = _Command;
|
|
187
202
|
Command.MaxDep = 5;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
203
|
+
class InternalCommand extends Command {
|
|
204
|
+
hasPrefix(_args) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
parseArgs(args, _globalOptions) {
|
|
208
|
+
const argumentss = args["_"];
|
|
209
|
+
const options = args;
|
|
210
|
+
delete options["_"];
|
|
211
|
+
delete options["help"];
|
|
212
|
+
delete options["version"];
|
|
213
|
+
return {
|
|
214
|
+
command: this,
|
|
215
|
+
arguments: argumentss,
|
|
216
|
+
options: args
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
class HelpCommand extends InternalCommand {
|
|
221
|
+
constructor(commands, help, logger) {
|
|
222
|
+
super("-h, --help", { description: "Display this message", logger });
|
|
223
|
+
this.runCommands = [];
|
|
224
|
+
this.helpCommands = [];
|
|
225
|
+
this.commands = commands;
|
|
226
|
+
this.help = help;
|
|
227
|
+
}
|
|
228
|
+
shouldRun(args) {
|
|
229
|
+
const isRestEmpty = !args["--"]?.length;
|
|
230
|
+
if ((args.help || args.h) && isRestEmpty) {
|
|
231
|
+
if (args["_"].length > 0) {
|
|
232
|
+
for (const cmd of this.commands) {
|
|
233
|
+
if (!cmd.default && !cmd.isInternal) {
|
|
234
|
+
if (cmd.shouldRun(args)) {
|
|
235
|
+
this.runCommands.push(cmd);
|
|
236
|
+
} else if (cmd.hasPrefix(args)) {
|
|
237
|
+
this.helpCommands.push(cmd);
|
|
199
238
|
}
|
|
200
239
|
}
|
|
201
240
|
}
|
|
202
|
-
return true;
|
|
203
|
-
} else {
|
|
204
|
-
return false;
|
|
205
241
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
for (const line of breadc.help(helpCommand)) {
|
|
210
|
-
breadc.logger.println(line);
|
|
242
|
+
return true;
|
|
243
|
+
} else {
|
|
244
|
+
return false;
|
|
211
245
|
}
|
|
212
|
-
}
|
|
246
|
+
}
|
|
247
|
+
async run() {
|
|
248
|
+
const shouldHelp = this.runCommands.length > 0 ? this.runCommands : this.helpCommands;
|
|
249
|
+
for (const line of this.help(shouldHelp)) {
|
|
250
|
+
this.logger.println(line);
|
|
251
|
+
}
|
|
252
|
+
this.runCommands.splice(0);
|
|
253
|
+
this.helpCommands.splice(0);
|
|
254
|
+
}
|
|
213
255
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
256
|
+
class VersionCommand extends InternalCommand {
|
|
257
|
+
constructor(version, logger) {
|
|
258
|
+
super("-v, --version", { description: "Display version number", logger });
|
|
259
|
+
this.version = version;
|
|
260
|
+
}
|
|
261
|
+
shouldRun(args) {
|
|
262
|
+
const isEmpty = !args["_"].length && !args["--"]?.length;
|
|
263
|
+
if (args.version && isEmpty) {
|
|
264
|
+
return true;
|
|
265
|
+
} else if (args.v && isEmpty) {
|
|
266
|
+
return true;
|
|
267
|
+
} else {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async run() {
|
|
272
|
+
this.logger.println(this.version);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function isArg(arg) {
|
|
276
|
+
return arg[0] === "[" && arg[arg.length - 1] === "]" || arg[0] === "<" && arg[arg.length - 1] === ">";
|
|
230
277
|
}
|
|
231
278
|
|
|
232
279
|
class Breadc {
|
|
233
280
|
constructor(name, option) {
|
|
234
281
|
this.options = [];
|
|
235
282
|
this.commands = [];
|
|
283
|
+
this.callbacks = {
|
|
284
|
+
pre: [],
|
|
285
|
+
post: []
|
|
286
|
+
};
|
|
236
287
|
this.name = name;
|
|
237
288
|
this._version = option.version ?? "unknown";
|
|
238
289
|
this.description = option.description;
|
|
239
290
|
this.logger = createDefaultLogger(name, option.logger);
|
|
240
|
-
|
|
241
|
-
name: this.name,
|
|
242
|
-
version: () => this.version.call(this),
|
|
243
|
-
help: (command) => this.help.call(this, command),
|
|
244
|
-
logger: this.logger,
|
|
245
|
-
options: this.options,
|
|
246
|
-
commands: this.commands
|
|
247
|
-
};
|
|
248
|
-
this.commands.push(createVersionCommand(breadc), createHelpCommand(breadc));
|
|
291
|
+
this.commands.push(new VersionCommand(this.version(), this.logger), new HelpCommand(this.commands, this.help.bind(this), this.logger));
|
|
249
292
|
}
|
|
250
293
|
version() {
|
|
251
294
|
return `${this.name}/${this._version}`;
|
|
252
295
|
}
|
|
253
|
-
help(
|
|
296
|
+
help(commands = []) {
|
|
254
297
|
const output = [];
|
|
255
298
|
const println = (msg) => output.push(msg);
|
|
256
299
|
println(this.version());
|
|
257
|
-
if (
|
|
300
|
+
if (commands.length === 0) {
|
|
258
301
|
if (this.description) {
|
|
259
302
|
println("");
|
|
260
303
|
if (Array.isArray(this.description)) {
|
|
@@ -265,27 +308,26 @@ class Breadc {
|
|
|
265
308
|
println(this.description);
|
|
266
309
|
}
|
|
267
310
|
}
|
|
268
|
-
} else {
|
|
269
|
-
if (command.description) {
|
|
270
|
-
println("");
|
|
271
|
-
println(command.description);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
if (!command) {
|
|
275
311
|
if (this.defaultCommand) {
|
|
276
312
|
println(``);
|
|
277
313
|
println(`Usage:`);
|
|
278
|
-
println(` $ ${this.name} ${this.defaultCommand.format
|
|
314
|
+
println(` $ ${this.name} ${this.defaultCommand.format}`);
|
|
315
|
+
}
|
|
316
|
+
} else if (commands.length === 1) {
|
|
317
|
+
const command = commands[0];
|
|
318
|
+
if (command.description) {
|
|
319
|
+
println("");
|
|
320
|
+
println(command.description);
|
|
279
321
|
}
|
|
280
|
-
} else {
|
|
281
322
|
println(``);
|
|
282
323
|
println(`Usage:`);
|
|
283
|
-
println(` $ ${this.name} ${command.format
|
|
324
|
+
println(` $ ${this.name} ${command.format}`);
|
|
284
325
|
}
|
|
285
|
-
if (
|
|
326
|
+
if (commands.length !== 1) {
|
|
327
|
+
const cmdList = (commands.length === 0 ? this.commands : commands).filter((c) => !c.isInternal);
|
|
286
328
|
println(``);
|
|
287
329
|
println(`Commands:`);
|
|
288
|
-
const commandHelps =
|
|
330
|
+
const commandHelps = cmdList.map((c) => [` $ ${this.name} ${c.format}`, c.description]);
|
|
289
331
|
for (const line of twoColumn(commandHelps)) {
|
|
290
332
|
println(line);
|
|
291
333
|
}
|
|
@@ -293,7 +335,7 @@ class Breadc {
|
|
|
293
335
|
println(``);
|
|
294
336
|
println(`Options:`);
|
|
295
337
|
const optionHelps = [].concat([
|
|
296
|
-
...
|
|
338
|
+
...commands.length > 0 ? commands.flatMap((cmd) => cmd.options.map((o) => [` ${o.format}`, o.description])) : [],
|
|
297
339
|
...this.options.map((o) => [` ${o.format}`, o.description]),
|
|
298
340
|
[` -h, --help`, `Display this message`],
|
|
299
341
|
[` -v, --version`, `Display version number`]
|
|
@@ -331,15 +373,28 @@ class Breadc {
|
|
|
331
373
|
...this.options,
|
|
332
374
|
...this.commands.flatMap((c) => c.options)
|
|
333
375
|
];
|
|
376
|
+
{
|
|
377
|
+
const names = /* @__PURE__ */ new Map();
|
|
378
|
+
for (const option of allowOptions) {
|
|
379
|
+
if (names.has(option.name)) {
|
|
380
|
+
const otherOption = names.get(option.name);
|
|
381
|
+
if (otherOption.type !== option.type) {
|
|
382
|
+
this.logger.warn(`Option "${option.name}" encounters conflict`);
|
|
383
|
+
}
|
|
384
|
+
} else {
|
|
385
|
+
names.set(option.name, option);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
334
389
|
const alias = allowOptions.reduce((map, o) => {
|
|
335
390
|
if (o.shortcut) {
|
|
336
391
|
map[o.shortcut] = o.name;
|
|
337
392
|
}
|
|
338
393
|
return map;
|
|
339
|
-
}, {});
|
|
394
|
+
}, { h: "help", v: "version" });
|
|
340
395
|
const argv = minimist(args, {
|
|
341
396
|
string: allowOptions.filter((o) => o.type === "string").map((o) => o.name),
|
|
342
|
-
boolean: allowOptions.filter((o) => o.type === "boolean").map((o) => o.name),
|
|
397
|
+
boolean: allowOptions.filter((o) => o.type === "boolean").map((o) => o.name).concat(["help", "version"]),
|
|
343
398
|
alias,
|
|
344
399
|
unknown: (t) => {
|
|
345
400
|
if (t[0] !== "-")
|
|
@@ -363,32 +418,33 @@ class Breadc {
|
|
|
363
418
|
}
|
|
364
419
|
}
|
|
365
420
|
if (this.defaultCommand) {
|
|
421
|
+
this.defaultCommand.shouldRun(argv);
|
|
366
422
|
return this.defaultCommand.parseArgs(argv, this.options);
|
|
367
423
|
}
|
|
368
424
|
const argumentss = argv["_"];
|
|
369
425
|
const options = argv;
|
|
370
426
|
delete options["_"];
|
|
427
|
+
delete options["help"];
|
|
428
|
+
delete options["version"];
|
|
371
429
|
return {
|
|
372
430
|
command: void 0,
|
|
373
431
|
arguments: argumentss,
|
|
374
432
|
options
|
|
375
433
|
};
|
|
376
434
|
}
|
|
435
|
+
on(event, fn) {
|
|
436
|
+
this.callbacks[event].push(fn);
|
|
437
|
+
}
|
|
377
438
|
async run(args) {
|
|
378
439
|
const parsed = this.parse(args);
|
|
379
440
|
if (parsed.command) {
|
|
380
|
-
|
|
441
|
+
await Promise.all(this.callbacks.pre.map((fn) => fn(parsed.options)));
|
|
442
|
+
const returnValue = await parsed.command.run(...parsed.arguments, parsed.options);
|
|
443
|
+
await Promise.all(this.callbacks.post.map((fn) => fn(parsed.options)));
|
|
444
|
+
return returnValue;
|
|
381
445
|
}
|
|
382
446
|
}
|
|
383
447
|
}
|
|
384
|
-
function twoColumn(texts, split = " ") {
|
|
385
|
-
const left = padRight(texts.map((t) => t[0]));
|
|
386
|
-
return left.map((l, idx) => l + split + texts[idx][1]);
|
|
387
|
-
}
|
|
388
|
-
function padRight(texts, fill = " ") {
|
|
389
|
-
const length = texts.map((t) => t.length).reduce((max, l) => Math.max(max, l), 0);
|
|
390
|
-
return texts.map((t) => t + fill.repeat(length - t.length));
|
|
391
|
-
}
|
|
392
448
|
|
|
393
449
|
function breadc(name, option = {}) {
|
|
394
450
|
return new Breadc(name, option);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "breadc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Yet another Command Line Application Framework with fully strong TypeScript support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"bumpp": "^8.2.1",
|
|
44
44
|
"prettier": "^2.7.1",
|
|
45
45
|
"typescript": "^4.7.4",
|
|
46
|
-
"unbuild": "^0.7.
|
|
47
|
-
"vite": "^
|
|
48
|
-
"vitest": "^0.
|
|
46
|
+
"unbuild": "^0.7.6",
|
|
47
|
+
"vite": "^3.0.2",
|
|
48
|
+
"vitest": "^0.18.1"
|
|
49
49
|
},
|
|
50
|
-
"packageManager": "pnpm@7.
|
|
50
|
+
"packageManager": "pnpm@7.5.2",
|
|
51
51
|
"scripts": {
|
|
52
52
|
"build": "unbuild",
|
|
53
53
|
"format": "prettier --write src/**/*.ts test/*.ts examples/*.ts",
|