gunshi 0.35.0 → 0.35.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/lib/bone.d.ts +2 -2
- package/lib/bone.js +1 -1
- package/lib/{cli-DoX4INTi.js → cli-DnXk5aCq.js} +8 -3
- package/lib/combinators.d.ts +2 -10
- package/lib/combinators.js +80 -7
- package/lib/context-CAPhwgJJ.js +119 -0
- package/lib/context.d.ts +2 -2
- package/lib/context.js +1 -90
- package/lib/core-CbE1I4Jl.js +518 -0
- package/lib/definition.d.ts +2 -2
- package/lib/definition.js +1 -1
- package/lib/error-6srXBX7u.d.ts +87 -0
- package/lib/error-xSnOOdnr.js +65 -0
- package/lib/generator.d.ts +1 -1
- package/lib/generator.js +2 -2
- package/lib/index.d.ts +4 -4
- package/lib/index.js +7 -7
- package/lib/{core-CFEvRYOJ.js → plugin-DhJZVIbl.js} +7 -0
- package/lib/plugin.d.ts +4 -4
- package/lib/plugin.js +5 -10
- package/lib/renderer.d.ts +3 -3
- package/lib/renderer.js +1 -1
- package/lib/{resolver-mxPNDBm_.d.ts → resolver-CgkhQ6Tt.d.ts} +62 -4
- package/lib/{core-xgWIcbHL.js → resolver-UFOtObbe.js} +158 -456
- package/lib/{src-B4uHG_qb.js → src-DbfZvEe0.js} +74 -9
- package/lib/{types-DZ0S_FSe.d.ts → types-CD5eRJFK.d.ts} +18 -1
- package/lib/utils-BKhsrjPW.js +32 -0
- package/lib/utils.d.ts +8 -3
- package/lib/utils.js +109 -1
- package/package.json +7 -7
- package/lib/constants-BkgP86Yw.d.ts +0 -11
- package/lib/constants-CQq1R_68.js +0 -29
- package/lib/utils-DNVHO9MQ.js +0 -131
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
|
|
3
|
-
import { createCommandContext } from "./context.js";
|
|
4
|
-
//#region ../../node_modules/.pnpm/args-tokens@0.27.0/node_modules/args-tokens/lib/parser.js
|
|
1
|
+
import { n as kebabnize, t as formatChoices } from "./utils-BKhsrjPW.js";
|
|
2
|
+
//#region ../../node_modules/.pnpm/args-tokens@0.28.0/node_modules/args-tokens/lib/parser.js
|
|
5
3
|
/**
|
|
6
4
|
* forked from `nodejs/node` (`pkgjs/parseargs`)
|
|
7
5
|
* repository url: https://github.com/nodejs/node (https://github.com/pkgjs/parseargs)
|
|
@@ -206,7 +204,7 @@ function hasOptionValue(value) {
|
|
|
206
204
|
return !(value == null) && value.codePointAt(0) !== HYPHEN_CODE;
|
|
207
205
|
}
|
|
208
206
|
//#endregion
|
|
209
|
-
//#region ../../node_modules/.pnpm/args-tokens@0.
|
|
207
|
+
//#region ../../node_modules/.pnpm/args-tokens@0.28.0/node_modules/args-tokens/lib/resolver.js
|
|
210
208
|
/**
|
|
211
209
|
* Entry point of argument options resolver.
|
|
212
210
|
*
|
|
@@ -216,6 +214,60 @@ function hasOptionValue(value) {
|
|
|
216
214
|
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
217
215
|
* @license MIT
|
|
218
216
|
*/
|
|
217
|
+
/**
|
|
218
|
+
* Machine-readable error codes for {@link ArgsValidationError}.
|
|
219
|
+
*
|
|
220
|
+
* Each code identifies a validation failure category and is also suitable as an
|
|
221
|
+
* i18n resource key for localized rendering.
|
|
222
|
+
*/
|
|
223
|
+
const ArgsValidationErrorKeys = {
|
|
224
|
+
requiredOption: "err:arg:required-option",
|
|
225
|
+
requiredPositional: "err:arg:required-positional",
|
|
226
|
+
invalidType: "err:arg:invalid-type",
|
|
227
|
+
invalidChoice: "err:arg:invalid-choice",
|
|
228
|
+
customParse: "err:arg:custom-parse",
|
|
229
|
+
unknownOption: "err:arg:unknown-option"
|
|
230
|
+
};
|
|
231
|
+
/**
|
|
232
|
+
* An error that contains structured metadata for argument validation failures.
|
|
233
|
+
*
|
|
234
|
+
* The `message` remains the English fallback message. Renderers can use `code`
|
|
235
|
+
* and `values` to localize the error, falling back to `message` when localization
|
|
236
|
+
* is unavailable.
|
|
237
|
+
*/
|
|
238
|
+
var ArgsValidationError = class extends Error {
|
|
239
|
+
/**
|
|
240
|
+
* Machine-readable error code for this validation failure.
|
|
241
|
+
*
|
|
242
|
+
* This code can also be used as an i18n resource key.
|
|
243
|
+
*/
|
|
244
|
+
code;
|
|
245
|
+
/**
|
|
246
|
+
* Interpolation values for `code`.
|
|
247
|
+
*/
|
|
248
|
+
values;
|
|
249
|
+
/**
|
|
250
|
+
* Create an `ArgsValidationError` instance.
|
|
251
|
+
*
|
|
252
|
+
* @param message - fallback error message
|
|
253
|
+
* @param options - structured validation metadata
|
|
254
|
+
*/
|
|
255
|
+
constructor(message, options = {}) {
|
|
256
|
+
super(message, { cause: options.cause });
|
|
257
|
+
this.name = "ArgsValidationError";
|
|
258
|
+
this.code = options.code;
|
|
259
|
+
this.values = options.values ?? {};
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
/**
|
|
263
|
+
* Check whether the given value is an {@link ArgsValidationError}.
|
|
264
|
+
*
|
|
265
|
+
* @param error - value to check
|
|
266
|
+
* @returns `true` when the value is an `ArgsValidationError`
|
|
267
|
+
*/
|
|
268
|
+
function isArgsValidationError(error) {
|
|
269
|
+
return error instanceof ArgsValidationError;
|
|
270
|
+
}
|
|
219
271
|
const SKIP_POSITIONAL_DEFAULT = -1;
|
|
220
272
|
/**
|
|
221
273
|
* Resolve command line arguments.
|
|
@@ -379,11 +431,9 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
|
|
|
379
431
|
const parsed = [];
|
|
380
432
|
for (let i = positionalsCount; i < endPositionals; i++) {
|
|
381
433
|
const p = positionalTokens[i];
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
errors.push(error);
|
|
386
|
-
}
|
|
434
|
+
const [parsedValue, error] = parseSchemaValue(p.value, rawArg, arg, schema);
|
|
435
|
+
if (error) errors.push(error);
|
|
436
|
+
else parsed.push(parsedValue);
|
|
387
437
|
}
|
|
388
438
|
values[rawArg] = parsed;
|
|
389
439
|
} else {
|
|
@@ -393,22 +443,22 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
|
|
|
393
443
|
}
|
|
394
444
|
positionalsCount = endPositionals;
|
|
395
445
|
explicit[rawArg] = true;
|
|
396
|
-
} else if (schema.required) errors.push(createRequireError(arg, schema));
|
|
397
|
-
} else if (schema.required) errors.push(createRequireError(arg, schema));
|
|
446
|
+
} else if (schema.required) errors.push(createRequireError(rawArg, arg, schema));
|
|
447
|
+
} else if (schema.required) errors.push(createRequireError(rawArg, arg, schema));
|
|
398
448
|
} else {
|
|
399
449
|
const positional = positionalTokens[positionalsCount];
|
|
400
450
|
if (shouldRequireMissingSinglePositional(schema)) {
|
|
401
451
|
if (positional != null) {
|
|
402
|
-
resolveSinglePositionalValue(values, errors, rawArg, schema, positional);
|
|
452
|
+
resolveSinglePositionalValue(values, errors, rawArg, arg, schema, positional);
|
|
403
453
|
explicit[rawArg] = true;
|
|
404
454
|
positionalsCount++;
|
|
405
|
-
} else errors.push(createRequireError(arg, schema));
|
|
455
|
+
} else errors.push(createRequireError(rawArg, arg, schema));
|
|
406
456
|
continue;
|
|
407
457
|
}
|
|
408
458
|
if (positional != null) {
|
|
409
459
|
const requiredPositionals = getRequiredPositionalsAfter(rawArg);
|
|
410
460
|
if (Math.max(positionalTokens.length - positionalsCount, 0) > requiredPositionals) {
|
|
411
|
-
resolveSinglePositionalValue(values, errors, rawArg, schema, positional);
|
|
461
|
+
resolveSinglePositionalValue(values, errors, rawArg, arg, schema, positional);
|
|
412
462
|
explicit[rawArg] = true;
|
|
413
463
|
positionalsCount++;
|
|
414
464
|
continue;
|
|
@@ -422,14 +472,14 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
|
|
|
422
472
|
if (!optionTokens.find((token) => {
|
|
423
473
|
return schema.short && token.name === schema.short || token.rawName && hasLongOptionPrefix(token.rawName) && token.name === arg;
|
|
424
474
|
})) {
|
|
425
|
-
errors.push(createRequireError(arg, schema));
|
|
475
|
+
errors.push(createRequireError(rawArg, arg, schema));
|
|
426
476
|
continue;
|
|
427
477
|
}
|
|
428
478
|
}
|
|
429
479
|
for (let i = 0; i < optionTokens.length; i++) {
|
|
430
480
|
const token = optionTokens[i];
|
|
431
481
|
if (checkTokenName(arg, schema, token) && token.rawName != void 0 && hasLongOptionPrefix(token.rawName) || schema.short === token.name && token.rawName != void 0 && isShortOption(token.rawName)) {
|
|
432
|
-
const invalid = validateRequire(token, arg, schema);
|
|
482
|
+
const invalid = validateRequire(token, rawArg, arg, schema);
|
|
433
483
|
if (invalid) {
|
|
434
484
|
errors.push(invalid);
|
|
435
485
|
continue;
|
|
@@ -437,7 +487,7 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
|
|
|
437
487
|
explicit[rawArg] = true;
|
|
438
488
|
const actualInputName = isShortOption(token.rawName) ? `-${token.name}` : `--${arg}`;
|
|
439
489
|
actualInputNames.set(rawArg, actualInputName);
|
|
440
|
-
const [parsedValue, error] = parse(token, arg, schema);
|
|
490
|
+
const [parsedValue, error] = parse(token, rawArg, arg, schema);
|
|
441
491
|
if (error) errors.push(error);
|
|
442
492
|
else if (schema.multiple) {
|
|
443
493
|
values[rawArg] ||= [];
|
|
@@ -457,39 +507,49 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
|
|
|
457
507
|
explicit
|
|
458
508
|
};
|
|
459
509
|
}
|
|
460
|
-
function parse(token, option, schema) {
|
|
461
|
-
if (typeof schema.parse === "function")
|
|
510
|
+
function parse(token, rawArg, option, schema) {
|
|
511
|
+
if (typeof schema.parse === "function") {
|
|
462
512
|
if (schema.type === "boolean") {
|
|
463
513
|
const boolValue = !(schema.negatable && token.name.startsWith("no-"));
|
|
464
|
-
return
|
|
514
|
+
return parseSchemaValue(String(boolValue), rawArg, option, schema);
|
|
465
515
|
}
|
|
466
|
-
return
|
|
467
|
-
} catch (error) {
|
|
468
|
-
return [void 0, error];
|
|
516
|
+
return parseSchemaValue(token.value ?? String(schema.default ?? ""), rawArg, option, schema);
|
|
469
517
|
}
|
|
470
518
|
switch (schema.type) {
|
|
471
|
-
case "string": return typeof token.value === "string" ? [token.value || schema.default, void 0] : [void 0, createTypeError(option, schema)];
|
|
519
|
+
case "string": return typeof token.value === "string" ? [token.value || schema.default, void 0] : [void 0, createTypeError(rawArg, option, schema, token.value)];
|
|
472
520
|
case "boolean": return [!(schema.negatable && token.name.startsWith("no-")), void 0];
|
|
473
521
|
case "number":
|
|
474
|
-
if (!isNumeric(token.value)) return [void 0, createTypeError(option, schema)];
|
|
522
|
+
if (!isNumeric(token.value)) return [void 0, createTypeError(rawArg, option, schema, token.value)];
|
|
475
523
|
return token.value ? [+token.value, void 0] : [+(schema.default || ""), void 0];
|
|
476
524
|
case "enum":
|
|
477
|
-
if (schema.choices && !schema.choices.includes(token.value)) return [void 0,
|
|
525
|
+
if (schema.choices && !schema.choices.includes(token.value)) return [void 0, createChoiceError(rawArg, option, schema, token.value)];
|
|
478
526
|
return [token.value || schema.default, void 0];
|
|
479
527
|
case "custom": throw new TypeError(`argument '${option}' should have a 'parse' function`);
|
|
480
528
|
default: throw new Error(`Unsupported argument type '${schema.type}' for option '${option}'`);
|
|
481
529
|
}
|
|
482
530
|
}
|
|
483
|
-
function
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
function resolveSinglePositionalValue(values, errors, rawArg, schema, positional) {
|
|
487
|
-
if (typeof schema.parse === "function") try {
|
|
488
|
-
values[rawArg] = schema.parse(positional.value);
|
|
531
|
+
function parseSchemaValue(value, rawArg, option, schema) {
|
|
532
|
+
try {
|
|
533
|
+
return [schema.parse(value), void 0];
|
|
489
534
|
} catch (error) {
|
|
490
|
-
|
|
535
|
+
return [void 0, createCustomParseError(error, rawArg, option, schema, value)];
|
|
491
536
|
}
|
|
492
|
-
|
|
537
|
+
}
|
|
538
|
+
function createRequireError(rawArg, option, schema) {
|
|
539
|
+
return new ArgResolveError(schema.type === "positional" ? `Positional argument '${option}' is required` : `Optional argument ${createOptionDisplayName(option, schema)} is required`, option, "required", schema, {
|
|
540
|
+
code: schema.type === "positional" ? ArgsValidationErrorKeys.requiredPositional : ArgsValidationErrorKeys.requiredOption,
|
|
541
|
+
values: schema.type === "positional" ? { name: rawArg } : {
|
|
542
|
+
displayName: createOptionDisplayName(option, schema),
|
|
543
|
+
name: rawArg
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
function resolveSinglePositionalValue(values, errors, rawArg, option, schema, positional) {
|
|
548
|
+
if (typeof schema.parse === "function") {
|
|
549
|
+
const [parsedValue, error] = parseSchemaValue(positional.value, rawArg, option, schema);
|
|
550
|
+
if (error) errors.push(error);
|
|
551
|
+
else values[rawArg] = parsedValue;
|
|
552
|
+
} else values[rawArg] = positional.value;
|
|
493
553
|
}
|
|
494
554
|
function hasDefault(schema) {
|
|
495
555
|
return schema.default != null;
|
|
@@ -519,7 +579,7 @@ function createRequiredPositionalsAfter(argEntries) {
|
|
|
519
579
|
* An error that occurs when resolving arguments.
|
|
520
580
|
* This error is thrown when the argument is not valid.
|
|
521
581
|
*/
|
|
522
|
-
var ArgResolveError = class extends
|
|
582
|
+
var ArgResolveError = class extends ArgsValidationError {
|
|
523
583
|
name;
|
|
524
584
|
schema;
|
|
525
585
|
type;
|
|
@@ -528,24 +588,78 @@ var ArgResolveError = class extends Error {
|
|
|
528
588
|
*
|
|
529
589
|
* @param message - the error message
|
|
530
590
|
* @param name - the name of the argument
|
|
531
|
-
* @param type - the type of the error
|
|
591
|
+
* @param type - the type of the error: 'type', 'required', or 'conflict'
|
|
532
592
|
* @param schema - the argument schema that caused the error
|
|
593
|
+
* @param options - structured validation metadata
|
|
533
594
|
*/
|
|
534
|
-
constructor(message, name, type, schema) {
|
|
535
|
-
super(message);
|
|
595
|
+
constructor(message, name, type, schema, options = {}) {
|
|
596
|
+
super(message, options);
|
|
536
597
|
this.name = name;
|
|
537
598
|
this.type = type;
|
|
538
599
|
this.schema = schema;
|
|
539
600
|
}
|
|
540
601
|
};
|
|
541
|
-
function validateRequire(token, option, schema) {
|
|
542
|
-
if (schema.required && schema.type !== "boolean" && !token.value) return createRequireError(option, schema);
|
|
602
|
+
function validateRequire(token, rawArg, option, schema) {
|
|
603
|
+
if (schema.required && schema.type !== "boolean" && !token.value) return createRequireError(rawArg, option, schema);
|
|
543
604
|
}
|
|
544
605
|
function isNumeric(str) {
|
|
545
606
|
return str.trim() !== "" && !isNaN(str);
|
|
546
607
|
}
|
|
547
|
-
function createTypeError(option, schema) {
|
|
548
|
-
return new ArgResolveError(`Optional argument
|
|
608
|
+
function createTypeError(rawArg, option, schema, actual) {
|
|
609
|
+
return new ArgResolveError(`Optional argument ${createOptionDisplayName(option, schema)} should be '${schema.type}'`, option, "type", schema, {
|
|
610
|
+
code: ArgsValidationErrorKeys.invalidType,
|
|
611
|
+
values: {
|
|
612
|
+
displayName: createOptionDisplayName(option, schema),
|
|
613
|
+
name: rawArg,
|
|
614
|
+
expected: schema.type,
|
|
615
|
+
...actual != null ? { actual } : {}
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
function createChoiceError(rawArg, option, schema, actual) {
|
|
620
|
+
const choices = schema.choices ?? [];
|
|
621
|
+
return new ArgResolveError(`Optional argument ${createOptionDisplayName(option, schema)} should be chosen from '${schema.type}' [${formatChoices(choices)}] values`, option, "type", schema, {
|
|
622
|
+
code: ArgsValidationErrorKeys.invalidChoice,
|
|
623
|
+
values: {
|
|
624
|
+
displayName: createOptionDisplayName(option, schema),
|
|
625
|
+
name: rawArg,
|
|
626
|
+
expected: schema.type,
|
|
627
|
+
choices: formatChoices(choices),
|
|
628
|
+
choiceValues: [...choices],
|
|
629
|
+
...actual != null ? { actual } : {}
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
function createCustomParseError(error, rawArg, option, schema, value) {
|
|
634
|
+
if (isArgsValidationError(error)) {
|
|
635
|
+
augmentValidationError(error, rawArg, option, schema, value);
|
|
636
|
+
return error;
|
|
637
|
+
}
|
|
638
|
+
const reason = getErrorReason(error);
|
|
639
|
+
return new ArgsValidationError(reason, {
|
|
640
|
+
code: ArgsValidationErrorKeys.customParse,
|
|
641
|
+
values: {
|
|
642
|
+
displayName: createArgumentDisplayName(option, schema),
|
|
643
|
+
name: rawArg,
|
|
644
|
+
reason
|
|
645
|
+
},
|
|
646
|
+
cause: error
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
function augmentValidationError(error, rawArg, option, schema, value) {
|
|
650
|
+
const values = error.values;
|
|
651
|
+
values.name ??= rawArg;
|
|
652
|
+
values.displayName ??= createArgumentDisplayName(option, schema);
|
|
653
|
+
if ((error.code === ArgsValidationErrorKeys.invalidType || error.code === ArgsValidationErrorKeys.invalidChoice) && values.actual == null) values.actual = value;
|
|
654
|
+
}
|
|
655
|
+
function getErrorReason(error) {
|
|
656
|
+
return error instanceof Error ? error.message : String(error);
|
|
657
|
+
}
|
|
658
|
+
function createArgumentDisplayName(option, schema) {
|
|
659
|
+
return schema.type === "positional" ? `'${option}'` : createOptionDisplayName(option, schema);
|
|
660
|
+
}
|
|
661
|
+
function createOptionDisplayName(option, schema) {
|
|
662
|
+
return `'--${option}'${schema.short ? ` or '-${schema.short}'` : ""}`;
|
|
549
663
|
}
|
|
550
664
|
function checkConflicts(args, explicit, toKebab, actualInputNames) {
|
|
551
665
|
for (const rawArg in args) {
|
|
@@ -564,416 +678,4 @@ function checkConflicts(args, explicit, toKebab, actualInputNames) {
|
|
|
564
678
|
return [];
|
|
565
679
|
}
|
|
566
680
|
//#endregion
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
570
|
-
* @license MIT
|
|
571
|
-
*/
|
|
572
|
-
/**
|
|
573
|
-
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
574
|
-
* @license MIT
|
|
575
|
-
*/
|
|
576
|
-
//#endregion
|
|
577
|
-
//#region src/decorators.ts
|
|
578
|
-
/**
|
|
579
|
-
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
580
|
-
* @license MIT
|
|
581
|
-
*/
|
|
582
|
-
const EMPTY_RENDERER = () => Promise.resolve("");
|
|
583
|
-
/**
|
|
584
|
-
* Factory function for creating a decorators manager.
|
|
585
|
-
*
|
|
586
|
-
* @returns A new decorators manager instance
|
|
587
|
-
*/
|
|
588
|
-
function createDecorators() {
|
|
589
|
-
/**
|
|
590
|
-
* private states
|
|
591
|
-
*/
|
|
592
|
-
const headerDecorators = [];
|
|
593
|
-
const usageDecorators = [];
|
|
594
|
-
const validationDecorators = [];
|
|
595
|
-
const commandDecorators = [];
|
|
596
|
-
/**
|
|
597
|
-
* helper function for building renderers
|
|
598
|
-
*/
|
|
599
|
-
function buildRenderer(decorators, defaultRenderer) {
|
|
600
|
-
if (decorators.length === 0) return defaultRenderer;
|
|
601
|
-
let renderer = defaultRenderer;
|
|
602
|
-
for (const decorator of decorators) {
|
|
603
|
-
const previousRenderer = renderer;
|
|
604
|
-
renderer = (ctx) => decorator(previousRenderer, ctx);
|
|
605
|
-
}
|
|
606
|
-
return renderer;
|
|
607
|
-
}
|
|
608
|
-
/**
|
|
609
|
-
* public interfaces
|
|
610
|
-
*/
|
|
611
|
-
return Object.freeze({
|
|
612
|
-
addHeaderDecorator(decorator) {
|
|
613
|
-
headerDecorators.push(decorator);
|
|
614
|
-
},
|
|
615
|
-
addUsageDecorator(decorator) {
|
|
616
|
-
usageDecorators.push(decorator);
|
|
617
|
-
},
|
|
618
|
-
addValidationErrorsDecorator(decorator) {
|
|
619
|
-
validationDecorators.push(decorator);
|
|
620
|
-
},
|
|
621
|
-
addCommandDecorator(decorator) {
|
|
622
|
-
commandDecorators.push(decorator);
|
|
623
|
-
},
|
|
624
|
-
get commandDecorators() {
|
|
625
|
-
return [...commandDecorators];
|
|
626
|
-
},
|
|
627
|
-
getHeaderRenderer() {
|
|
628
|
-
return buildRenderer(headerDecorators, EMPTY_RENDERER);
|
|
629
|
-
},
|
|
630
|
-
getUsageRenderer() {
|
|
631
|
-
return buildRenderer(usageDecorators, EMPTY_RENDERER);
|
|
632
|
-
},
|
|
633
|
-
getValidationErrorsRenderer() {
|
|
634
|
-
if (validationDecorators.length === 0) return EMPTY_RENDERER;
|
|
635
|
-
let renderer = EMPTY_RENDERER;
|
|
636
|
-
for (const decorator of validationDecorators) {
|
|
637
|
-
const previousRenderer = renderer;
|
|
638
|
-
renderer = (ctx, error) => decorator(previousRenderer, ctx, error);
|
|
639
|
-
}
|
|
640
|
-
return renderer;
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
//#endregion
|
|
645
|
-
//#region src/plugin/context.ts
|
|
646
|
-
/**
|
|
647
|
-
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
648
|
-
* @license MIT
|
|
649
|
-
*/
|
|
650
|
-
/**
|
|
651
|
-
* Factory function for creating a plugin context.
|
|
652
|
-
*
|
|
653
|
-
* @typeParam G - A type extending {@linkcode GunshiParams} to specify the shape of command parameters.
|
|
654
|
-
*
|
|
655
|
-
* @param decorators - A {@linkcode Decorators} instance.
|
|
656
|
-
* @param initialSubCommands - Initial sub commands map.
|
|
657
|
-
* @returns A new {@linkcode PluginContext} instance.
|
|
658
|
-
*/
|
|
659
|
-
function createPluginContext(decorators, initialSubCommands) {
|
|
660
|
-
/**
|
|
661
|
-
* private states
|
|
662
|
-
*/
|
|
663
|
-
const globalOptions = /* @__PURE__ */ new Map();
|
|
664
|
-
const subCommands = new Map(initialSubCommands || []);
|
|
665
|
-
/**
|
|
666
|
-
* public interfaces
|
|
667
|
-
*/
|
|
668
|
-
return Object.freeze({
|
|
669
|
-
get globalOptions() {
|
|
670
|
-
return new Map(globalOptions);
|
|
671
|
-
},
|
|
672
|
-
addGlobalOption(name, schema) {
|
|
673
|
-
if (!name) throw new Error("Option name must be a non-empty string");
|
|
674
|
-
if (globalOptions.has(name)) throw new Error(`Global option '${name}' is already registered`);
|
|
675
|
-
globalOptions.set(name, schema);
|
|
676
|
-
},
|
|
677
|
-
get subCommands() {
|
|
678
|
-
return new Map(subCommands);
|
|
679
|
-
},
|
|
680
|
-
addCommand(name, command) {
|
|
681
|
-
if (!name) throw new Error("Command name must be a non-empty string");
|
|
682
|
-
if (subCommands.has(name)) throw new Error(`Command '${name}' is already registered`);
|
|
683
|
-
subCommands.set(name, command);
|
|
684
|
-
},
|
|
685
|
-
hasCommand(name) {
|
|
686
|
-
return subCommands.has(name);
|
|
687
|
-
},
|
|
688
|
-
decorateHeaderRenderer(decorator) {
|
|
689
|
-
decorators.addHeaderDecorator(decorator);
|
|
690
|
-
},
|
|
691
|
-
decorateUsageRenderer(decorator) {
|
|
692
|
-
decorators.addUsageDecorator(decorator);
|
|
693
|
-
},
|
|
694
|
-
decorateValidationErrorsRenderer(decorator) {
|
|
695
|
-
decorators.addValidationErrorsDecorator(decorator);
|
|
696
|
-
},
|
|
697
|
-
decorateCommand(decorator) {
|
|
698
|
-
decorators.addCommandDecorator(decorator);
|
|
699
|
-
}
|
|
700
|
-
});
|
|
701
|
-
}
|
|
702
|
-
//#endregion
|
|
703
|
-
//#region src/plugin/dependency.ts
|
|
704
|
-
/**
|
|
705
|
-
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
706
|
-
* @license MIT
|
|
707
|
-
*/
|
|
708
|
-
/**
|
|
709
|
-
* Resolve plugin dependencies using topological sort
|
|
710
|
-
*
|
|
711
|
-
* @param plugins - Array of plugins to resolve
|
|
712
|
-
* @returns Array of plugins sorted by dependencies
|
|
713
|
-
*/
|
|
714
|
-
function resolveDependencies(plugins) {
|
|
715
|
-
const sorted = [];
|
|
716
|
-
const visited = /* @__PURE__ */ new Set();
|
|
717
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
718
|
-
const pluginMap = /* @__PURE__ */ new Map();
|
|
719
|
-
for (const plugin of plugins) if (plugin.id) {
|
|
720
|
-
if (pluginMap.has(plugin.id)) console.warn(`Duplicate plugin id detected: \`${plugin.id}\``);
|
|
721
|
-
pluginMap.set(plugin.id, plugin);
|
|
722
|
-
}
|
|
723
|
-
function visit(plugin) {
|
|
724
|
-
if (!plugin.id) return;
|
|
725
|
-
if (visited.has(plugin.id)) return;
|
|
726
|
-
if (visiting.has(plugin.id)) throw new Error(`Circular dependency detected: \`${[...visiting].join(` -> `) + " -> " + plugin.id}\``);
|
|
727
|
-
visiting.add(plugin.id);
|
|
728
|
-
const deps = plugin.dependencies || [];
|
|
729
|
-
for (const dep of deps) {
|
|
730
|
-
const depId = typeof dep === "string" ? dep : dep.id;
|
|
731
|
-
const isOptional = typeof dep === "string" ? false : dep.optional || false;
|
|
732
|
-
const depPlugin = pluginMap.get(depId);
|
|
733
|
-
if (!depPlugin && !isOptional) throw new Error(`Missing required dependency: \`${depId}\` on \`${plugin.id}\``);
|
|
734
|
-
if (depPlugin) visit(depPlugin);
|
|
735
|
-
}
|
|
736
|
-
visiting.delete(plugin.id);
|
|
737
|
-
visited.add(plugin.id);
|
|
738
|
-
sorted.push(plugin);
|
|
739
|
-
}
|
|
740
|
-
for (const plugin of plugins) visit(plugin);
|
|
741
|
-
return sorted;
|
|
742
|
-
}
|
|
743
|
-
//#endregion
|
|
744
|
-
//#region src/cli/core.ts
|
|
745
|
-
/**
|
|
746
|
-
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
747
|
-
* @license MIT
|
|
748
|
-
*/
|
|
749
|
-
/**
|
|
750
|
-
* Run the command.
|
|
751
|
-
*
|
|
752
|
-
* @typeParam G - A type extending {@linkcode GunshiParams} to specify the shape of command and cli options.
|
|
753
|
-
*
|
|
754
|
-
* @param argv - Command line arguments
|
|
755
|
-
* @param entry - A {@link Command | entry command}, an {@link CommandRunner | inline command runner}, or a {@link LazyCommand | lazily-loaded command}
|
|
756
|
-
* @param options - A {@link CliOptions | CLI options}
|
|
757
|
-
* @param plugins - An array of {@link Plugin | plugins} to be applied
|
|
758
|
-
* @returns A rendered usage or undefined. if you will use {@link CliOptions.usageSilent} option, it will return rendered usage string.
|
|
759
|
-
*/
|
|
760
|
-
async function cliCore(argv, entry, options, plugins) {
|
|
761
|
-
const decorators = createDecorators();
|
|
762
|
-
const pluginContext = createPluginContext(decorators, createInitialSubCommands(options, entry));
|
|
763
|
-
const resolvedPlugins = await applyPlugins(pluginContext, [...plugins, ...options.plugins || []]);
|
|
764
|
-
const cliOptions = normalizeCliOptions(options, decorators, pluginContext);
|
|
765
|
-
const tokens = parseArgs(argv);
|
|
766
|
-
const resolved = resolveCommandTree(tokens, entry, cliOptions);
|
|
767
|
-
const { commandName: name, command, callMode, commandPath, depth, levelSubCommands } = resolved;
|
|
768
|
-
if (!command) throw new Error(`Command not found: ${name || ""}`);
|
|
769
|
-
if (levelSubCommands) cliOptions.subCommands = levelSubCommands;
|
|
770
|
-
const resolvedCommand = isLazyCommand(command) ? await resolveLazyCommand(command, name, true) : command;
|
|
771
|
-
const args = resolveArguments(pluginContext, getCommandArgs(resolvedCommand));
|
|
772
|
-
const skipPositional = depth > 0 ? depth - 1 : -1;
|
|
773
|
-
const { explicit, values, positionals, rest, error } = resolveArgs(args, tokens, {
|
|
774
|
-
shortGrouping: true,
|
|
775
|
-
toKebab: resolvedCommand.toKebab,
|
|
776
|
-
skipPositional
|
|
777
|
-
});
|
|
778
|
-
const omitted = resolved.omitted;
|
|
779
|
-
return await executeCommand(resolvedCommand, await createCommandContext({
|
|
780
|
-
args,
|
|
781
|
-
explicit,
|
|
782
|
-
values,
|
|
783
|
-
positionals,
|
|
784
|
-
rest,
|
|
785
|
-
argv,
|
|
786
|
-
tokens,
|
|
787
|
-
omitted,
|
|
788
|
-
callMode,
|
|
789
|
-
commandPath,
|
|
790
|
-
command: resolvedCommand,
|
|
791
|
-
extensions: getPluginExtensions(resolvedPlugins),
|
|
792
|
-
validationError: error,
|
|
793
|
-
cliOptions
|
|
794
|
-
}), decorators.commandDecorators);
|
|
795
|
-
}
|
|
796
|
-
async function applyPlugins(pluginContext, plugins) {
|
|
797
|
-
const sortedPlugins = resolveDependencies(plugins);
|
|
798
|
-
try {
|
|
799
|
-
for (const plugin of sortedPlugins)
|
|
800
|
-
/**
|
|
801
|
-
* NOTE(kazupon):
|
|
802
|
-
* strictly `Args` are not required for plugin installation.
|
|
803
|
-
* because the strictly `Args` required by each plugin are unknown,
|
|
804
|
-
* and the plugin side can not know what the user will specify.
|
|
805
|
-
*/
|
|
806
|
-
await plugin(pluginContext);
|
|
807
|
-
} catch (error) {
|
|
808
|
-
console.error("Error loading plugin:", error.message);
|
|
809
|
-
}
|
|
810
|
-
return sortedPlugins;
|
|
811
|
-
}
|
|
812
|
-
function getCommandArgs(cmd) {
|
|
813
|
-
if (isLazyCommand(cmd)) return cmd.args || create();
|
|
814
|
-
else if (typeof cmd === "object") return cmd.args || create();
|
|
815
|
-
else return create();
|
|
816
|
-
}
|
|
817
|
-
function resolveArguments(pluginContext, args) {
|
|
818
|
-
return Object.assign(create(), Object.fromEntries(pluginContext.globalOptions), args);
|
|
819
|
-
}
|
|
820
|
-
const isObject = (val) => val !== null && typeof val === "object";
|
|
821
|
-
function createInitialSubCommands(options, entryCmd) {
|
|
822
|
-
const hasSubCommands = options.subCommands ? options.subCommands instanceof Map ? options.subCommands.size > 0 : isObject(options.subCommands) && Object.keys(options.subCommands).length > 0 : false;
|
|
823
|
-
const subCommands = new Map(options.subCommands instanceof Map ? options.subCommands : []);
|
|
824
|
-
if (!(options.subCommands instanceof Map) && isObject(options.subCommands)) for (const [name, cmd] of Object.entries(options.subCommands)) subCommands.set(name, cmd);
|
|
825
|
-
if (hasSubCommands) {
|
|
826
|
-
if (isLazyCommand(entryCmd)) {
|
|
827
|
-
const entryCopy = Object.assign((...args) => entryCmd(...args), entryCmd, { entry: true });
|
|
828
|
-
subCommands.set(resolveEntryName(entryCopy), entryCopy);
|
|
829
|
-
} else if (typeof entryCmd === "object") {
|
|
830
|
-
const entryCopy = Object.assign(create(), entryCmd, { entry: true });
|
|
831
|
-
subCommands.set(resolveEntryName(entryCopy), entryCopy);
|
|
832
|
-
} else if (typeof entryCmd === "function") {
|
|
833
|
-
const name = entryCmd.name || "(anonymous)";
|
|
834
|
-
subCommands.set(name, {
|
|
835
|
-
run: entryCmd,
|
|
836
|
-
name,
|
|
837
|
-
entry: true
|
|
838
|
-
});
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
return subCommands;
|
|
842
|
-
}
|
|
843
|
-
function normalizeCliOptions(options, decorators, pluginContext) {
|
|
844
|
-
const subCommands = new Map(pluginContext.subCommands);
|
|
845
|
-
const resolvedOptions = Object.assign(create(), CLI_OPTIONS_DEFAULT, options, { subCommands });
|
|
846
|
-
if (resolvedOptions.renderHeader === void 0) resolvedOptions.renderHeader = decorators.getHeaderRenderer();
|
|
847
|
-
if (resolvedOptions.renderUsage === void 0) resolvedOptions.renderUsage = decorators.getUsageRenderer();
|
|
848
|
-
if (resolvedOptions.renderValidationErrors === void 0) resolvedOptions.renderValidationErrors = decorators.getValidationErrorsRenderer();
|
|
849
|
-
return resolvedOptions;
|
|
850
|
-
}
|
|
851
|
-
function getPositionalTokens(tokens) {
|
|
852
|
-
return tokens.filter((t) => t.kind === "positional").map((t) => t.value).filter((v) => !!v);
|
|
853
|
-
}
|
|
854
|
-
function resolveCommandTree(tokens, entry, options) {
|
|
855
|
-
const positionals = getPositionalTokens(tokens);
|
|
856
|
-
function resolveAsEntry() {
|
|
857
|
-
if (typeof entry === "function") if ("commandName" in entry && entry.commandName) return {
|
|
858
|
-
commandName: entry.commandName,
|
|
859
|
-
command: entry,
|
|
860
|
-
callMode: "entry",
|
|
861
|
-
commandPath: [],
|
|
862
|
-
depth: 0,
|
|
863
|
-
omitted: options.subCommands.size > 0 && !positionals[0],
|
|
864
|
-
levelSubCommands: options.subCommands.size > 0 ? options.subCommands : void 0
|
|
865
|
-
};
|
|
866
|
-
else return {
|
|
867
|
-
command: {
|
|
868
|
-
run: entry,
|
|
869
|
-
entry: true
|
|
870
|
-
},
|
|
871
|
-
callMode: "entry",
|
|
872
|
-
commandPath: [],
|
|
873
|
-
depth: 0,
|
|
874
|
-
omitted: options.subCommands.size > 0 && !positionals[0],
|
|
875
|
-
levelSubCommands: options.subCommands.size > 0 ? options.subCommands : void 0
|
|
876
|
-
};
|
|
877
|
-
else if (typeof entry === "object") return {
|
|
878
|
-
commandName: resolveEntryName(entry),
|
|
879
|
-
command: entry,
|
|
880
|
-
callMode: "entry",
|
|
881
|
-
commandPath: [],
|
|
882
|
-
depth: 0,
|
|
883
|
-
omitted: options.subCommands.size > 0 && !positionals[0],
|
|
884
|
-
levelSubCommands: options.subCommands.size > 0 ? options.subCommands : void 0
|
|
885
|
-
};
|
|
886
|
-
else return {
|
|
887
|
-
callMode: "unexpected",
|
|
888
|
-
commandPath: [],
|
|
889
|
-
depth: 0,
|
|
890
|
-
omitted: false,
|
|
891
|
-
levelSubCommands: void 0
|
|
892
|
-
};
|
|
893
|
-
}
|
|
894
|
-
if (positionals.length === 0 || options.subCommands.size === 0) return resolveAsEntry();
|
|
895
|
-
let currentSubCommands = options.subCommands;
|
|
896
|
-
let resolvedCommand;
|
|
897
|
-
let resolvedName;
|
|
898
|
-
const commandPath = [];
|
|
899
|
-
let depth = 0;
|
|
900
|
-
for (let i = 0; i < positionals.length; i++) {
|
|
901
|
-
const token = positionals[i];
|
|
902
|
-
const cmd = currentSubCommands.get(token);
|
|
903
|
-
if (cmd == null) {
|
|
904
|
-
if (depth === 0) {
|
|
905
|
-
if (options.fallbackToEntry) return resolveAsEntry();
|
|
906
|
-
return {
|
|
907
|
-
commandName: token,
|
|
908
|
-
callMode: "unexpected",
|
|
909
|
-
commandPath: [],
|
|
910
|
-
depth: 0,
|
|
911
|
-
omitted: false,
|
|
912
|
-
levelSubCommands: void 0
|
|
913
|
-
};
|
|
914
|
-
}
|
|
915
|
-
break;
|
|
916
|
-
}
|
|
917
|
-
let resolved = cmd;
|
|
918
|
-
if (typeof cmd === "function" && cmd.commandName == null) resolved = Object.assign((...args) => cmd(...args), cmd, { commandName: token });
|
|
919
|
-
else if (typeof cmd === "object" && cmd.name == null) resolved = Object.assign(create(), cmd, { name: token });
|
|
920
|
-
resolvedCommand = resolved;
|
|
921
|
-
resolvedName = token;
|
|
922
|
-
commandPath.push(token);
|
|
923
|
-
depth++;
|
|
924
|
-
const nestedSubCommands = getCommandSubCommands(cmd);
|
|
925
|
-
if (nestedSubCommands && nestedSubCommands.size > 0) currentSubCommands = nestedSubCommands;
|
|
926
|
-
else break;
|
|
927
|
-
}
|
|
928
|
-
if (!resolvedCommand) return resolveAsEntry();
|
|
929
|
-
const resolvedSubCommands = getCommandSubCommands(resolvedCommand);
|
|
930
|
-
const omitted = resolvedSubCommands != null && resolvedSubCommands.size > 0;
|
|
931
|
-
let levelSubCommands;
|
|
932
|
-
if (omitted && resolvedSubCommands) {
|
|
933
|
-
levelSubCommands = new Map(resolvedSubCommands);
|
|
934
|
-
let entryCopy;
|
|
935
|
-
if (typeof resolvedCommand === "function") entryCopy = Object.assign((...args) => resolvedCommand(...args), resolvedCommand, { entry: true });
|
|
936
|
-
else entryCopy = Object.assign(create(), resolvedCommand, { entry: true });
|
|
937
|
-
levelSubCommands.set(resolvedName || resolveEntryName(entryCopy), entryCopy);
|
|
938
|
-
}
|
|
939
|
-
return {
|
|
940
|
-
commandName: resolvedName,
|
|
941
|
-
command: resolvedCommand,
|
|
942
|
-
callMode: depth > 0 ? "subCommand" : "entry",
|
|
943
|
-
commandPath,
|
|
944
|
-
depth,
|
|
945
|
-
omitted,
|
|
946
|
-
levelSubCommands
|
|
947
|
-
};
|
|
948
|
-
}
|
|
949
|
-
function resolveEntryName(entry) {
|
|
950
|
-
return isLazyCommand(entry) ? entry.commandName || "(anonymous)" : entry.name || "(anonymous)";
|
|
951
|
-
}
|
|
952
|
-
function getPluginExtensions(plugins) {
|
|
953
|
-
const extensions = create();
|
|
954
|
-
for (const plugin of plugins) if (plugin.extension) {
|
|
955
|
-
const key = plugin.id;
|
|
956
|
-
if (extensions[key]) console.warn(`Plugin "${key}" is already installed. Ignore it for command context extending.`);
|
|
957
|
-
else extensions[key] = plugin.extension;
|
|
958
|
-
}
|
|
959
|
-
return extensions;
|
|
960
|
-
}
|
|
961
|
-
async function executeCommand(cmd, ctx, decorators) {
|
|
962
|
-
const baseRunner = cmd.run || NOOP;
|
|
963
|
-
const decoratedRunner = decorators.reduceRight((runner, decorator) => decorator(runner), baseRunner);
|
|
964
|
-
try {
|
|
965
|
-
if (ctx.env.onBeforeCommand) await ctx.env.onBeforeCommand(ctx);
|
|
966
|
-
const result = await decoratedRunner(ctx);
|
|
967
|
-
if (ctx.env.onAfterCommand) await ctx.env.onAfterCommand(ctx, result);
|
|
968
|
-
return typeof result === "string" ? result : void 0;
|
|
969
|
-
} catch (error) {
|
|
970
|
-
if (ctx.env.onErrorCommand) try {
|
|
971
|
-
await ctx.env.onErrorCommand(ctx, error);
|
|
972
|
-
} catch (hookError) {
|
|
973
|
-
console.error("Error in onErrorCommand hook:", hookError);
|
|
974
|
-
}
|
|
975
|
-
throw error;
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
//#endregion
|
|
979
|
-
export { resolveArgs as n, parseArgs as r, cliCore as t };
|
|
681
|
+
export { parseArgs as a, resolveArgs as i, ArgsValidationErrorKeys as n, isArgsValidationError as r, ArgsValidationError as t };
|