@gunshi/bone 0.35.1 → 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/index.d.ts +27 -3
- package/lib/index.js +343 -54
- package/package.json +5 -5
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../node_modules/.pnpm/args-tokens@0.
|
|
1
|
+
//#region ../../node_modules/.pnpm/args-tokens@0.28.0/node_modules/args-tokens/lib/parser-DT7Ztcch.d.ts
|
|
2
2
|
//#region src/parser.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Entry point of argument parser.
|
|
@@ -55,7 +55,7 @@ interface ArgToken {
|
|
|
55
55
|
* Parser Options.
|
|
56
56
|
*/
|
|
57
57
|
//#endregion
|
|
58
|
-
//#region ../../node_modules/.pnpm/args-tokens@0.
|
|
58
|
+
//#region ../../node_modules/.pnpm/args-tokens@0.28.0/node_modules/args-tokens/lib/resolver.d.ts
|
|
59
59
|
//#region src/resolver.d.ts
|
|
60
60
|
/**
|
|
61
61
|
* An argument schema definition for command-line argument parsing.
|
|
@@ -476,7 +476,7 @@ interface ArgSchema {
|
|
|
476
476
|
*
|
|
477
477
|
* @param value - Raw string value from command line
|
|
478
478
|
* @returns Parsed value of any type
|
|
479
|
-
* @throws Error or subclass when value is invalid
|
|
479
|
+
* @throws {Error} Error or subclass when value is invalid
|
|
480
480
|
*
|
|
481
481
|
* @example
|
|
482
482
|
* Custom parsing functions:
|
|
@@ -508,6 +508,13 @@ interface ArgSchema {
|
|
|
508
508
|
*/
|
|
509
509
|
parse?: (value: string) => any;
|
|
510
510
|
}
|
|
511
|
+
/**
|
|
512
|
+
* Machine-readable error codes for {@link ArgsValidationError}.
|
|
513
|
+
*
|
|
514
|
+
* Each code identifies a validation failure category and is also suitable as an
|
|
515
|
+
* i18n resource key for localized rendering.
|
|
516
|
+
*/
|
|
517
|
+
|
|
511
518
|
/**
|
|
512
519
|
* An object that contains {@link ArgSchema | argument schema}.
|
|
513
520
|
*
|
|
@@ -922,6 +929,13 @@ interface CommandEnvironment<G extends GunshiParamsConstraint = DefaultGunshiPar
|
|
|
922
929
|
* @see {@linkcode CliOptions.usageSilent}
|
|
923
930
|
*/
|
|
924
931
|
usageSilent: boolean;
|
|
932
|
+
/**
|
|
933
|
+
* Whether to treat undefined options as argument validation errors.
|
|
934
|
+
*
|
|
935
|
+
* @default false
|
|
936
|
+
* @see {@linkcode CliOptions.strict}
|
|
937
|
+
*/
|
|
938
|
+
strict: boolean;
|
|
925
939
|
/**
|
|
926
940
|
* Sub commands.
|
|
927
941
|
*
|
|
@@ -1009,6 +1023,16 @@ interface CliOptions<G extends GunshiParamsConstraint = DefaultGunshiParams> {
|
|
|
1009
1023
|
* Whether to display the command usage.
|
|
1010
1024
|
*/
|
|
1011
1025
|
usageSilent?: boolean;
|
|
1026
|
+
/**
|
|
1027
|
+
* Whether to treat undefined options as argument validation errors.
|
|
1028
|
+
*
|
|
1029
|
+
* When enabled, option tokens that are not declared by the resolved command
|
|
1030
|
+
* arguments or installed global options are reported as validation errors
|
|
1031
|
+
* before the command runner is executed.
|
|
1032
|
+
*
|
|
1033
|
+
* @default false
|
|
1034
|
+
*/
|
|
1035
|
+
strict?: boolean;
|
|
1012
1036
|
/**
|
|
1013
1037
|
* Render function the command usage.
|
|
1014
1038
|
*/
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../node_modules/.pnpm/args-tokens@0.
|
|
1
|
+
//#region ../../node_modules/.pnpm/args-tokens@0.28.0/node_modules/args-tokens/lib/parser.js
|
|
2
2
|
/**
|
|
3
3
|
* forked from `nodejs/node` (`pkgjs/parseargs`)
|
|
4
4
|
* repository url: https://github.com/nodejs/node (https://github.com/pkgjs/parseargs)
|
|
@@ -203,7 +203,7 @@ function hasOptionValue(value) {
|
|
|
203
203
|
return !(value == null) && value.codePointAt(0) !== HYPHEN_CODE;
|
|
204
204
|
}
|
|
205
205
|
//#endregion
|
|
206
|
-
//#region ../../node_modules/.pnpm/args-tokens@0.
|
|
206
|
+
//#region ../../node_modules/.pnpm/args-tokens@0.28.0/node_modules/args-tokens/lib/utils.js
|
|
207
207
|
/**
|
|
208
208
|
* Entry point of utils.
|
|
209
209
|
*
|
|
@@ -224,8 +224,17 @@ function hasOptionValue(value) {
|
|
|
224
224
|
function kebabnize(str) {
|
|
225
225
|
return str.replace(/[A-Z]/g, (match, offset) => (offset > 0 ? "-" : "") + match.toLowerCase());
|
|
226
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Format allowed argument choices for validation error values and fallback messages.
|
|
229
|
+
*
|
|
230
|
+
* @param choices - Choice values to format
|
|
231
|
+
* @returns JSON-quoted choices joined with `, `.
|
|
232
|
+
*/
|
|
233
|
+
function formatChoices(choices) {
|
|
234
|
+
return choices.map((value) => JSON.stringify(value)).join(", ");
|
|
235
|
+
}
|
|
227
236
|
//#endregion
|
|
228
|
-
//#region ../../node_modules/.pnpm/args-tokens@0.
|
|
237
|
+
//#region ../../node_modules/.pnpm/args-tokens@0.28.0/node_modules/args-tokens/lib/resolver.js
|
|
229
238
|
/**
|
|
230
239
|
* Entry point of argument options resolver.
|
|
231
240
|
*
|
|
@@ -235,6 +244,60 @@ function kebabnize(str) {
|
|
|
235
244
|
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
236
245
|
* @license MIT
|
|
237
246
|
*/
|
|
247
|
+
/**
|
|
248
|
+
* Machine-readable error codes for {@link ArgsValidationError}.
|
|
249
|
+
*
|
|
250
|
+
* Each code identifies a validation failure category and is also suitable as an
|
|
251
|
+
* i18n resource key for localized rendering.
|
|
252
|
+
*/
|
|
253
|
+
const ArgsValidationErrorKeys = {
|
|
254
|
+
requiredOption: "err:arg:required-option",
|
|
255
|
+
requiredPositional: "err:arg:required-positional",
|
|
256
|
+
invalidType: "err:arg:invalid-type",
|
|
257
|
+
invalidChoice: "err:arg:invalid-choice",
|
|
258
|
+
customParse: "err:arg:custom-parse",
|
|
259
|
+
unknownOption: "err:arg:unknown-option"
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* An error that contains structured metadata for argument validation failures.
|
|
263
|
+
*
|
|
264
|
+
* The `message` remains the English fallback message. Renderers can use `code`
|
|
265
|
+
* and `values` to localize the error, falling back to `message` when localization
|
|
266
|
+
* is unavailable.
|
|
267
|
+
*/
|
|
268
|
+
var ArgsValidationError = class extends Error {
|
|
269
|
+
/**
|
|
270
|
+
* Machine-readable error code for this validation failure.
|
|
271
|
+
*
|
|
272
|
+
* This code can also be used as an i18n resource key.
|
|
273
|
+
*/
|
|
274
|
+
code;
|
|
275
|
+
/**
|
|
276
|
+
* Interpolation values for `code`.
|
|
277
|
+
*/
|
|
278
|
+
values;
|
|
279
|
+
/**
|
|
280
|
+
* Create an `ArgsValidationError` instance.
|
|
281
|
+
*
|
|
282
|
+
* @param message - fallback error message
|
|
283
|
+
* @param options - structured validation metadata
|
|
284
|
+
*/
|
|
285
|
+
constructor(message, options = {}) {
|
|
286
|
+
super(message, { cause: options.cause });
|
|
287
|
+
this.name = "ArgsValidationError";
|
|
288
|
+
this.code = options.code;
|
|
289
|
+
this.values = options.values ?? {};
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
/**
|
|
293
|
+
* Check whether the given value is an {@link ArgsValidationError}.
|
|
294
|
+
*
|
|
295
|
+
* @param error - value to check
|
|
296
|
+
* @returns `true` when the value is an `ArgsValidationError`
|
|
297
|
+
*/
|
|
298
|
+
function isArgsValidationError(error) {
|
|
299
|
+
return error instanceof ArgsValidationError;
|
|
300
|
+
}
|
|
238
301
|
const SKIP_POSITIONAL_DEFAULT = -1;
|
|
239
302
|
/**
|
|
240
303
|
* Resolve command line arguments.
|
|
@@ -398,11 +461,9 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
|
|
|
398
461
|
const parsed = [];
|
|
399
462
|
for (let i = positionalsCount; i < endPositionals; i++) {
|
|
400
463
|
const p = positionalTokens[i];
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
errors.push(error);
|
|
405
|
-
}
|
|
464
|
+
const [parsedValue, error] = parseSchemaValue(p.value, rawArg, arg, schema);
|
|
465
|
+
if (error) errors.push(error);
|
|
466
|
+
else parsed.push(parsedValue);
|
|
406
467
|
}
|
|
407
468
|
values[rawArg] = parsed;
|
|
408
469
|
} else {
|
|
@@ -412,22 +473,22 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
|
|
|
412
473
|
}
|
|
413
474
|
positionalsCount = endPositionals;
|
|
414
475
|
explicit[rawArg] = true;
|
|
415
|
-
} else if (schema.required) errors.push(createRequireError(arg, schema));
|
|
416
|
-
} else if (schema.required) errors.push(createRequireError(arg, schema));
|
|
476
|
+
} else if (schema.required) errors.push(createRequireError(rawArg, arg, schema));
|
|
477
|
+
} else if (schema.required) errors.push(createRequireError(rawArg, arg, schema));
|
|
417
478
|
} else {
|
|
418
479
|
const positional = positionalTokens[positionalsCount];
|
|
419
480
|
if (shouldRequireMissingSinglePositional(schema)) {
|
|
420
481
|
if (positional != null) {
|
|
421
|
-
resolveSinglePositionalValue(values, errors, rawArg, schema, positional);
|
|
482
|
+
resolveSinglePositionalValue(values, errors, rawArg, arg, schema, positional);
|
|
422
483
|
explicit[rawArg] = true;
|
|
423
484
|
positionalsCount++;
|
|
424
|
-
} else errors.push(createRequireError(arg, schema));
|
|
485
|
+
} else errors.push(createRequireError(rawArg, arg, schema));
|
|
425
486
|
continue;
|
|
426
487
|
}
|
|
427
488
|
if (positional != null) {
|
|
428
489
|
const requiredPositionals = getRequiredPositionalsAfter(rawArg);
|
|
429
490
|
if (Math.max(positionalTokens.length - positionalsCount, 0) > requiredPositionals) {
|
|
430
|
-
resolveSinglePositionalValue(values, errors, rawArg, schema, positional);
|
|
491
|
+
resolveSinglePositionalValue(values, errors, rawArg, arg, schema, positional);
|
|
431
492
|
explicit[rawArg] = true;
|
|
432
493
|
positionalsCount++;
|
|
433
494
|
continue;
|
|
@@ -441,14 +502,14 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
|
|
|
441
502
|
if (!optionTokens.find((token) => {
|
|
442
503
|
return schema.short && token.name === schema.short || token.rawName && hasLongOptionPrefix(token.rawName) && token.name === arg;
|
|
443
504
|
})) {
|
|
444
|
-
errors.push(createRequireError(arg, schema));
|
|
505
|
+
errors.push(createRequireError(rawArg, arg, schema));
|
|
445
506
|
continue;
|
|
446
507
|
}
|
|
447
508
|
}
|
|
448
509
|
for (let i = 0; i < optionTokens.length; i++) {
|
|
449
510
|
const token = optionTokens[i];
|
|
450
511
|
if (checkTokenName(arg, schema, token) && token.rawName != void 0 && hasLongOptionPrefix(token.rawName) || schema.short === token.name && token.rawName != void 0 && isShortOption(token.rawName)) {
|
|
451
|
-
const invalid = validateRequire(token, arg, schema);
|
|
512
|
+
const invalid = validateRequire(token, rawArg, arg, schema);
|
|
452
513
|
if (invalid) {
|
|
453
514
|
errors.push(invalid);
|
|
454
515
|
continue;
|
|
@@ -456,7 +517,7 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
|
|
|
456
517
|
explicit[rawArg] = true;
|
|
457
518
|
const actualInputName = isShortOption(token.rawName) ? `-${token.name}` : `--${arg}`;
|
|
458
519
|
actualInputNames.set(rawArg, actualInputName);
|
|
459
|
-
const [parsedValue, error] = parse(token, arg, schema);
|
|
520
|
+
const [parsedValue, error] = parse(token, rawArg, arg, schema);
|
|
460
521
|
if (error) errors.push(error);
|
|
461
522
|
else if (schema.multiple) {
|
|
462
523
|
values[rawArg] ||= [];
|
|
@@ -476,39 +537,49 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
|
|
|
476
537
|
explicit
|
|
477
538
|
};
|
|
478
539
|
}
|
|
479
|
-
function parse(token, option, schema) {
|
|
480
|
-
if (typeof schema.parse === "function")
|
|
540
|
+
function parse(token, rawArg, option, schema) {
|
|
541
|
+
if (typeof schema.parse === "function") {
|
|
481
542
|
if (schema.type === "boolean") {
|
|
482
543
|
const boolValue = !(schema.negatable && token.name.startsWith("no-"));
|
|
483
|
-
return
|
|
544
|
+
return parseSchemaValue(String(boolValue), rawArg, option, schema);
|
|
484
545
|
}
|
|
485
|
-
return
|
|
486
|
-
} catch (error) {
|
|
487
|
-
return [void 0, error];
|
|
546
|
+
return parseSchemaValue(token.value ?? String(schema.default ?? ""), rawArg, option, schema);
|
|
488
547
|
}
|
|
489
548
|
switch (schema.type) {
|
|
490
|
-
case "string": return typeof token.value === "string" ? [token.value || schema.default, void 0] : [void 0, createTypeError(option, schema)];
|
|
549
|
+
case "string": return typeof token.value === "string" ? [token.value || schema.default, void 0] : [void 0, createTypeError(rawArg, option, schema, token.value)];
|
|
491
550
|
case "boolean": return [!(schema.negatable && token.name.startsWith("no-")), void 0];
|
|
492
551
|
case "number":
|
|
493
|
-
if (!isNumeric(token.value)) return [void 0, createTypeError(option, schema)];
|
|
552
|
+
if (!isNumeric(token.value)) return [void 0, createTypeError(rawArg, option, schema, token.value)];
|
|
494
553
|
return token.value ? [+token.value, void 0] : [+(schema.default || ""), void 0];
|
|
495
554
|
case "enum":
|
|
496
|
-
if (schema.choices && !schema.choices.includes(token.value)) return [void 0,
|
|
555
|
+
if (schema.choices && !schema.choices.includes(token.value)) return [void 0, createChoiceError(rawArg, option, schema, token.value)];
|
|
497
556
|
return [token.value || schema.default, void 0];
|
|
498
557
|
case "custom": throw new TypeError(`argument '${option}' should have a 'parse' function`);
|
|
499
558
|
default: throw new Error(`Unsupported argument type '${schema.type}' for option '${option}'`);
|
|
500
559
|
}
|
|
501
560
|
}
|
|
502
|
-
function
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
function resolveSinglePositionalValue(values, errors, rawArg, schema, positional) {
|
|
506
|
-
if (typeof schema.parse === "function") try {
|
|
507
|
-
values[rawArg] = schema.parse(positional.value);
|
|
561
|
+
function parseSchemaValue(value, rawArg, option, schema) {
|
|
562
|
+
try {
|
|
563
|
+
return [schema.parse(value), void 0];
|
|
508
564
|
} catch (error) {
|
|
509
|
-
|
|
565
|
+
return [void 0, createCustomParseError(error, rawArg, option, schema, value)];
|
|
510
566
|
}
|
|
511
|
-
|
|
567
|
+
}
|
|
568
|
+
function createRequireError(rawArg, option, schema) {
|
|
569
|
+
return new ArgResolveError(schema.type === "positional" ? `Positional argument '${option}' is required` : `Optional argument ${createOptionDisplayName(option, schema)} is required`, option, "required", schema, {
|
|
570
|
+
code: schema.type === "positional" ? ArgsValidationErrorKeys.requiredPositional : ArgsValidationErrorKeys.requiredOption,
|
|
571
|
+
values: schema.type === "positional" ? { name: rawArg } : {
|
|
572
|
+
displayName: createOptionDisplayName(option, schema),
|
|
573
|
+
name: rawArg
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
function resolveSinglePositionalValue(values, errors, rawArg, option, schema, positional) {
|
|
578
|
+
if (typeof schema.parse === "function") {
|
|
579
|
+
const [parsedValue, error] = parseSchemaValue(positional.value, rawArg, option, schema);
|
|
580
|
+
if (error) errors.push(error);
|
|
581
|
+
else values[rawArg] = parsedValue;
|
|
582
|
+
} else values[rawArg] = positional.value;
|
|
512
583
|
}
|
|
513
584
|
function hasDefault(schema) {
|
|
514
585
|
return schema.default != null;
|
|
@@ -538,7 +609,7 @@ function createRequiredPositionalsAfter(argEntries) {
|
|
|
538
609
|
* An error that occurs when resolving arguments.
|
|
539
610
|
* This error is thrown when the argument is not valid.
|
|
540
611
|
*/
|
|
541
|
-
var ArgResolveError = class extends
|
|
612
|
+
var ArgResolveError = class extends ArgsValidationError {
|
|
542
613
|
name;
|
|
543
614
|
schema;
|
|
544
615
|
type;
|
|
@@ -547,24 +618,78 @@ var ArgResolveError = class extends Error {
|
|
|
547
618
|
*
|
|
548
619
|
* @param message - the error message
|
|
549
620
|
* @param name - the name of the argument
|
|
550
|
-
* @param type - the type of the error
|
|
621
|
+
* @param type - the type of the error: 'type', 'required', or 'conflict'
|
|
551
622
|
* @param schema - the argument schema that caused the error
|
|
623
|
+
* @param options - structured validation metadata
|
|
552
624
|
*/
|
|
553
|
-
constructor(message, name, type, schema) {
|
|
554
|
-
super(message);
|
|
625
|
+
constructor(message, name, type, schema, options = {}) {
|
|
626
|
+
super(message, options);
|
|
555
627
|
this.name = name;
|
|
556
628
|
this.type = type;
|
|
557
629
|
this.schema = schema;
|
|
558
630
|
}
|
|
559
631
|
};
|
|
560
|
-
function validateRequire(token, option, schema) {
|
|
561
|
-
if (schema.required && schema.type !== "boolean" && !token.value) return createRequireError(option, schema);
|
|
632
|
+
function validateRequire(token, rawArg, option, schema) {
|
|
633
|
+
if (schema.required && schema.type !== "boolean" && !token.value) return createRequireError(rawArg, option, schema);
|
|
562
634
|
}
|
|
563
635
|
function isNumeric(str) {
|
|
564
636
|
return str.trim() !== "" && !isNaN(str);
|
|
565
637
|
}
|
|
566
|
-
function createTypeError(option, schema) {
|
|
567
|
-
return new ArgResolveError(`Optional argument
|
|
638
|
+
function createTypeError(rawArg, option, schema, actual) {
|
|
639
|
+
return new ArgResolveError(`Optional argument ${createOptionDisplayName(option, schema)} should be '${schema.type}'`, option, "type", schema, {
|
|
640
|
+
code: ArgsValidationErrorKeys.invalidType,
|
|
641
|
+
values: {
|
|
642
|
+
displayName: createOptionDisplayName(option, schema),
|
|
643
|
+
name: rawArg,
|
|
644
|
+
expected: schema.type,
|
|
645
|
+
...actual != null ? { actual } : {}
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
function createChoiceError(rawArg, option, schema, actual) {
|
|
650
|
+
const choices = schema.choices ?? [];
|
|
651
|
+
return new ArgResolveError(`Optional argument ${createOptionDisplayName(option, schema)} should be chosen from '${schema.type}' [${formatChoices(choices)}] values`, option, "type", schema, {
|
|
652
|
+
code: ArgsValidationErrorKeys.invalidChoice,
|
|
653
|
+
values: {
|
|
654
|
+
displayName: createOptionDisplayName(option, schema),
|
|
655
|
+
name: rawArg,
|
|
656
|
+
expected: schema.type,
|
|
657
|
+
choices: formatChoices(choices),
|
|
658
|
+
choiceValues: [...choices],
|
|
659
|
+
...actual != null ? { actual } : {}
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
function createCustomParseError(error, rawArg, option, schema, value) {
|
|
664
|
+
if (isArgsValidationError(error)) {
|
|
665
|
+
augmentValidationError(error, rawArg, option, schema, value);
|
|
666
|
+
return error;
|
|
667
|
+
}
|
|
668
|
+
const reason = getErrorReason(error);
|
|
669
|
+
return new ArgsValidationError(reason, {
|
|
670
|
+
code: ArgsValidationErrorKeys.customParse,
|
|
671
|
+
values: {
|
|
672
|
+
displayName: createArgumentDisplayName(option, schema),
|
|
673
|
+
name: rawArg,
|
|
674
|
+
reason
|
|
675
|
+
},
|
|
676
|
+
cause: error
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
function augmentValidationError(error, rawArg, option, schema, value) {
|
|
680
|
+
const values = error.values;
|
|
681
|
+
values.name ??= rawArg;
|
|
682
|
+
values.displayName ??= createArgumentDisplayName(option, schema);
|
|
683
|
+
if ((error.code === ArgsValidationErrorKeys.invalidType || error.code === ArgsValidationErrorKeys.invalidChoice) && values.actual == null) values.actual = value;
|
|
684
|
+
}
|
|
685
|
+
function getErrorReason(error) {
|
|
686
|
+
return error instanceof Error ? error.message : String(error);
|
|
687
|
+
}
|
|
688
|
+
function createArgumentDisplayName(option, schema) {
|
|
689
|
+
return schema.type === "positional" ? `'${option}'` : createOptionDisplayName(option, schema);
|
|
690
|
+
}
|
|
691
|
+
function createOptionDisplayName(option, schema) {
|
|
692
|
+
return `'--${option}'${schema.short ? ` or '-${schema.short}'` : ""}`;
|
|
568
693
|
}
|
|
569
694
|
function checkConflicts(args, explicit, toKebab, actualInputNames) {
|
|
570
695
|
for (const rawArg in args) {
|
|
@@ -583,7 +708,7 @@ function checkConflicts(args, explicit, toKebab, actualInputNames) {
|
|
|
583
708
|
return [];
|
|
584
709
|
}
|
|
585
710
|
//#endregion
|
|
586
|
-
//#region ../../node_modules/.pnpm/args-tokens@0.
|
|
711
|
+
//#region ../../node_modules/.pnpm/args-tokens@0.28.0/node_modules/args-tokens/lib/index.js
|
|
587
712
|
/**
|
|
588
713
|
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
589
714
|
* @license MIT
|
|
@@ -614,6 +739,7 @@ const CLI_OPTIONS_DEFAULT = {
|
|
|
614
739
|
middleMargin: 10,
|
|
615
740
|
usageOptionType: false,
|
|
616
741
|
usageOptionValue: true,
|
|
742
|
+
strict: false,
|
|
617
743
|
renderHeader: void 0,
|
|
618
744
|
renderUsage: void 0,
|
|
619
745
|
renderValidationErrors: void 0,
|
|
@@ -885,6 +1011,59 @@ function createDecorators() {
|
|
|
885
1011
|
});
|
|
886
1012
|
}
|
|
887
1013
|
//#endregion
|
|
1014
|
+
//#region ../gunshi/src/error.ts
|
|
1015
|
+
/**
|
|
1016
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
1017
|
+
* @license MIT
|
|
1018
|
+
*/
|
|
1019
|
+
/**
|
|
1020
|
+
* Command not found error resource keys.
|
|
1021
|
+
*/
|
|
1022
|
+
const CommandNotFoundErrorKeys = { notFound: "err:cmd:not-found" };
|
|
1023
|
+
/**
|
|
1024
|
+
* Error raised when a command cannot be resolved.
|
|
1025
|
+
*/
|
|
1026
|
+
var CommandNotFoundError = class extends Error {
|
|
1027
|
+
code;
|
|
1028
|
+
values;
|
|
1029
|
+
commandName;
|
|
1030
|
+
candidates;
|
|
1031
|
+
commandPath;
|
|
1032
|
+
/**
|
|
1033
|
+
* Create a command-not-found error.
|
|
1034
|
+
*
|
|
1035
|
+
* @param message - Fallback error message
|
|
1036
|
+
* @param options - Command-not-found metadata
|
|
1037
|
+
*/
|
|
1038
|
+
constructor(message, options) {
|
|
1039
|
+
super(message, { cause: options.cause });
|
|
1040
|
+
this.name = "CommandNotFoundError";
|
|
1041
|
+
this.code = options.code;
|
|
1042
|
+
this.values = options.values || {};
|
|
1043
|
+
this.commandName = options.commandName;
|
|
1044
|
+
this.candidates = options.candidates || [];
|
|
1045
|
+
this.commandPath = options.commandPath || [];
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
/**
|
|
1049
|
+
* Check whether an error is a {@link CommandNotFoundError}.
|
|
1050
|
+
*
|
|
1051
|
+
* @param error - An unknown error
|
|
1052
|
+
* @returns `true` if the error is a {@link CommandNotFoundError}
|
|
1053
|
+
*/
|
|
1054
|
+
function isCommandNotFoundError(error) {
|
|
1055
|
+
return error instanceof CommandNotFoundError;
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Check whether validation errors should be handled before version, help, or command execution.
|
|
1059
|
+
*
|
|
1060
|
+
* @param error - An aggregate validation error
|
|
1061
|
+
* @returns `true` if the validation error must be handled with priority
|
|
1062
|
+
*/
|
|
1063
|
+
function hasPriorityValidationError(error) {
|
|
1064
|
+
return error?.errors.some((error) => isCommandNotFoundError(error) || isArgsValidationError(error) && error.code === ArgsValidationErrorKeys.unknownOption) ?? false;
|
|
1065
|
+
}
|
|
1066
|
+
//#endregion
|
|
888
1067
|
//#region ../gunshi/src/plugin/context.ts
|
|
889
1068
|
/**
|
|
890
1069
|
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
@@ -1008,17 +1187,35 @@ async function cliCore(argv, entry, options, plugins) {
|
|
|
1008
1187
|
const tokens = parseArgs(argv);
|
|
1009
1188
|
const resolved = resolveCommandTree(tokens, entry, cliOptions);
|
|
1010
1189
|
const { commandName: name, command, callMode, commandPath, depth, levelSubCommands } = resolved;
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1190
|
+
let targetCommand = command;
|
|
1191
|
+
let targetCommandName = name;
|
|
1192
|
+
let targetCallMode = callMode;
|
|
1193
|
+
let targetCommandPath = commandPath;
|
|
1194
|
+
let targetDepth = depth;
|
|
1195
|
+
let targetOmitted = resolved.omitted;
|
|
1196
|
+
let targetLevelSubCommands = levelSubCommands;
|
|
1197
|
+
const additionalValidationErrors = [];
|
|
1198
|
+
if (!targetCommand) {
|
|
1199
|
+
if (!resolved.parentCommand || !resolved.unresolvedCommandName) throw new Error(`Command not found: ${name || ""}`);
|
|
1200
|
+
targetCommand = resolved.parentCommand;
|
|
1201
|
+
targetCommandName = resolved.parentCommandName;
|
|
1202
|
+
targetCommandPath = resolved.parentCommandPath || [];
|
|
1203
|
+
targetDepth = targetCommandPath.length;
|
|
1204
|
+
targetCallMode = targetDepth > 0 ? "subCommand" : "entry";
|
|
1205
|
+
targetOmitted = false;
|
|
1206
|
+
targetLevelSubCommands = resolved.parentSubCommands;
|
|
1207
|
+
additionalValidationErrors.push(createCommandNotFoundError(resolved));
|
|
1208
|
+
}
|
|
1209
|
+
if (targetLevelSubCommands) cliOptions.subCommands = targetLevelSubCommands;
|
|
1210
|
+
const resolvedCommand = isLazyCommand(targetCommand) ? await resolveLazyCommand(targetCommand, targetCommandName, true) : targetCommand;
|
|
1014
1211
|
const args = resolveArguments(pluginContext, getCommandArgs(resolvedCommand));
|
|
1015
|
-
const skipPositional =
|
|
1212
|
+
const skipPositional = targetDepth > 0 ? targetDepth - 1 : -1;
|
|
1016
1213
|
const { explicit, values, positionals, rest, error } = resolveArgs(args, tokens, {
|
|
1017
1214
|
shortGrouping: true,
|
|
1018
1215
|
toKebab: resolvedCommand.toKebab,
|
|
1019
1216
|
skipPositional
|
|
1020
1217
|
});
|
|
1021
|
-
const
|
|
1218
|
+
const validationError = mergeValidationErrors(error, [...additionalValidationErrors, ...cliOptions.strict ? createUnknownOptionErrors(findUnknownOptions(args, tokens, { toKebab: resolvedCommand.toKebab })) : []]);
|
|
1022
1219
|
return await executeCommand(resolvedCommand, await createCommandContext({
|
|
1023
1220
|
args,
|
|
1024
1221
|
explicit,
|
|
@@ -1027,12 +1224,12 @@ async function cliCore(argv, entry, options, plugins) {
|
|
|
1027
1224
|
rest,
|
|
1028
1225
|
argv,
|
|
1029
1226
|
tokens,
|
|
1030
|
-
omitted,
|
|
1031
|
-
callMode,
|
|
1032
|
-
commandPath,
|
|
1227
|
+
omitted: targetOmitted,
|
|
1228
|
+
callMode: targetCallMode,
|
|
1229
|
+
commandPath: targetCommandPath,
|
|
1033
1230
|
command: resolvedCommand,
|
|
1034
1231
|
extensions: getPluginExtensions(resolvedPlugins),
|
|
1035
|
-
validationError
|
|
1232
|
+
validationError,
|
|
1036
1233
|
cliOptions
|
|
1037
1234
|
}), decorators.commandDecorators);
|
|
1038
1235
|
}
|
|
@@ -1060,6 +1257,66 @@ function getCommandArgs(cmd) {
|
|
|
1060
1257
|
function resolveArguments(pluginContext, args) {
|
|
1061
1258
|
return Object.assign(create(), Object.fromEntries(pluginContext.globalOptions), args);
|
|
1062
1259
|
}
|
|
1260
|
+
const NEGATABLE_OPTION_PREFIX = "no-";
|
|
1261
|
+
function findUnknownOptions(args, tokens, options) {
|
|
1262
|
+
const knownLongOptions = /* @__PURE__ */ new Set();
|
|
1263
|
+
const knownShortOptions = /* @__PURE__ */ new Set();
|
|
1264
|
+
const knownNegatableOptions = /* @__PURE__ */ new Set();
|
|
1265
|
+
const knownLongOptionCandidates = /* @__PURE__ */ new Set();
|
|
1266
|
+
const longOptionCandidates = [];
|
|
1267
|
+
function addLongOptionCandidate(name) {
|
|
1268
|
+
const candidate = `--${name}`;
|
|
1269
|
+
if (knownLongOptionCandidates.has(candidate)) return;
|
|
1270
|
+
knownLongOptionCandidates.add(candidate);
|
|
1271
|
+
longOptionCandidates.push(candidate);
|
|
1272
|
+
}
|
|
1273
|
+
for (const [name, schema] of Object.entries(args)) {
|
|
1274
|
+
if (schema.type === "positional") continue;
|
|
1275
|
+
const optionName = resolveOptionName(name, schema, options);
|
|
1276
|
+
knownLongOptions.add(optionName);
|
|
1277
|
+
addLongOptionCandidate(optionName);
|
|
1278
|
+
if (schema.short) knownShortOptions.add(schema.short);
|
|
1279
|
+
if (schema.type === "boolean" && schema.negatable) {
|
|
1280
|
+
const negatableOptionName = `${NEGATABLE_OPTION_PREFIX}${optionName}`;
|
|
1281
|
+
knownNegatableOptions.add(negatableOptionName);
|
|
1282
|
+
addLongOptionCandidate(negatableOptionName);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
const unknownOptions = [];
|
|
1286
|
+
for (const token of tokens) {
|
|
1287
|
+
if (token.kind === "option-terminator") break;
|
|
1288
|
+
if (token.kind !== "option" || !token.name || !token.rawName) continue;
|
|
1289
|
+
const isLongOption = token.rawName.startsWith("--");
|
|
1290
|
+
if (isLongOption) {
|
|
1291
|
+
if (knownLongOptions.has(token.name) || knownNegatableOptions.has(token.name)) continue;
|
|
1292
|
+
} else if (knownShortOptions.has(token.name)) continue;
|
|
1293
|
+
unknownOptions.push({
|
|
1294
|
+
rawName: token.rawName,
|
|
1295
|
+
name: token.name,
|
|
1296
|
+
candidates: isLongOption ? longOptionCandidates : []
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
return unknownOptions;
|
|
1300
|
+
}
|
|
1301
|
+
function resolveOptionName(name, schema, options) {
|
|
1302
|
+
return options.toKebab || schema.toKebab ? kebabnize(name) : name;
|
|
1303
|
+
}
|
|
1304
|
+
function createUnknownOptionErrors(unknownOptions) {
|
|
1305
|
+
return unknownOptions.map(({ rawName, name, candidates }) => {
|
|
1306
|
+
return new ArgsValidationError(`Unknown option: ${rawName}`, {
|
|
1307
|
+
code: ArgsValidationErrorKeys.unknownOption,
|
|
1308
|
+
values: {
|
|
1309
|
+
rawName,
|
|
1310
|
+
name,
|
|
1311
|
+
candidates
|
|
1312
|
+
}
|
|
1313
|
+
});
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
function mergeValidationErrors(error, additionalErrors) {
|
|
1317
|
+
if (additionalErrors.length === 0) return error;
|
|
1318
|
+
return new AggregateError(error ? [...error.errors, ...additionalErrors] : additionalErrors, error?.message || additionalErrors[0]?.message);
|
|
1319
|
+
}
|
|
1063
1320
|
const isObject = (val) => val !== null && typeof val === "object";
|
|
1064
1321
|
function createInitialSubCommands(options, entryCmd) {
|
|
1065
1322
|
const hasSubCommands = options.subCommands ? options.subCommands instanceof Map ? options.subCommands.size > 0 : isObject(options.subCommands) && Object.keys(options.subCommands).length > 0 : false;
|
|
@@ -1146,16 +1403,34 @@ function resolveCommandTree(tokens, entry, options) {
|
|
|
1146
1403
|
if (cmd == null) {
|
|
1147
1404
|
if (depth === 0) {
|
|
1148
1405
|
if (options.fallbackToEntry) return resolveAsEntry();
|
|
1406
|
+
const parent = resolveAsEntry();
|
|
1149
1407
|
return {
|
|
1150
1408
|
commandName: token,
|
|
1151
1409
|
callMode: "unexpected",
|
|
1152
1410
|
commandPath: [],
|
|
1153
1411
|
depth: 0,
|
|
1154
1412
|
omitted: false,
|
|
1155
|
-
levelSubCommands: void 0
|
|
1413
|
+
levelSubCommands: void 0,
|
|
1414
|
+
unresolvedCommandName: token,
|
|
1415
|
+
parentCommand: parent.command,
|
|
1416
|
+
parentCommandName: parent.commandName,
|
|
1417
|
+
parentCommandPath: [],
|
|
1418
|
+
parentSubCommands: options.subCommands
|
|
1156
1419
|
};
|
|
1157
1420
|
}
|
|
1158
|
-
|
|
1421
|
+
return {
|
|
1422
|
+
commandName: token,
|
|
1423
|
+
callMode: "unexpected",
|
|
1424
|
+
commandPath: [...commandPath],
|
|
1425
|
+
depth,
|
|
1426
|
+
omitted: false,
|
|
1427
|
+
levelSubCommands: void 0,
|
|
1428
|
+
unresolvedCommandName: token,
|
|
1429
|
+
parentCommand: resolvedCommand,
|
|
1430
|
+
parentCommandName: resolvedName,
|
|
1431
|
+
parentCommandPath: [...commandPath],
|
|
1432
|
+
parentSubCommands: currentSubCommands
|
|
1433
|
+
};
|
|
1159
1434
|
}
|
|
1160
1435
|
let resolved = cmd;
|
|
1161
1436
|
if (typeof cmd === "function" && cmd.commandName == null) resolved = Object.assign((...args) => cmd(...args), cmd, { commandName: token });
|
|
@@ -1189,6 +1464,16 @@ function resolveCommandTree(tokens, entry, options) {
|
|
|
1189
1464
|
levelSubCommands
|
|
1190
1465
|
};
|
|
1191
1466
|
}
|
|
1467
|
+
function createCommandNotFoundError(resolved) {
|
|
1468
|
+
const commandName = resolved.unresolvedCommandName || resolved.commandName || "";
|
|
1469
|
+
return new CommandNotFoundError(`Command not found: ${commandName}`, {
|
|
1470
|
+
code: CommandNotFoundErrorKeys.notFound,
|
|
1471
|
+
values: { commandName },
|
|
1472
|
+
commandName,
|
|
1473
|
+
candidates: [...resolved.parentSubCommands?.keys() || []],
|
|
1474
|
+
commandPath: resolved.parentCommandPath || []
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1192
1477
|
function resolveEntryName(entry) {
|
|
1193
1478
|
return isLazyCommand(entry) ? entry.commandName || "(anonymous)" : entry.name || "(anonymous)";
|
|
1194
1479
|
}
|
|
@@ -1202,7 +1487,11 @@ function getPluginExtensions(plugins) {
|
|
|
1202
1487
|
return extensions;
|
|
1203
1488
|
}
|
|
1204
1489
|
async function executeCommand(cmd, ctx, decorators) {
|
|
1205
|
-
const
|
|
1490
|
+
const commandRunner = cmd.run || NOOP;
|
|
1491
|
+
const baseRunner = (ctx) => {
|
|
1492
|
+
if (hasPriorityValidationError(ctx.validationError)) throw ctx.validationError;
|
|
1493
|
+
return commandRunner(ctx);
|
|
1494
|
+
};
|
|
1206
1495
|
const decoratedRunner = decorators.reduceRight((runner, decorator) => decorator(runner), baseRunner);
|
|
1207
1496
|
try {
|
|
1208
1497
|
if (ctx.env.onBeforeCommand) await ctx.env.onBeforeCommand(ctx);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gunshi/bone",
|
|
3
3
|
"description": "gunshi minimum",
|
|
4
|
-
"version": "0.35.
|
|
4
|
+
"version": "0.35.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "kazuya kawaguchi",
|
|
7
7
|
"email": "kawakazu80@gmail.com"
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
"jsr-exports-lint": "^0.4.2",
|
|
57
57
|
"publint": "^0.3.20",
|
|
58
58
|
"tsdown": "0.21.0",
|
|
59
|
-
"@gunshi/definition": "0.35.
|
|
60
|
-
"@gunshi/plugin-global": "0.35.
|
|
61
|
-
"@gunshi/plugin-renderer": "0.35.
|
|
62
|
-
"gunshi": "0.35.
|
|
59
|
+
"@gunshi/definition": "0.35.2",
|
|
60
|
+
"@gunshi/plugin-global": "0.35.2",
|
|
61
|
+
"@gunshi/plugin-renderer": "0.35.2",
|
|
62
|
+
"gunshi": "0.35.2"
|
|
63
63
|
},
|
|
64
64
|
"scripts": {
|
|
65
65
|
"build": "tsdown",
|