breadc 0.9.7 → 1.0.0-beta.2
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 +14 -36
- package/dist/index.d.mts +2 -221
- package/dist/index.mjs +2 -763
- package/package.json +10 -29
- package/dist/index.cjs +0 -770
- package/dist/index.d.cts +0 -221
- package/dist/index.d.ts +0 -221
package/dist/index.mjs
CHANGED
|
@@ -1,764 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BreadcAppError, BreadcError, ResolveCommandError, ResolveGroupError, ResolveOptionError, argument, breadc, command, group, option } from "@breadc/core";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
class ParseError extends Error {
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function camelCase(text) {
|
|
9
|
-
return text.split("-").map((t, idx) => idx === 0 ? t : t[0].toUpperCase() + t.slice(1)).join("");
|
|
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
|
-
}
|
|
19
|
-
|
|
20
|
-
const OptionRE = /^(-[a-zA-Z], )?--([a-zA-Z0-9\-]+)( <[a-zA-Z0-9\-]+>)?$/;
|
|
21
|
-
function makeOption(format, config = { default: void 0 }) {
|
|
22
|
-
let name = "";
|
|
23
|
-
let short = void 0;
|
|
24
|
-
const match = OptionRE.exec(format);
|
|
25
|
-
if (match) {
|
|
26
|
-
name = match[2];
|
|
27
|
-
if (match[1]) {
|
|
28
|
-
short = match[1][1];
|
|
29
|
-
}
|
|
30
|
-
if (match[3]) {
|
|
31
|
-
if (name.startsWith("no-")) {
|
|
32
|
-
throw new BreadcError(`Can not parse option format (${format})`);
|
|
33
|
-
}
|
|
34
|
-
const initial = config.default ?? void 0;
|
|
35
|
-
return {
|
|
36
|
-
format,
|
|
37
|
-
type: "string",
|
|
38
|
-
name,
|
|
39
|
-
short,
|
|
40
|
-
description: config.description ?? "",
|
|
41
|
-
order: 0,
|
|
42
|
-
// @ts-ignore
|
|
43
|
-
initial: config.cast ? config.cast(initial) : initial,
|
|
44
|
-
cast: config.cast
|
|
45
|
-
};
|
|
46
|
-
} else {
|
|
47
|
-
if (name.startsWith("no-")) {
|
|
48
|
-
name = name.slice(3);
|
|
49
|
-
config.default = true;
|
|
50
|
-
}
|
|
51
|
-
const initial = config.default === void 0 || config.default === null ? false : config.default;
|
|
52
|
-
return {
|
|
53
|
-
format,
|
|
54
|
-
type: "boolean",
|
|
55
|
-
name,
|
|
56
|
-
short,
|
|
57
|
-
description: config.description ?? "",
|
|
58
|
-
order: 0,
|
|
59
|
-
// @ts-ignore
|
|
60
|
-
initial: config.cast ? config.cast(initial) : initial,
|
|
61
|
-
cast: config.cast
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
} else {
|
|
65
|
-
throw new BreadcError(`Can not parse option format (${format})`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
const initContextOptions = (options, context) => {
|
|
69
|
-
for (const option of options) {
|
|
70
|
-
context.options.set(option.name, option);
|
|
71
|
-
if (option.short) {
|
|
72
|
-
context.options.set(option.short, option);
|
|
73
|
-
}
|
|
74
|
-
if (option.type === "boolean") {
|
|
75
|
-
context.options.set("no-" + option.name, option);
|
|
76
|
-
}
|
|
77
|
-
if (option.initial !== void 0) {
|
|
78
|
-
context.result.options[camelCase(option.name)] = option.initial;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
var __defProp = Object.defineProperty;
|
|
84
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
85
|
-
var __publicField = (obj, key, value) => {
|
|
86
|
-
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
87
|
-
return value;
|
|
88
|
-
};
|
|
89
|
-
class Token {
|
|
90
|
-
constructor(text) {
|
|
91
|
-
__publicField(this, "text");
|
|
92
|
-
__publicField(this, "_type");
|
|
93
|
-
this.text = text;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* @returns Raw argument text
|
|
97
|
-
*/
|
|
98
|
-
raw() {
|
|
99
|
-
return this.text;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* @returns Number representation
|
|
103
|
-
*/
|
|
104
|
-
number() {
|
|
105
|
-
return Number(this.text);
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* @returns Remove start - for long or short option
|
|
109
|
-
*/
|
|
110
|
-
option() {
|
|
111
|
-
return this.text.replace(/^-+/, "");
|
|
112
|
-
}
|
|
113
|
-
isOption() {
|
|
114
|
-
return this.type() === "long" || this._type === "short";
|
|
115
|
-
}
|
|
116
|
-
isText() {
|
|
117
|
-
return this.type() === "number" || this._type === "string";
|
|
118
|
-
}
|
|
119
|
-
type() {
|
|
120
|
-
if (this._type) {
|
|
121
|
-
return this._type;
|
|
122
|
-
} else if (this.text === "--") {
|
|
123
|
-
return this._type = "--";
|
|
124
|
-
} else if (this.text === "-") {
|
|
125
|
-
return this._type = "-";
|
|
126
|
-
} else if (!isNaN(Number(this.text))) {
|
|
127
|
-
return this._type = "number";
|
|
128
|
-
} else if (this.text.startsWith("--")) {
|
|
129
|
-
return this._type = "long";
|
|
130
|
-
} else if (this.text.startsWith("-")) {
|
|
131
|
-
return this._type = "short";
|
|
132
|
-
} else {
|
|
133
|
-
return this._type = "string";
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/* c8 ignore next 1 */
|
|
137
|
-
}
|
|
138
|
-
class Lexer {
|
|
139
|
-
constructor(rawArgs) {
|
|
140
|
-
__publicField(this, "rawArgs");
|
|
141
|
-
__publicField(this, "cursor", 0);
|
|
142
|
-
this.rawArgs = rawArgs;
|
|
143
|
-
}
|
|
144
|
-
next() {
|
|
145
|
-
const value = this.rawArgs[this.cursor];
|
|
146
|
-
this.cursor += 1;
|
|
147
|
-
return value ? new Token(value) : void 0;
|
|
148
|
-
}
|
|
149
|
-
hasNext() {
|
|
150
|
-
return this.cursor < this.rawArgs.length;
|
|
151
|
-
}
|
|
152
|
-
peek() {
|
|
153
|
-
const value = this.rawArgs[this.cursor];
|
|
154
|
-
return value ? new Token(value) : void 0;
|
|
155
|
-
}
|
|
156
|
-
[Symbol.iterator]() {
|
|
157
|
-
const that = this;
|
|
158
|
-
return {
|
|
159
|
-
next() {
|
|
160
|
-
const value = that.rawArgs[that.cursor];
|
|
161
|
-
that.cursor += 1;
|
|
162
|
-
return {
|
|
163
|
-
value: value !== void 0 ? new Token(value) : void 0,
|
|
164
|
-
done: that.cursor > that.rawArgs.length
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function makeTreeNode(pnode) {
|
|
172
|
-
const node = {
|
|
173
|
-
children: /* @__PURE__ */ new Map(),
|
|
174
|
-
init() {
|
|
175
|
-
},
|
|
176
|
-
next(token, context) {
|
|
177
|
-
const t = token.raw();
|
|
178
|
-
context.result["--"].push(t);
|
|
179
|
-
if (node.children.has(t)) {
|
|
180
|
-
const next = node.children.get(t);
|
|
181
|
-
next.init(context);
|
|
182
|
-
return next;
|
|
183
|
-
} else {
|
|
184
|
-
return node;
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
finish() {
|
|
188
|
-
return pnode.command?.callback;
|
|
189
|
-
},
|
|
190
|
-
...pnode
|
|
191
|
-
};
|
|
192
|
-
return node;
|
|
193
|
-
}
|
|
194
|
-
function parseOption(cursor, token, context) {
|
|
195
|
-
const o = token.option();
|
|
196
|
-
const [key, rawV] = o.split("=");
|
|
197
|
-
if (context.options.has(key)) {
|
|
198
|
-
const option = context.options.get(key);
|
|
199
|
-
const name = camelCase(option.name);
|
|
200
|
-
if (option.parse) {
|
|
201
|
-
return option.parse(cursor, token, context);
|
|
202
|
-
} else if (option.type === "boolean") {
|
|
203
|
-
const negative = key.startsWith("no-");
|
|
204
|
-
if (rawV === void 0 || ["true", "yes", "t", "y"].includes(rawV.toLowerCase())) {
|
|
205
|
-
context.result.options[name] = !negative ? true : false;
|
|
206
|
-
} else if (["false", "no", "f", "n"].includes(rawV.toLowerCase())) {
|
|
207
|
-
context.result.options[name] = !negative ? false : true;
|
|
208
|
-
} else {
|
|
209
|
-
throw new ParseError(`Unexpected value ${rawV} for ${option.format}`);
|
|
210
|
-
}
|
|
211
|
-
} else if (option.type === "string") {
|
|
212
|
-
if (rawV !== void 0) {
|
|
213
|
-
context.result.options[name] = rawV;
|
|
214
|
-
} else {
|
|
215
|
-
const value = context.lexer.next();
|
|
216
|
-
if (value !== void 0 && !value.isOption()) {
|
|
217
|
-
context.result.options[name] = value.raw();
|
|
218
|
-
} else {
|
|
219
|
-
throw new ParseError(
|
|
220
|
-
`You should provide arguments for ${option.format}`
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
} else {
|
|
225
|
-
throw new ParseError("unreachable");
|
|
226
|
-
}
|
|
227
|
-
if (option.cast) {
|
|
228
|
-
context.result.options[name] = option.cast(context.result.options[name]);
|
|
229
|
-
}
|
|
230
|
-
} else {
|
|
231
|
-
switch (context.config.allowUnknownOption) {
|
|
232
|
-
case "rest":
|
|
233
|
-
context.result["--"].push(token.raw());
|
|
234
|
-
case "skip":
|
|
235
|
-
break;
|
|
236
|
-
case "error":
|
|
237
|
-
default:
|
|
238
|
-
throw new ParseError(`Unknown option ${token.raw()}`);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return cursor;
|
|
242
|
-
}
|
|
243
|
-
function parse(root, args) {
|
|
244
|
-
const lexer = new Lexer(args);
|
|
245
|
-
const context = {
|
|
246
|
-
lexer,
|
|
247
|
-
options: /* @__PURE__ */ new Map(),
|
|
248
|
-
result: {
|
|
249
|
-
arguments: [],
|
|
250
|
-
options: {},
|
|
251
|
-
"--": []
|
|
252
|
-
},
|
|
253
|
-
meta: {},
|
|
254
|
-
config: {
|
|
255
|
-
allowUnknownOption: "error"
|
|
256
|
-
},
|
|
257
|
-
parseOption
|
|
258
|
-
};
|
|
259
|
-
let cursor = root;
|
|
260
|
-
root.init(context);
|
|
261
|
-
for (const token of lexer) {
|
|
262
|
-
if (token.type() === "--") {
|
|
263
|
-
break;
|
|
264
|
-
} else if (token.isOption()) {
|
|
265
|
-
const res = context.parseOption(cursor, token, context);
|
|
266
|
-
if (res === false) {
|
|
267
|
-
break;
|
|
268
|
-
} else {
|
|
269
|
-
cursor = res;
|
|
270
|
-
}
|
|
271
|
-
} else if (token.isText()) {
|
|
272
|
-
const res = cursor.next(token, context);
|
|
273
|
-
if (res === false) {
|
|
274
|
-
break;
|
|
275
|
-
} else {
|
|
276
|
-
cursor = res;
|
|
277
|
-
}
|
|
278
|
-
} else {
|
|
279
|
-
throw new ParseError("unreachable");
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
const callback = cursor.finish(context);
|
|
283
|
-
for (const token of lexer) {
|
|
284
|
-
context.result["--"].push(token.raw());
|
|
285
|
-
}
|
|
286
|
-
return {
|
|
287
|
-
callback,
|
|
288
|
-
matched: {
|
|
289
|
-
node: cursor,
|
|
290
|
-
command: cursor.command,
|
|
291
|
-
option: cursor.option
|
|
292
|
-
},
|
|
293
|
-
meta: context.meta,
|
|
294
|
-
arguments: context.result.arguments,
|
|
295
|
-
options: context.result.options,
|
|
296
|
-
"--": context.result["--"]
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function makeCommand(format, config, root, container) {
|
|
301
|
-
const args = [];
|
|
302
|
-
const options = [];
|
|
303
|
-
const command = {
|
|
304
|
-
callback: void 0,
|
|
305
|
-
format,
|
|
306
|
-
description: config.description ?? "",
|
|
307
|
-
_default: false,
|
|
308
|
-
_arguments: args,
|
|
309
|
-
_options: options,
|
|
310
|
-
option(format2, _config, _config2 = {}) {
|
|
311
|
-
const config2 = typeof _config === "string" ? { description: _config, ..._config2 } : _config;
|
|
312
|
-
const option = makeOption(format2, config2);
|
|
313
|
-
options.push(option);
|
|
314
|
-
return command;
|
|
315
|
-
},
|
|
316
|
-
alias(format2) {
|
|
317
|
-
const aliasArgs = [];
|
|
318
|
-
const node2 = makeNode(aliasArgs);
|
|
319
|
-
function* g() {
|
|
320
|
-
for (const f of format2.split(" ")) {
|
|
321
|
-
yield { type: "const", name: f };
|
|
322
|
-
}
|
|
323
|
-
for (const a of args.filter((a2) => a2.type !== "const")) {
|
|
324
|
-
yield a;
|
|
325
|
-
}
|
|
326
|
-
return void 0;
|
|
327
|
-
}
|
|
328
|
-
insertTreeNode(aliasArgs, node2, g());
|
|
329
|
-
return command;
|
|
330
|
-
},
|
|
331
|
-
action(fn) {
|
|
332
|
-
command.callback = async (parsed) => {
|
|
333
|
-
await container.preCommand(command, parsed);
|
|
334
|
-
const result = await fn(...parsed.arguments, {
|
|
335
|
-
...parsed.options,
|
|
336
|
-
"--": parsed["--"]
|
|
337
|
-
});
|
|
338
|
-
await container.postCommand(command, parsed);
|
|
339
|
-
return result;
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
};
|
|
343
|
-
const node = makeNode(args);
|
|
344
|
-
insertTreeNode(args, node, parseCommandFormat(format));
|
|
345
|
-
return command;
|
|
346
|
-
function makeNode(args2) {
|
|
347
|
-
return makeTreeNode({
|
|
348
|
-
command,
|
|
349
|
-
init(context) {
|
|
350
|
-
context.config.allowUnknownOption = config.allowUnknownOption ?? "error";
|
|
351
|
-
initContextOptions(options, context);
|
|
352
|
-
},
|
|
353
|
-
finish(context) {
|
|
354
|
-
const rest = context.result["--"];
|
|
355
|
-
for (let i = 0; i < args2.length; i++) {
|
|
356
|
-
if (args2[i].type === "const") {
|
|
357
|
-
if (rest[i] !== args2[i].name) {
|
|
358
|
-
throw new ParseError(`Sub-command ${args2[i].name} mismatch`);
|
|
359
|
-
}
|
|
360
|
-
} else if (args2[i].type === "require") {
|
|
361
|
-
if (i >= rest.length) {
|
|
362
|
-
throw new ParseError(
|
|
363
|
-
`You must provide require argument ${args2[i].name}`
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
context.result.arguments.push(rest[i]);
|
|
367
|
-
} else if (args2[i].type === "optional") {
|
|
368
|
-
context.result.arguments.push(rest[i]);
|
|
369
|
-
} else if (args2[i].type === "rest") {
|
|
370
|
-
context.result.arguments.push(rest.splice(i));
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
context.result["--"] = rest.splice(args2.length);
|
|
374
|
-
return command.callback;
|
|
375
|
-
}
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
function insertTreeNode(args2, node2, parsed) {
|
|
379
|
-
let cursor = root;
|
|
380
|
-
for (const arg of parsed) {
|
|
381
|
-
args2.push(arg);
|
|
382
|
-
if (arg.type === "const") {
|
|
383
|
-
const name = arg.name;
|
|
384
|
-
if (cursor.children.has(name)) {
|
|
385
|
-
cursor = cursor.children.get(name);
|
|
386
|
-
} else {
|
|
387
|
-
const internalNode = makeTreeNode({
|
|
388
|
-
next(token, context) {
|
|
389
|
-
const t = token.raw();
|
|
390
|
-
context.result["--"].push(t);
|
|
391
|
-
if (internalNode.children.has(t)) {
|
|
392
|
-
const next = internalNode.children.get(t);
|
|
393
|
-
next.init(context);
|
|
394
|
-
return next;
|
|
395
|
-
} else {
|
|
396
|
-
throw new ParseError(`Unknown sub-command (${t})`);
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
finish() {
|
|
400
|
-
throw new ParseError(`Unknown sub-command`);
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
cursor.children.set(name, internalNode);
|
|
404
|
-
cursor = internalNode;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
cursor.command = command;
|
|
409
|
-
if (cursor !== root) {
|
|
410
|
-
for (const [key, value] of cursor.children) {
|
|
411
|
-
node2.children.set(key, value);
|
|
412
|
-
}
|
|
413
|
-
cursor.children = node2.children;
|
|
414
|
-
cursor.next = node2.next;
|
|
415
|
-
cursor.init = node2.init;
|
|
416
|
-
cursor.finish = node2.finish;
|
|
417
|
-
} else {
|
|
418
|
-
command._default = true;
|
|
419
|
-
cursor.finish = node2.finish;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
function* parseCommandFormat(format) {
|
|
424
|
-
let state = 0;
|
|
425
|
-
for (let i = 0; i < format.length; i++) {
|
|
426
|
-
if (format[i] === "<") {
|
|
427
|
-
if (state !== 0 && state !== 1) {
|
|
428
|
-
throw new BreadcError(
|
|
429
|
-
`Required arguments should be placed before optional or rest arguments`
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
const start = i;
|
|
433
|
-
while (i < format.length && format[i] !== ">") {
|
|
434
|
-
i++;
|
|
435
|
-
}
|
|
436
|
-
const name = format.slice(start + 1, i);
|
|
437
|
-
state = 1;
|
|
438
|
-
yield { type: "require", name };
|
|
439
|
-
} else if (format[i] === "[") {
|
|
440
|
-
if (state !== 0 && state !== 1) {
|
|
441
|
-
throw new BreadcError(
|
|
442
|
-
`There is at most one optional or rest arguments`
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
const start = i;
|
|
446
|
-
while (i < format.length && format[i] !== "]") {
|
|
447
|
-
i++;
|
|
448
|
-
}
|
|
449
|
-
const name = format.slice(start + 1, i);
|
|
450
|
-
state = 2;
|
|
451
|
-
if (name.startsWith("...")) {
|
|
452
|
-
yield { type: "rest", name };
|
|
453
|
-
} else {
|
|
454
|
-
yield { type: "optional", name };
|
|
455
|
-
}
|
|
456
|
-
} else if (format[i] !== " ") {
|
|
457
|
-
if (state !== 0) {
|
|
458
|
-
throw new BreadcError(`Sub-command should be placed at the beginning`);
|
|
459
|
-
}
|
|
460
|
-
const start = i;
|
|
461
|
-
while (i < format.length && format[i] !== " ") {
|
|
462
|
-
i++;
|
|
463
|
-
}
|
|
464
|
-
const name = format.slice(start, i);
|
|
465
|
-
state = 0;
|
|
466
|
-
yield { type: "const", name };
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
return void 0;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
function makePluginContainer(plugins = []) {
|
|
473
|
-
const onPreCommand = {};
|
|
474
|
-
const onPostCommand = {};
|
|
475
|
-
for (const plugin of plugins) {
|
|
476
|
-
if (typeof plugin.onPreCommand === "function") {
|
|
477
|
-
const key = "*";
|
|
478
|
-
if (!(key in onPreCommand)) {
|
|
479
|
-
onPreCommand[key] = [];
|
|
480
|
-
}
|
|
481
|
-
onPreCommand[key].push(plugin.onPreCommand);
|
|
482
|
-
} else {
|
|
483
|
-
for (const [key, fn] of Object.entries(plugin.onPreCommand ?? {})) {
|
|
484
|
-
if (!(key in onPreCommand)) {
|
|
485
|
-
onPreCommand[key] = [];
|
|
486
|
-
}
|
|
487
|
-
onPreCommand[key].push(fn);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
if (typeof plugin.onPostCommand === "function") {
|
|
491
|
-
const key = "*";
|
|
492
|
-
if (!(key in onPostCommand)) {
|
|
493
|
-
onPostCommand[key] = [];
|
|
494
|
-
}
|
|
495
|
-
onPostCommand[key].push(plugin.onPostCommand);
|
|
496
|
-
} else {
|
|
497
|
-
for (const [key, fn] of Object.entries(plugin.onPostCommand ?? {})) {
|
|
498
|
-
if (!(key in onPostCommand)) {
|
|
499
|
-
onPostCommand[key] = [];
|
|
500
|
-
}
|
|
501
|
-
onPostCommand[key].push(fn);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
const run = async (container, command, result) => {
|
|
506
|
-
const prefix = command._arguments.filter((a) => a.type === "const").map((a) => a.name);
|
|
507
|
-
if (prefix.length === 0) {
|
|
508
|
-
prefix.push("_");
|
|
509
|
-
}
|
|
510
|
-
for (let i = 0; i <= prefix.length; i++) {
|
|
511
|
-
const key = i === 0 ? "*" : prefix.slice(0, i).map(
|
|
512
|
-
(t, idx) => idx === 0 ? t : t[0].toUpperCase() + t.slice(1)
|
|
513
|
-
).join("");
|
|
514
|
-
const fns = container[key];
|
|
515
|
-
if (fns && fns.length > 0) {
|
|
516
|
-
await Promise.all(fns.map((fn) => fn(result)));
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
};
|
|
520
|
-
return {
|
|
521
|
-
init(breadc, allCommands, globalOptions) {
|
|
522
|
-
if (plugins.length === 0)
|
|
523
|
-
return;
|
|
524
|
-
for (const p of plugins) {
|
|
525
|
-
p.onInit?.(breadc, allCommands, globalOptions);
|
|
526
|
-
}
|
|
527
|
-
},
|
|
528
|
-
async preRun(breadc) {
|
|
529
|
-
if (plugins.length === 0)
|
|
530
|
-
return;
|
|
531
|
-
for (const p of plugins) {
|
|
532
|
-
await p.onPreRun?.(breadc);
|
|
533
|
-
}
|
|
534
|
-
},
|
|
535
|
-
async preCommand(command, result) {
|
|
536
|
-
if (plugins.length === 0)
|
|
537
|
-
return;
|
|
538
|
-
await run(onPreCommand, command, result);
|
|
539
|
-
},
|
|
540
|
-
async postCommand(command, result) {
|
|
541
|
-
if (plugins.length === 0)
|
|
542
|
-
return;
|
|
543
|
-
await run(onPostCommand, command, result);
|
|
544
|
-
},
|
|
545
|
-
async postRun(breadc) {
|
|
546
|
-
if (plugins.length === 0)
|
|
547
|
-
return;
|
|
548
|
-
for (const p of plugins) {
|
|
549
|
-
await p.onPostRun?.(breadc);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
function definePlugin(plugin) {
|
|
555
|
-
return plugin;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
function makeVersionCommand(name, config) {
|
|
559
|
-
let description = "Print version";
|
|
560
|
-
if (typeof config.builtin?.version === "object") {
|
|
561
|
-
if (config.builtin.version.description) {
|
|
562
|
-
description = config.builtin.version.description;
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
const node = makeTreeNode({
|
|
566
|
-
next() {
|
|
567
|
-
return false;
|
|
568
|
-
},
|
|
569
|
-
finish() {
|
|
570
|
-
return () => {
|
|
571
|
-
const text = typeof config.builtin?.version === "object" && config.builtin.version.content ? config.builtin.version.content : `${name}/${config.version ? config.version : "unknown"}`;
|
|
572
|
-
console.log(text);
|
|
573
|
-
return text;
|
|
574
|
-
};
|
|
575
|
-
}
|
|
576
|
-
});
|
|
577
|
-
const option = {
|
|
578
|
-
format: "-v, --version",
|
|
579
|
-
name: "version",
|
|
580
|
-
short: "v",
|
|
581
|
-
type: "boolean",
|
|
582
|
-
initial: void 0,
|
|
583
|
-
order: 999999999 + 1,
|
|
584
|
-
description,
|
|
585
|
-
parse() {
|
|
586
|
-
return node;
|
|
587
|
-
}
|
|
588
|
-
};
|
|
589
|
-
return option;
|
|
590
|
-
}
|
|
591
|
-
function makeHelpCommand(name, config, allCommands) {
|
|
592
|
-
function expandMessage(message) {
|
|
593
|
-
const result = [];
|
|
594
|
-
for (const row of message) {
|
|
595
|
-
if (typeof row === "function") {
|
|
596
|
-
const r = row();
|
|
597
|
-
if (r) {
|
|
598
|
-
result.push(...expandMessage(r));
|
|
599
|
-
}
|
|
600
|
-
} else if (typeof row === "string") {
|
|
601
|
-
result.push(row);
|
|
602
|
-
} else if (Array.isArray(row)) {
|
|
603
|
-
const lines = twoColumn(row);
|
|
604
|
-
for (const line of lines) {
|
|
605
|
-
result.push(line);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
return result;
|
|
610
|
-
}
|
|
611
|
-
function expandCommands(cursor) {
|
|
612
|
-
const visited = /* @__PURE__ */ new WeakSet();
|
|
613
|
-
const added = /* @__PURE__ */ new WeakSet();
|
|
614
|
-
const commands = cursor.command ? [cursor.command] : [];
|
|
615
|
-
const q = [cursor];
|
|
616
|
-
visited.add(cursor);
|
|
617
|
-
for (let i = 0; i < q.length; i++) {
|
|
618
|
-
const cur = q[i];
|
|
619
|
-
for (const [_key, cmd] of cur.children) {
|
|
620
|
-
if (!visited.has(cmd)) {
|
|
621
|
-
visited.add(cmd);
|
|
622
|
-
if (cmd.command && !added.has(cmd.command)) {
|
|
623
|
-
added.add(cmd.command);
|
|
624
|
-
commands.push(cmd.command);
|
|
625
|
-
}
|
|
626
|
-
q.push(cmd);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
const alias = /* @__PURE__ */ new Map();
|
|
631
|
-
for (const cmd of commands) {
|
|
632
|
-
if (!alias.has(cmd.format)) {
|
|
633
|
-
alias.set(cmd.format, cmd);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
return [...alias.values()];
|
|
637
|
-
}
|
|
638
|
-
let description = "Print help";
|
|
639
|
-
if (typeof config.builtin?.help === "object") {
|
|
640
|
-
if (config.builtin.help.description) {
|
|
641
|
-
description = config.builtin.help.description;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
const node = makeTreeNode({
|
|
645
|
-
next() {
|
|
646
|
-
return false;
|
|
647
|
-
},
|
|
648
|
-
finish(context) {
|
|
649
|
-
return () => {
|
|
650
|
-
const cursor = context.meta.__cursor__;
|
|
651
|
-
const usage = allCommands.length === 0 ? `[OPTIONS]` : allCommands.length === 1 ? `[OPTIONS] ${allCommands[0].format}` : allCommands.some((c) => c._default) ? `[OPTIONS] [COMMAND]` : `[OPTIONS] <COMMAND>`;
|
|
652
|
-
const output = [
|
|
653
|
-
`${name}/${config.version ? config.version : "unknown"}`,
|
|
654
|
-
() => {
|
|
655
|
-
if (config.description) {
|
|
656
|
-
return ["", config.description];
|
|
657
|
-
} else {
|
|
658
|
-
return void 0;
|
|
659
|
-
}
|
|
660
|
-
},
|
|
661
|
-
"",
|
|
662
|
-
`${bold(underline("Usage:"))} ${bold(name)} ${usage}`,
|
|
663
|
-
() => {
|
|
664
|
-
const cmds = expandCommands(cursor);
|
|
665
|
-
if (cmds.length > 0) {
|
|
666
|
-
return [
|
|
667
|
-
"",
|
|
668
|
-
bold(underline("Commands:")),
|
|
669
|
-
cmds.map((cmd) => [
|
|
670
|
-
` ${bold(name)} ${bold(cmd.format)}`,
|
|
671
|
-
cmd.description
|
|
672
|
-
])
|
|
673
|
-
];
|
|
674
|
-
} else {
|
|
675
|
-
return void 0;
|
|
676
|
-
}
|
|
677
|
-
},
|
|
678
|
-
"",
|
|
679
|
-
bold(underline("Options:")),
|
|
680
|
-
[...context.options.entries()].filter(([key, op]) => key === op.name).sort((lhs, rhs) => lhs[1].order - rhs[1].order).map(([_key, op]) => [
|
|
681
|
-
" " + (!op.short ? " " : "") + bold(op.format),
|
|
682
|
-
op.description
|
|
683
|
-
]),
|
|
684
|
-
""
|
|
685
|
-
];
|
|
686
|
-
const text = expandMessage(output).join("\n");
|
|
687
|
-
console.log(text);
|
|
688
|
-
return text;
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
});
|
|
692
|
-
const option = {
|
|
693
|
-
format: "-h, --help",
|
|
694
|
-
name: "help",
|
|
695
|
-
short: "h",
|
|
696
|
-
type: "boolean",
|
|
697
|
-
initial: void 0,
|
|
698
|
-
description,
|
|
699
|
-
order: 999999999,
|
|
700
|
-
parse(cursor, _token, context) {
|
|
701
|
-
context.meta.__cursor__ = cursor;
|
|
702
|
-
return node;
|
|
703
|
-
}
|
|
704
|
-
};
|
|
705
|
-
return option;
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
function breadc(name, config = {}) {
|
|
709
|
-
let defaultCommand = void 0;
|
|
710
|
-
const allCommands = [];
|
|
711
|
-
const globalOptions = [];
|
|
712
|
-
if (config.builtin?.help !== false) {
|
|
713
|
-
globalOptions.push(makeHelpCommand(name, config, allCommands));
|
|
714
|
-
}
|
|
715
|
-
if (config.builtin?.version !== false) {
|
|
716
|
-
globalOptions.push(makeVersionCommand(name, config));
|
|
717
|
-
}
|
|
718
|
-
const container = makePluginContainer(config.plugins);
|
|
719
|
-
const root = makeTreeNode({
|
|
720
|
-
init(context) {
|
|
721
|
-
initContextOptions(globalOptions, context);
|
|
722
|
-
if (defaultCommand) {
|
|
723
|
-
initContextOptions(defaultCommand._options, context);
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
});
|
|
727
|
-
const breadc2 = {
|
|
728
|
-
name,
|
|
729
|
-
description: config.description ?? "",
|
|
730
|
-
option(format, _config, _config2 = {}) {
|
|
731
|
-
const config2 = typeof _config === "string" ? { description: _config, ..._config2 } : _config;
|
|
732
|
-
const option = makeOption(format, config2);
|
|
733
|
-
globalOptions.push(option);
|
|
734
|
-
return breadc2;
|
|
735
|
-
},
|
|
736
|
-
command(text, _config = {}, _config2 = {}) {
|
|
737
|
-
const config2 = typeof _config === "string" ? { description: _config, ..._config2 } : _config;
|
|
738
|
-
const command = makeCommand(text, config2, root, container);
|
|
739
|
-
if (command._default) {
|
|
740
|
-
defaultCommand = command;
|
|
741
|
-
}
|
|
742
|
-
allCommands.push(command);
|
|
743
|
-
return command;
|
|
744
|
-
},
|
|
745
|
-
parse(args) {
|
|
746
|
-
return parse(root, args);
|
|
747
|
-
},
|
|
748
|
-
async run(args) {
|
|
749
|
-
const result = breadc2.parse(args);
|
|
750
|
-
const callback = result.callback;
|
|
751
|
-
if (callback) {
|
|
752
|
-
await container.preRun(breadc2);
|
|
753
|
-
const r = await callback(result);
|
|
754
|
-
await container.postRun(breadc2);
|
|
755
|
-
return r;
|
|
756
|
-
}
|
|
757
|
-
return void 0;
|
|
758
|
-
}
|
|
759
|
-
};
|
|
760
|
-
container.init(breadc2, allCommands, globalOptions);
|
|
761
|
-
return breadc2;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
export { BreadcError, ParseError, breadc, definePlugin, makeTreeNode };
|
|
3
|
+
export { BreadcAppError, BreadcError, ResolveCommandError, ResolveGroupError, ResolveOptionError, argument, breadc, command, group, option };
|