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