@breadc/core 1.0.0-beta.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/dist/index.mjs ADDED
@@ -0,0 +1,1045 @@
1
+ //#region src/utils/string.ts
2
+ function splitOnce(text, separator) {
3
+ const found = text.indexOf(separator);
4
+ if (found === -1) return [text, void 0];
5
+ return [text.slice(0, found), text.slice(found + separator.length)];
6
+ }
7
+ function camelCase(text) {
8
+ if (text.indexOf("-") === -1) return text;
9
+ return text.split("-").map((t, idx) => idx === 0 ? t : t[0].toUpperCase() + t.slice(1)).join("");
10
+ }
11
+
12
+ //#endregion
13
+ //#region src/error.ts
14
+ var BreadcError = class extends Error {};
15
+ var RuntimeError = class extends BreadcError {};
16
+ var BreadcAppError = class extends BreadcError {
17
+ static DUPLICATED_DEFAULT_COMMAND = `Find duplicated default commands`;
18
+ static DUPLICATED_COMMAND = `Find duplicated commands`;
19
+ static NO_ACTION_BOUND = `There is no action function bound in this command`;
20
+ cause;
21
+ constructor(message, cause) {
22
+ super(message);
23
+ this.cause = cause;
24
+ }
25
+ };
26
+ var ResolveGroupError = class extends BreadcError {
27
+ static EMPTY = "Group spec should not be empty";
28
+ static INVALID_ARG_IN_GROUP = "Resolving argument in group spec";
29
+ cause;
30
+ constructor(message, cause) {
31
+ super(`${message} at the command "${cause.spec}", position ${cause.position}`);
32
+ this.cause = cause;
33
+ }
34
+ };
35
+ var ResolveCommandError = class extends BreadcError {
36
+ static INVALID_ARG = "Resolving invalid argument";
37
+ static INVALID_EMPTY_ARG = "Resolving invalid empty argument";
38
+ static INVALID_REQUIRED_ARG = "Resolving invalid required argument";
39
+ static INVALID_OPTIONAL_ARG = "Resolving invalid optional argument";
40
+ static INVALID_SPREAD_ARG = "Resolving invalid spread argument";
41
+ static INVALID_ALIAS_FORMAT = "Alias command format should not have arguments";
42
+ static PIECE_BEFORE_REQUIRED = "Sub-command should be placed in the beginning";
43
+ static REQUIRED_BEFORE_OPTIONAL = "Required argument should be placed before optional arguments";
44
+ static OPTIONAL_BEFORE_SPREAD = "Optional argument should be placed before spread arguments";
45
+ static SPREAD_ONLY_ONCE = "Spread argument can only appear once";
46
+ cause;
47
+ constructor(message, cause) {
48
+ super(`${message} at the command "${cause.spec}", position ${cause.position}`);
49
+ this.cause = cause;
50
+ }
51
+ };
52
+ var ResolveOptionError = class extends BreadcError {
53
+ static INVALID_OPTION = "Resolving invalid option";
54
+ cause;
55
+ constructor(message, cause) {
56
+ super(`${message} at the option "${cause.spec}"`);
57
+ this.cause = cause;
58
+ }
59
+ };
60
+
61
+ //#endregion
62
+ //#region src/breadc/option.ts
63
+ function option(spec, description, init) {
64
+ return {
65
+ spec,
66
+ init: {
67
+ description,
68
+ ...init
69
+ },
70
+ type: void 0,
71
+ long: ""
72
+ };
73
+ }
74
+ function rawOption(type, long, short, init) {
75
+ return {
76
+ spec: "",
77
+ init,
78
+ type,
79
+ long,
80
+ short
81
+ };
82
+ }
83
+
84
+ //#endregion
85
+ //#region src/breadc/command.ts
86
+ function command(spec, init) {
87
+ const run = (() => {});
88
+ const aliases = [];
89
+ const args = [];
90
+ const options = [];
91
+ run.spec = spec;
92
+ run.init = init;
93
+ run._aliases = aliases;
94
+ run._options = options;
95
+ run._arguments = args;
96
+ run.alias = (spec) => {
97
+ aliases.push(spec);
98
+ return run;
99
+ };
100
+ run.argument = (spec, init) => {
101
+ const arg = typeof spec === "string" ? argument(spec, init) : spec;
102
+ args.push(arg);
103
+ return run;
104
+ };
105
+ run.option = (spec, description, init) => {
106
+ const option$5 = typeof spec === "string" ? option(spec, description, init) : spec;
107
+ options.push(option$5);
108
+ return run;
109
+ };
110
+ run.use = (middleware) => {
111
+ if (!run._actionMiddlewares) run._actionMiddlewares = [];
112
+ run._actionMiddlewares.push(middleware);
113
+ return run;
114
+ };
115
+ run.allowUnknownOption = (middleware) => {
116
+ if (!run._unknownOptionMiddlewares) run._unknownOptionMiddlewares = [];
117
+ if (typeof middleware === "function") run._unknownOptionMiddlewares.push(middleware);
118
+ else run._unknownOptionMiddlewares.push((_ctx, key, value) => ({
119
+ name: key,
120
+ value
121
+ }));
122
+ return run;
123
+ };
124
+ run.action = (fn) => {
125
+ run._actionFn = fn;
126
+ return run;
127
+ };
128
+ return run;
129
+ }
130
+ function rawArgument(type, name) {
131
+ return {
132
+ spec: "",
133
+ type,
134
+ name,
135
+ init: {}
136
+ };
137
+ }
138
+ function argument(spec, init) {
139
+ let type;
140
+ let name;
141
+ if ("<" === spec[0] && ">" === spec[spec.length - 1]) {
142
+ type = "required";
143
+ name = spec.slice(1, spec.length - 1);
144
+ } else if ("[" === spec[0] && "]" === spec[spec.length - 1]) if (spec[1] === "." && spec[2] === ".") {
145
+ type = "spread";
146
+ for (let i = 1; i < spec.length; i++) if (spec[i] !== ".") {
147
+ name = spec.slice(i, spec.length - 1);
148
+ break;
149
+ }
150
+ } else {
151
+ type = "optional";
152
+ name = spec.slice(1, spec.length - 1);
153
+ }
154
+ else throw new ResolveCommandError(ResolveCommandError.INVALID_ARG, {
155
+ spec,
156
+ position: -1
157
+ });
158
+ return {
159
+ spec,
160
+ type,
161
+ name,
162
+ init
163
+ };
164
+ }
165
+
166
+ //#endregion
167
+ //#region src/runtime/matched.ts
168
+ var MatchedArgument = class {
169
+ argument;
170
+ token;
171
+ dirty = false;
172
+ raw;
173
+ constructor(argument) {
174
+ this.argument = argument;
175
+ if (argument.init?.initial !== void 0) this.raw = argument.init.initial;
176
+ else switch (argument.type) {
177
+ case "required":
178
+ case "optional":
179
+ this.raw = void 0;
180
+ break;
181
+ case "spread":
182
+ this.raw = [];
183
+ break;
184
+ }
185
+ }
186
+ value() {
187
+ if (this.dirty || this.argument.init?.default === void 0) {
188
+ const cast = this.argument.init?.cast;
189
+ return cast ? cast(this.raw) : this.raw;
190
+ } else return this.argument.init.default;
191
+ }
192
+ accept(_context, value) {
193
+ switch (this.argument.type) {
194
+ case "optional":
195
+ if (this.dirty) throw new RuntimeError();
196
+ this.raw = value ?? this.argument.init?.initial ?? void 0;
197
+ this.dirty = true;
198
+ return this;
199
+ case "required":
200
+ if (this.dirty) throw new RuntimeError();
201
+ this.raw = value ?? this.argument.init?.initial ?? "";
202
+ this.dirty = true;
203
+ return this;
204
+ case "spread":
205
+ this.raw.push(value ?? this.argument.init?.initial ?? "");
206
+ this.dirty = true;
207
+ return this;
208
+ default:
209
+ // c8 ignore
210
+ return this;
211
+ }
212
+ }
213
+ };
214
+ const FALSE_OPTION = [
215
+ "false",
216
+ "f",
217
+ "no",
218
+ "n",
219
+ "off"
220
+ ];
221
+ var MatchedOption = class {
222
+ option;
223
+ dirty = false;
224
+ raw;
225
+ constructor(option) {
226
+ this.option = option;
227
+ if (option.init.initial !== void 0) this.raw = option.init.initial;
228
+ else switch (option.type) {
229
+ case "boolean":
230
+ case "optional":
231
+ this.raw = false;
232
+ break;
233
+ case "required":
234
+ this.raw = void 0;
235
+ break;
236
+ case "spread":
237
+ this.raw = [];
238
+ break;
239
+ }
240
+ }
241
+ value() {
242
+ if (this.dirty || this.option.init.default === void 0) {
243
+ const cast = this.option.init.cast;
244
+ return cast ? cast(this.raw) : this.raw;
245
+ } else return this.option.init.default;
246
+ }
247
+ accept(context, long, text) {
248
+ switch (this.option.type) {
249
+ case "boolean":
250
+ if (this.dirty) throw new RuntimeError();
251
+ if (text !== void 0) {
252
+ const value = text.toLowerCase();
253
+ if (!long.startsWith("no-")) this.raw = FALSE_OPTION.includes(value) ? false : true;
254
+ else this.raw = FALSE_OPTION.includes(value) ? true : false;
255
+ } else if (!long.startsWith("no-")) this.raw = true;
256
+ else this.raw = false;
257
+ this.dirty = true;
258
+ break;
259
+ case "optional": {
260
+ if (this.dirty) throw new RuntimeError();
261
+ let value = text;
262
+ if (value === void 0) {
263
+ const token = context.tokens.peek();
264
+ if (token && !token.isEscape && !token.isLong && (!token.isShort || token.isNegativeNumber)) {
265
+ value = token.toRaw();
266
+ context.tokens.next();
267
+ }
268
+ }
269
+ if (value !== void 0) this.raw = value;
270
+ else this.raw = this.option.init.initial ?? true;
271
+ this.dirty = true;
272
+ break;
273
+ }
274
+ case "required":
275
+ case "spread": {
276
+ let value = text;
277
+ if (value === void 0) {
278
+ const token = context.tokens.peek();
279
+ if (token && !token.isEscape) {
280
+ value = token.toRaw();
281
+ context.tokens.next();
282
+ }
283
+ }
284
+ if (this.option.type === "required") {
285
+ if (this.dirty) throw new Error("");
286
+ this.raw = value ?? this.option.init.initial;
287
+ } else this.raw.push(value ?? "");
288
+ this.dirty = true;
289
+ break;
290
+ }
291
+ }
292
+ return this;
293
+ }
294
+ };
295
+
296
+ //#endregion
297
+ //#region src/runtime/lexer.ts
298
+ var TokenStream = class {
299
+ args;
300
+ tokens = [];
301
+ cursor = 0;
302
+ constructor(args) {
303
+ this.args = args;
304
+ }
305
+ reset() {
306
+ this.cursor = 0;
307
+ return this;
308
+ }
309
+ /**
310
+ * Get the current arg token and advance the cursor
311
+ *
312
+ * @returns next arg
313
+ */
314
+ next() {
315
+ const arg = this.args[this.cursor];
316
+ if (arg === void 0) return void 0;
317
+ const token = this.tokens[this.cursor] ?? (this.tokens[this.cursor] = new Token(arg));
318
+ this.cursor++;
319
+ return token;
320
+ }
321
+ /**
322
+ * Move the cursor to preivous position
323
+ */
324
+ prev() {
325
+ this.cursor--;
326
+ }
327
+ /**
328
+ * Peek the next arg and does not advance the cursor
329
+ *
330
+ * @returns next arg
331
+ */
332
+ peek() {
333
+ const arg = this.args[this.cursor];
334
+ if (arg === void 0) return void 0;
335
+ const token = this.tokens[this.cursor];
336
+ if (token !== void 0) return token;
337
+ return this.tokens[this.cursor] = new Token(arg);
338
+ }
339
+ /**
340
+ * Return all remaining raw arguments, advancing the cursor to the end
341
+ *
342
+ * @returns all remaining raw arguments
343
+ */
344
+ remaining() {
345
+ const remaining = this.args.slice(this.cursor).map((t) => new Token(t));
346
+ this.cursor = this.args.length;
347
+ return remaining;
348
+ }
349
+ /**
350
+ * Return whether has consumed all the args
351
+ *
352
+ * @returns whether has consumed all the args
353
+ */
354
+ get isEnd() {
355
+ return this.args[this.cursor] === void 0;
356
+ }
357
+ *[Symbol.iterator]() {
358
+ for (; this.cursor < this.args.length; this.cursor++) yield new Token(this.args[this.cursor]);
359
+ }
360
+ };
361
+ var Token = class {
362
+ text;
363
+ constructor(arg) {
364
+ this.text = arg;
365
+ }
366
+ /**
367
+ * @returns raw arg string
368
+ */
369
+ toRaw() {
370
+ return this.text;
371
+ }
372
+ /**
373
+ * @returns whether arg is empty
374
+ */
375
+ get isEmpty() {
376
+ return this.text.length === 0;
377
+ }
378
+ /**
379
+ * @returns whether arg looks like a stdio argument (`-`)
380
+ */
381
+ get isStdio() {
382
+ return this.text === "-";
383
+ }
384
+ /**
385
+ * @returns whether arg looks like an argument escape (`--`)
386
+ */
387
+ get isEscape() {
388
+ return this.text === "--";
389
+ }
390
+ /**
391
+ * @returns whether arg looks like a negative number (`-123`)
392
+ */
393
+ get isNegativeNumber() {
394
+ return this.text.startsWith("-") && !Number.isNaN(Number.parseFloat(this.text));
395
+ }
396
+ /**
397
+ * @returns whether arg looks like a number (`123`, `-123`)
398
+ */
399
+ get isNumber() {
400
+ return !Number.isNaN(Number.parseFloat(this.text));
401
+ }
402
+ /**
403
+ * @returns whether arg can treat as a long-flag
404
+ */
405
+ get isLong() {
406
+ return this.text[0] === "-" && this.text[1] === "-" && this.text.length > 2;
407
+ }
408
+ /**
409
+ * Treat as a long-flag, un-checked version
410
+ *
411
+ * @returns a long flag and its argument
412
+ */
413
+ toLong() {
414
+ const result = splitOnce(this.text, "=");
415
+ return [result[0].slice(2), result[1]];
416
+ }
417
+ /**
418
+ * Treat as a long-flag, checked version
419
+ *
420
+ * @returns a long flag and its argument
421
+ */
422
+ checkToLong() {
423
+ if (this.text[0] !== "-") return void 0;
424
+ if (this.text[1] !== "-") return void 0;
425
+ if (this.text.length <= 2) return void 0;
426
+ const result = splitOnce(this.text, "=");
427
+ return [result[0].slice(2), result[1]];
428
+ }
429
+ /**
430
+ * @returns whether arg can treat as a long-flag
431
+ */
432
+ get isShort() {
433
+ return this.text[0] === "-" && this.text[1] !== "-" && this.text.length > 1;
434
+ }
435
+ /**
436
+ * Treat as a short-flag, un-checked version
437
+ *
438
+ * @returns a short flag and its argument
439
+ */
440
+ toShort() {
441
+ const result = splitOnce(this.text, "=");
442
+ return [result[0].slice(1), result[1]];
443
+ }
444
+ /**
445
+ * Treat as a short-flag, checked version
446
+ *
447
+ * @returns a short flag and its argument
448
+ */
449
+ checkToShort() {
450
+ if (this.text[0] !== "-") return void 0;
451
+ if (this.text.length <= 1) return void 0;
452
+ if (this.text[1] === "-") return void 0;
453
+ const result = splitOnce(this.text, "=");
454
+ return [result[0].slice(1), result[1]];
455
+ }
456
+ get length() {
457
+ return this.text.length;
458
+ }
459
+ toString() {
460
+ return this.text;
461
+ }
462
+ [Symbol.iterator]() {
463
+ return this.text[Symbol.iterator]();
464
+ }
465
+ };
466
+
467
+ //#endregion
468
+ //#region src/runtime/context.ts
469
+ function context(breadc, args) {
470
+ return {
471
+ data: {},
472
+ output: void 0,
473
+ group: void 0,
474
+ command: void 0,
475
+ pieces: [],
476
+ options: /* @__PURE__ */ new Map(),
477
+ arguments: [],
478
+ remaining: [],
479
+ breadc,
480
+ tokens: new TokenStream(args)
481
+ };
482
+ }
483
+ function reset(context) {
484
+ context.data = {};
485
+ context.group = void 0;
486
+ context.command = void 0;
487
+ context.pieces.length = 0;
488
+ context.options.clear();
489
+ context.arguments.length = 0;
490
+ context.remaining.length = 0;
491
+ context.tokens.reset();
492
+ return context;
493
+ }
494
+
495
+ //#endregion
496
+ //#region src/runtime/builder.ts
497
+ function isGroup(command) {
498
+ return !!command._commands;
499
+ }
500
+ function resolveGroup(group) {
501
+ if (group._pieces) return;
502
+ const { spec } = group;
503
+ const pieces = [];
504
+ for (let i = 0; i < spec.length;) if (spec[i] === "<" || spec[i] === "[") throw new ResolveGroupError(ResolveGroupError.INVALID_ARG_IN_GROUP, {
505
+ spec,
506
+ position: i
507
+ });
508
+ else if (spec[i] === " ") while (i < spec.length && spec[i] === " ") i++;
509
+ else {
510
+ let j = i;
511
+ while (j < spec.length && spec[j] !== " ") j++;
512
+ pieces.push(spec.slice(i, j));
513
+ i = j;
514
+ }
515
+ group._pieces = [pieces];
516
+ }
517
+ function resolveCommand(command) {
518
+ if (command._pieces) return;
519
+ const { spec, _aliases: aliases } = command;
520
+ let i = 0;
521
+ const parent = command._group?._pieces[0] ?? [];
522
+ const pieces = [...parent];
523
+ for (; i < spec.length;) if (spec[i] === "<" || spec[i] === "[") break;
524
+ else if (spec[i] === " ") while (i < spec.length && spec[i] === " ") i++;
525
+ else {
526
+ let j = i;
527
+ while (j < spec.length && spec[j] !== " ") j++;
528
+ pieces.push(spec.slice(i, j));
529
+ i = j;
530
+ }
531
+ /**
532
+ * States:
533
+ *
534
+ * 0 := aaa bbb (0 -> 0, 0 -> 1, 0 -> 2, 0 -> 3)
535
+ * 1 := aaa bbb <xxx> <yyy> (1 -> 1, 1 -> 2, 1 -> 3)
536
+ * 2 := aaa bbb <xxx> <yyy> [zzz] (2 -> 2, 2 -> 3)
537
+ * 3 := aaa bbb <xxx> <yyy> [zzz] [...www] (3 -> empty)
538
+ */
539
+ let state = 1;
540
+ const resolvedArguments = [];
541
+ let spread;
542
+ for (; i < spec.length;) if (spec[i] === "<") {
543
+ if (i + 1 >= spec.length || spec[i + 1] === " ") throw new ResolveCommandError(ResolveCommandError.INVALID_REQUIRED_ARG, {
544
+ spec,
545
+ position: i
546
+ });
547
+ else i++;
548
+ if (state >= 2) throw new ResolveCommandError(ResolveCommandError.REQUIRED_BEFORE_OPTIONAL, {
549
+ spec,
550
+ position: i
551
+ });
552
+ let piece = "";
553
+ while (i < spec.length && spec[i] !== ">") piece += spec[i++];
554
+ if (i === spec.length || spec[i] !== ">") throw new ResolveCommandError(ResolveCommandError.INVALID_REQUIRED_ARG, {
555
+ spec,
556
+ position: i
557
+ });
558
+ else i++;
559
+ if (i < spec.length && spec[i] !== " ") throw new ResolveCommandError(ResolveCommandError.INVALID_REQUIRED_ARG, {
560
+ spec,
561
+ position: i
562
+ });
563
+ if (piece === "") throw new ResolveCommandError(ResolveCommandError.INVALID_EMPTY_ARG, {
564
+ spec,
565
+ position: i
566
+ });
567
+ state = 1;
568
+ resolvedArguments.push(rawArgument("required", piece));
569
+ } else if (spec[i] === "[") {
570
+ if (i + 1 >= spec.length || spec[i + 1] === " ") throw new ResolveCommandError(ResolveCommandError.INVALID_OPTIONAL_ARG, {
571
+ spec,
572
+ position: i
573
+ });
574
+ else i++;
575
+ if (spec[i] === ".") {
576
+ if (state >= 3) throw new ResolveCommandError(ResolveCommandError.SPREAD_ONLY_ONCE, {
577
+ spec,
578
+ position: i
579
+ });
580
+ while (i < spec.length && spec[i] === ".") i++;
581
+ let piece = "";
582
+ while (i < spec.length && spec[i] !== "]") piece += spec[i++];
583
+ if (i === spec.length || spec[i] !== "]") throw new ResolveCommandError(ResolveCommandError.INVALID_SPREAD_ARG, {
584
+ spec,
585
+ position: i
586
+ });
587
+ else i++;
588
+ if (i < spec.length && spec[i] !== " ") throw new ResolveCommandError(ResolveCommandError.INVALID_SPREAD_ARG, {
589
+ spec,
590
+ position: i
591
+ });
592
+ if (piece === "") throw new ResolveCommandError(ResolveCommandError.INVALID_EMPTY_ARG, {
593
+ spec,
594
+ position: i
595
+ });
596
+ state = 3;
597
+ spread = rawArgument("spread", piece);
598
+ resolvedArguments.push(spread);
599
+ } else {
600
+ if (state >= 3) throw new ResolveCommandError(ResolveCommandError.OPTIONAL_BEFORE_SPREAD, {
601
+ spec,
602
+ position: i
603
+ });
604
+ let piece = "";
605
+ while (i < spec.length && spec[i] !== "]") piece += spec[i++];
606
+ if (i === spec.length || spec[i] !== "]") throw new ResolveCommandError(ResolveCommandError.INVALID_OPTIONAL_ARG, {
607
+ spec,
608
+ position: i
609
+ });
610
+ else i++;
611
+ if (i < spec.length && spec[i] !== " ") throw new ResolveCommandError(ResolveCommandError.INVALID_OPTIONAL_ARG, {
612
+ spec,
613
+ position: i
614
+ });
615
+ if (piece === "") throw new ResolveCommandError(ResolveCommandError.INVALID_EMPTY_ARG, {
616
+ spec,
617
+ position: i
618
+ });
619
+ state = 2;
620
+ resolvedArguments.push(rawArgument("optional", piece));
621
+ }
622
+ } else if (spec[i] === " ") while (i < spec.length && spec[i] === " ") i++;
623
+ else throw new ResolveCommandError(ResolveCommandError.PIECE_BEFORE_REQUIRED, {
624
+ spec,
625
+ position: i
626
+ });
627
+ if (pieces.length === 0) command._default = true;
628
+ for (const argument of command._arguments) switch (argument.type) {
629
+ case "required":
630
+ if (state === 1) resolvedArguments.push(argument);
631
+ else throw new ResolveCommandError(ResolveCommandError.REQUIRED_BEFORE_OPTIONAL, {
632
+ spec,
633
+ position: i
634
+ });
635
+ break;
636
+ case "optional":
637
+ if (state <= 2) {
638
+ state = 2;
639
+ resolvedArguments.push(argument);
640
+ } else throw new ResolveCommandError(ResolveCommandError.OPTIONAL_BEFORE_SPREAD, {
641
+ spec,
642
+ position: i
643
+ });
644
+ break;
645
+ case "spread":
646
+ if (spread) throw new ResolveCommandError(ResolveCommandError.SPREAD_ONLY_ONCE, {
647
+ spec,
648
+ position: i
649
+ });
650
+ state = 3;
651
+ spread = argument;
652
+ resolvedArguments.push(argument);
653
+ break;
654
+ }
655
+ command._arguments = resolvedArguments;
656
+ if (aliases.length > 0) {
657
+ const resolvedAliases = [pieces];
658
+ for (const spec of aliases) {
659
+ const aliasPieces = [...parent];
660
+ for (let i = 0; i < spec.length;) if (spec[i] === "<" || spec[i] === "[") throw new ResolveCommandError(ResolveCommandError.INVALID_ALIAS_FORMAT, {
661
+ spec,
662
+ position: i
663
+ });
664
+ else if (spec[i] === " ") while (i < spec.length && spec[i] === " ") i++;
665
+ else {
666
+ let j = i;
667
+ while (j < spec.length && spec[j] !== " ") j++;
668
+ aliasPieces.push(spec.slice(i, j));
669
+ i = j;
670
+ }
671
+ if (aliasPieces.length === 0) command._default = true;
672
+ resolvedAliases.push(aliasPieces);
673
+ }
674
+ command._pieces = resolvedAliases;
675
+ } else command._pieces = [pieces];
676
+ return command;
677
+ }
678
+ const OptionRE = /^(?:-([a-zA-Z]), )?--(no-)?([a-zA-Z0-9\-]+)(?: (<[a-zA-Z0-9\-]+>|\[\.*[a-zA-Z0-9\-]+\]))?$/;
679
+ function resolveOption(option) {
680
+ if (option.type) return option;
681
+ const { spec } = option;
682
+ const match = OptionRE.exec(spec);
683
+ if (match) {
684
+ option.long = match[3];
685
+ if (match[1]) option.short = match[1];
686
+ if (match[4]) {
687
+ const arg = match[4];
688
+ option.argument = arg;
689
+ if (arg[0] === "<") option.type = "required";
690
+ else if (arg[1] === ".") option.type = "spread";
691
+ else option.type = "optional";
692
+ if (match[2]) throw new ResolveOptionError(ResolveOptionError.INVALID_OPTION, { spec });
693
+ } else {
694
+ option.type = "boolean";
695
+ if (match[2] && option.init.negated === void 0 && option.init.initial === void 0 && option.init.default === void 0) {
696
+ option.init.negated = true;
697
+ option.init.initial = true;
698
+ }
699
+ }
700
+ return option;
701
+ } else throw new ResolveOptionError(ResolveOptionError.INVALID_OPTION, { spec });
702
+ }
703
+ function buildApp(instance) {
704
+ for (const option of instance._options) resolveOption(option);
705
+ for (const command of instance._commands) {
706
+ if (isGroup(command)) resolveGroup(command);
707
+ else resolveCommand(command);
708
+ if (command._default) for (const option of instance._options) resolveOption(option);
709
+ }
710
+ }
711
+ function buildCommand(command) {
712
+ for (const option of command._options) resolveOption(option);
713
+ }
714
+ function buildGroup(group) {
715
+ for (const option of group._options) resolveOption(option);
716
+ for (const command of group._commands) resolveCommand(command);
717
+ }
718
+
719
+ //#endregion
720
+ //#region src/runtime/parser.ts
721
+ function parse(app, argv) {
722
+ const context$1 = context(app, argv);
723
+ buildApp(context$1.breadc);
724
+ const defaultCommands = context$1.breadc._commands.filter((c) => c._default);
725
+ if (defaultCommands.length >= 2) throw new BreadcAppError(BreadcAppError.DUPLICATED_DEFAULT_COMMAND, { commands: defaultCommands });
726
+ const defaultCommand = defaultCommands[0];
727
+ doParse(context$1, defaultCommand !== void 0 && context$1.breadc._commands.length === 1 ? defaultCommand : void 0);
728
+ if (context$1.command) {} else if (defaultCommand) {
729
+ reset(context$1);
730
+ doParse(context$1, defaultCommand);
731
+ }
732
+ return context$1;
733
+ }
734
+ function resolveArgs(context) {
735
+ return context.arguments.map((arg) => arg.value());
736
+ }
737
+ function resolveOptions(context) {
738
+ return Object.fromEntries([...context.options.values()].map((opt) => [camelCase(opt.option.long), opt.value()]));
739
+ }
740
+ function doParse(context, defaultCommand) {
741
+ const { breadc, tokens, options: matchedOptions } = context;
742
+ let index = 0;
743
+ let matchedGroup = void 0;
744
+ let matchedCommand = defaultCommand;
745
+ const pendingCommands = /* @__PURE__ */ new Map();
746
+ const pendingLongOptions = /* @__PURE__ */ new Map();
747
+ const pendingNegateOptions = /* @__PURE__ */ new Map();
748
+ const pendingShortOptions = /* @__PURE__ */ new Map();
749
+ const args = [];
750
+ const unknown = [];
751
+ const addPendingOptions = (options) => {
752
+ for (const option of options) {
753
+ pendingLongOptions.set(option.long, option);
754
+ if (option.type === "boolean") pendingNegateOptions.set("no-" + option.long, option);
755
+ if (option.short) pendingShortOptions.set(option.short, option);
756
+ matchedOptions.set(option.long, new MatchedOption(option));
757
+ }
758
+ };
759
+ const addPendingCommand = (command, alias) => {
760
+ const piece = command._pieces[alias][index];
761
+ if (!pendingCommands.has(piece)) pendingCommands.set(piece, []);
762
+ pendingCommands.get(piece).push([command, alias]);
763
+ };
764
+ addPendingOptions(breadc._options);
765
+ if (breadc._init.builtin?.version !== false) {
766
+ const spec = typeof breadc._init.builtin?.version === "object" ? breadc._init.builtin.version.spec : void 0;
767
+ const option$3 = spec ? resolveOption(option(spec)) : rawOption("boolean", "version", "v", { description: "" });
768
+ breadc._version = option$3;
769
+ pendingLongOptions.set(option$3.long, option$3);
770
+ if (option$3.short) pendingShortOptions.set(option$3.short, option$3);
771
+ }
772
+ if (breadc._init.builtin?.help !== false) {
773
+ const spec = typeof breadc._init.builtin?.help === "object" ? breadc._init.builtin.help.spec : void 0;
774
+ const option$4 = spec ? resolveOption(option(spec)) : rawOption("boolean", "help", "h", { description: "" });
775
+ breadc._help = option$4;
776
+ pendingLongOptions.set(option$4.long, option$4);
777
+ if (option$4.short) pendingShortOptions.set(option$4.short, option$4);
778
+ }
779
+ if (defaultCommand) {
780
+ matchedCommand = defaultCommand;
781
+ for (const command of breadc._commands) if (command._default) {
782
+ buildCommand(command);
783
+ addPendingOptions(command._options);
784
+ }
785
+ } else for (const command of breadc._commands) if (!command._default) for (let alias = 0; alias < command._pieces.length; alias++) addPendingCommand(command, alias);
786
+ while (!tokens.isEnd) {
787
+ const token = tokens.next();
788
+ const rawToken = token.toRaw();
789
+ if (token.isEscape) context.remaining.push(...tokens.remaining().map((t) => t.toRaw()));
790
+ else if (!matchedCommand && pendingCommands.has(rawToken)) {
791
+ index += 1;
792
+ context.pieces.push(rawToken);
793
+ const nextCommands = pendingCommands.get(rawToken);
794
+ pendingCommands.clear();
795
+ for (const [command, alias] of nextCommands) {
796
+ const pieces = command._pieces[alias];
797
+ if (index === pieces.length) if (isGroup(command)) {
798
+ const group = command;
799
+ if (!matchedGroup || matchedGroup === group) matchedGroup = group;
800
+ else throw new RuntimeError();
801
+ buildGroup(group);
802
+ addPendingOptions(group._options);
803
+ for (const command of group._commands) for (let alias = 0; alias < command._pieces.length; alias++) addPendingCommand(command, alias);
804
+ } else {
805
+ if (!matchedCommand || matchedCommand === command) matchedCommand = command;
806
+ else throw new RuntimeError();
807
+ buildCommand(command);
808
+ addPendingOptions(command._options);
809
+ }
810
+ else addPendingCommand(command, alias);
811
+ }
812
+ } else if (token.isLong || token.isShort && !token.isNegativeNumber) {
813
+ const isLong = token.isLong;
814
+ const [key, value] = isLong ? token.toLong() : token.toShort();
815
+ const option = isLong ? pendingLongOptions.get(key) || pendingNegateOptions.get(key) : pendingShortOptions.get(key);
816
+ if (option) {
817
+ if (!matchedOptions.has(option.long) || matchedOptions.get(option.long)?.option !== option) matchedOptions.set(option.long, new MatchedOption(option));
818
+ matchedOptions.get(option.long).accept(context, key, value);
819
+ } else {
820
+ const unknownOptionMiddlewares = [
821
+ ...breadc._unknownOptionMiddlewares,
822
+ ...matchedGroup?._unknownOptionMiddlewares ?? [],
823
+ ...matchedCommand?._unknownOptionMiddlewares ?? []
824
+ ];
825
+ for (const middleware of unknownOptionMiddlewares) {
826
+ const result = middleware(context, key, value);
827
+ if (result) {
828
+ const matched = new MatchedOption(rawOption(result.type ?? "optional", key, void 0, {})).accept(context, key, value);
829
+ matchedOptions.set(key, matched);
830
+ break;
831
+ }
832
+ }
833
+ }
834
+ } else if (matchedCommand) args.push(rawToken);
835
+ else unknown.push(rawToken);
836
+ }
837
+ context.group = matchedGroup;
838
+ context.command = matchedCommand;
839
+ if (matchedCommand) {
840
+ let i = 0;
841
+ for (; i < matchedCommand._arguments.length; i++) {
842
+ const argument = matchedCommand._arguments[i];
843
+ const matchedArgument = new MatchedArgument(argument);
844
+ const value = args[i];
845
+ if (argument.type === "required") {
846
+ if (value === void 0) throw new RuntimeError();
847
+ matchedArgument.accept(context, value);
848
+ context.arguments.push(matchedArgument);
849
+ } else if (argument.type === "optional") {
850
+ matchedArgument.accept(context, value);
851
+ context.arguments.push(matchedArgument);
852
+ } else {
853
+ for (; i < args.length; i++) matchedArgument.accept(context, args[i]);
854
+ context.arguments.push(matchedArgument);
855
+ }
856
+ }
857
+ if (i < args.length) context.remaining.unshift(...args.slice(i));
858
+ } else context.arguments.push(...unknown.map((arg, idx) => new MatchedArgument(rawArgument("required", `arg_${idx}`)).accept(context, arg)));
859
+ return matchedCommand;
860
+ }
861
+
862
+ //#endregion
863
+ //#region src/breadc/builtin/help.ts
864
+ function printHelp(context) {
865
+ const { breadc } = context;
866
+ const text = `${breadc.name}/${breadc._init.version ?? "unknown"}`;
867
+ console.log(text);
868
+ return text;
869
+ }
870
+
871
+ //#endregion
872
+ //#region src/breadc/builtin/version.ts
873
+ function printVersion(context) {
874
+ const { breadc } = context;
875
+ const text = `${breadc.name}/${breadc._init.version ?? "unknown"}`;
876
+ console.log(text);
877
+ return text;
878
+ }
879
+
880
+ //#endregion
881
+ //#region src/runtime/run.ts
882
+ async function run(app, argv) {
883
+ const context = parse(app, argv);
884
+ if (!context.command) {
885
+ const { breadc } = context;
886
+ const version = breadc._version;
887
+ if (version && context.options.get(version.long)?.value()) return printVersion(context);
888
+ const help = breadc._help;
889
+ if (help && context.options.get(help.long)?.value()) return printHelp(context);
890
+ if (breadc._unknownCommandMiddlewares.length > 0) {
891
+ let res;
892
+ for (const middleware of breadc._unknownCommandMiddlewares) res = await middleware(context);
893
+ return res;
894
+ } else return printHelp(context);
895
+ }
896
+ const actionMiddlewares = [
897
+ ...context.breadc._actionMiddlewares,
898
+ ...context.group?._actionMiddlewares ?? [],
899
+ ...context.command?._actionMiddlewares ?? []
900
+ ];
901
+ const args = resolveArgs(context);
902
+ const options = resolveOptions(context);
903
+ options["--"] = context.remaining;
904
+ if (context.command._actionFn) {
905
+ const actionFn = context.command._actionFn;
906
+ if (actionMiddlewares.length === 0) return await actionFn(...args, options);
907
+ else {
908
+ const invoked = [];
909
+ const makeNextFn = (index) => {
910
+ return (async (nextContext) => {
911
+ if (nextContext?.data) context.data = {
912
+ ...context.data,
913
+ ...nextContext?.data
914
+ };
915
+ invoked[index] = true;
916
+ if (index === actionMiddlewares.length) context.output = await actionFn(...args, options);
917
+ else {
918
+ const next = makeNextFn(index + 1);
919
+ await actionMiddlewares[index](context, next);
920
+ if (!invoked[index + 1]) await next();
921
+ }
922
+ return context;
923
+ });
924
+ };
925
+ await makeNextFn(0)(void 0);
926
+ return context.output;
927
+ }
928
+ } else throw new RuntimeError("");
929
+ }
930
+
931
+ //#endregion
932
+ //#region src/breadc/group.ts
933
+ function group(spec, init) {
934
+ if (!spec) throw new ResolveGroupError(ResolveGroupError.EMPTY, {
935
+ spec,
936
+ position: 0
937
+ });
938
+ const commands = [];
939
+ const options = [];
940
+ const actionMiddlewares = [];
941
+ const unknownOptionMiddlewares = [];
942
+ const group = {
943
+ spec,
944
+ init,
945
+ _pieces: void 0,
946
+ _commands: commands,
947
+ _options: options,
948
+ _actionMiddlewares: actionMiddlewares,
949
+ _unknownOptionMiddlewares: unknownOptionMiddlewares,
950
+ option(spec, description, init) {
951
+ const option$2 = typeof spec === "string" ? option(spec, description, init) : spec;
952
+ options.push(option$2);
953
+ return group;
954
+ },
955
+ command(spec, init) {
956
+ const command$2 = typeof spec === "string" ? command(spec, init) : spec;
957
+ command$2._group = group;
958
+ commands.push(command$2);
959
+ return command$2;
960
+ },
961
+ use(middleware) {
962
+ actionMiddlewares.push(middleware);
963
+ return group;
964
+ },
965
+ allowUnknownOption(middleware) {
966
+ if (typeof middleware === "function") unknownOptionMiddlewares.push(middleware);
967
+ else unknownOptionMiddlewares.push((_ctx, key, value) => ({
968
+ name: key,
969
+ value
970
+ }));
971
+ return group;
972
+ }
973
+ };
974
+ return group;
975
+ }
976
+
977
+ //#endregion
978
+ //#region src/breadc/app.ts
979
+ function breadc(name, init = {}) {
980
+ const commands = [];
981
+ const options = [];
982
+ const actionMiddlewares = [];
983
+ const unknownCommandMiddlewares = [];
984
+ const unknownOptionMiddlewares = [];
985
+ const app = {
986
+ name,
987
+ version: init.version,
988
+ _init: init,
989
+ _commands: commands,
990
+ _options: options,
991
+ _actionMiddlewares: actionMiddlewares,
992
+ _unknownOptionMiddlewares: unknownOptionMiddlewares,
993
+ _unknownCommandMiddlewares: unknownCommandMiddlewares,
994
+ option(spec, description, init) {
995
+ const option$1 = typeof spec === "string" ? option(spec, description, init) : spec;
996
+ options.push(option$1);
997
+ return app;
998
+ },
999
+ group(spec, init) {
1000
+ const group$1 = typeof spec === "string" ? group(spec, init) : spec;
1001
+ commands.push(group$1);
1002
+ return group$1;
1003
+ },
1004
+ command(spec, description, init) {
1005
+ const command$1 = typeof spec === "string" ? command(spec, description || init ? {
1006
+ description,
1007
+ ...init
1008
+ } : void 0) : spec;
1009
+ commands.push(command$1);
1010
+ return command$1;
1011
+ },
1012
+ use(middleware) {
1013
+ actionMiddlewares.push(middleware);
1014
+ return app;
1015
+ },
1016
+ onUnknownCommand(middleware) {
1017
+ unknownCommandMiddlewares.push(middleware);
1018
+ return app;
1019
+ },
1020
+ allowUnknownOption(middleware) {
1021
+ if (typeof middleware === "function") unknownOptionMiddlewares.push(middleware);
1022
+ else unknownOptionMiddlewares.push((_ctx, key, value) => ({
1023
+ name: key,
1024
+ value
1025
+ }));
1026
+ return app;
1027
+ },
1028
+ parse(argv) {
1029
+ const context = parse(app, argv);
1030
+ return {
1031
+ args: resolveArgs(context),
1032
+ options: resolveOptions(context),
1033
+ "--": context.remaining,
1034
+ context
1035
+ };
1036
+ },
1037
+ async run(argv) {
1038
+ return run(app, argv);
1039
+ }
1040
+ };
1041
+ return app;
1042
+ }
1043
+
1044
+ //#endregion
1045
+ export { BreadcAppError, BreadcError, MatchedArgument, MatchedOption, ResolveCommandError, ResolveGroupError, ResolveOptionError, argument, breadc, command, group, option };