@gunshi/plugin-renderer 1.0.0-rc.5 → 1.0.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/index.d.ts +7 -2
- package/lib/index.js +87 -60
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@ EXAMPLES:
|
|
|
115
115
|
|
|
116
116
|
- **`renderHeader(ctx: CommandContext): Promise<string>`**: Renders the command header section
|
|
117
117
|
- **`renderUsage(ctx: CommandContext): Promise<string>`**: Renders the complete usage/help information
|
|
118
|
-
- **`renderValidationErrors(ctx: CommandContext, error: AggregateError): Promise<string>`**: Renders validation errors
|
|
118
|
+
- **`renderValidationErrors(ctx: CommandContext, error: AggregateError): Promise<string>`**: Renders validation errors. With `--json`, the global options plugin prints the validation errors as one line of JSON, and does not call the renderer of the validation errors
|
|
119
119
|
|
|
120
120
|
## 🧩 Context Extensions
|
|
121
121
|
|
package/lib/index.d.ts
CHANGED
|
@@ -581,11 +581,12 @@ type CommonArgType = {
|
|
|
581
581
|
};
|
|
582
582
|
};
|
|
583
583
|
declare const COMMON_ARGS: CommonArgType;
|
|
584
|
-
declare const COMMAND_BUILTIN_RESOURCE_KEYS: readonly ["USAGE", "COMMAND", "SUBCOMMAND", "COMMANDS", "ARGUMENTS", "OPTIONS", "EXAMPLES", "FORMORE", "NEGATABLE", "DEFAULT", "CHOICES"];
|
|
584
|
+
declare const COMMAND_BUILTIN_RESOURCE_KEYS: readonly ["USAGE", "COMMAND", "SUBCOMMAND", "COMMANDS", "ARGUMENTS", "OPTIONS", "EXAMPLES", "FORMORE", "NEGATABLE", "DEFAULT", "CHOICES", "CAUSEDBY"];
|
|
585
585
|
declare const ARG_ERROR_RESOURCE_KEYS: readonly ["err:arg:required-option", "err:arg:required-positional", "err:arg:invalid-type", "err:arg:invalid-choice", "err:arg:custom-parse", "err:arg:unknown-option", "err:arg:unexpected-value", "err:arg:missing-value", "err:arg:conflict", "err:arg:invalid-default"];
|
|
586
586
|
declare const ARG_ERROR_HINT_RESOURCE_KEYS: readonly ["err:arg:missing-value:hint"];
|
|
587
587
|
declare const COMMAND_ERROR_RESOURCE_KEYS: readonly ["err:cmd:not-found", "err:cmd:ambiguous", "err:cmd:inconsistent-options", "err:cmd:lazy-schema-mismatch"];
|
|
588
588
|
declare const SUGGESTION_ERROR_RESOURCE_KEYS: readonly ["err:suggestion:did-you-mean"];
|
|
589
|
+
declare const JSON_ERROR_RESOURCE_KEYS: readonly ["err:json:not-supported", "err:json:invalid-result", "err:json:unserializable-result"];
|
|
589
590
|
//#endregion
|
|
590
591
|
//#region ../shared/src/types.d.ts
|
|
591
592
|
type RemoveIndexSignature<T> = { [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]; };
|
|
@@ -625,10 +626,14 @@ type CommandErrorResourceKeys = (typeof COMMAND_ERROR_RESOURCE_KEYS)[number];
|
|
|
625
626
|
* Suggestion error resource keys.
|
|
626
627
|
*/
|
|
627
628
|
type SuggestionErrorResourceKeys = (typeof SUGGESTION_ERROR_RESOURCE_KEYS)[number];
|
|
629
|
+
/**
|
|
630
|
+
* Resource keys of the errors of the JSON output.
|
|
631
|
+
*/
|
|
632
|
+
type JsonErrorResourceKeys = (typeof JSON_ERROR_RESOURCE_KEYS)[number];
|
|
628
633
|
/**
|
|
629
634
|
* Error resource keys.
|
|
630
635
|
*/
|
|
631
|
-
type ErrorResourceKeys = ArgErrorResourceKeys | ArgErrorHintResourceKeys | CommandErrorResourceKeys | SuggestionErrorResourceKeys;
|
|
636
|
+
type ErrorResourceKeys = ArgErrorResourceKeys | ArgErrorHintResourceKeys | CommandErrorResourceKeys | SuggestionErrorResourceKeys | JsonErrorResourceKeys;
|
|
632
637
|
/**
|
|
633
638
|
* Command built-in keys.
|
|
634
639
|
*/
|
package/lib/index.js
CHANGED
|
@@ -150,8 +150,13 @@ var en_US_default = {
|
|
|
150
150
|
NEGATABLE: "Negatable of",
|
|
151
151
|
DEFAULT: "default",
|
|
152
152
|
CHOICES: "choices",
|
|
153
|
+
CAUSEDBY: "Caused by",
|
|
153
154
|
help: "Display this help message",
|
|
154
155
|
version: "Display this version",
|
|
156
|
+
json: "Output the result as JSON",
|
|
157
|
+
"json:invalid-arguments": "{$count} arguments are invalid",
|
|
158
|
+
"json:next:help": "See the arguments of the command",
|
|
159
|
+
"json:next:help-placeholder": "Replace <COMMAND> with how you ran this CLI, to see the arguments of the command",
|
|
155
160
|
"err:arg:required-option": "Option {$displayName} is required",
|
|
156
161
|
"err:arg:required-positional": "Positional argument {$displayName} is required",
|
|
157
162
|
"err:arg:invalid-type": "Invalid value for {$displayName}: expected {$expected}",
|
|
@@ -167,6 +172,9 @@ var en_US_default = {
|
|
|
167
172
|
"err:cmd:ambiguous": "The command is ambiguous. Move options after the command name: {$candidatePaths}",
|
|
168
173
|
"err:cmd:inconsistent-options": "The options do not identify a consistent command. Move options after the command name.",
|
|
169
174
|
"err:cmd:lazy-schema-mismatch": "The lazy command definition does not match its loaded options. Declare leading options in the lazy definition or move options after the command name: {$commandPath}",
|
|
175
|
+
"err:json:not-supported": "This command does not support --json",
|
|
176
|
+
"err:json:invalid-result": "The command finished, but did not return an object to output as JSON. A plugin that decorates the command may have dropped its result",
|
|
177
|
+
"err:json:unserializable-result": "The command finished, but its result cannot be converted to JSON: {$message}",
|
|
170
178
|
"err:suggestion:did-you-mean": "Did you mean {$name}?"
|
|
171
179
|
};
|
|
172
180
|
//#endregion
|
|
@@ -344,6 +352,84 @@ function formatResource(resource, values = Object.create(null)) {
|
|
|
344
352
|
});
|
|
345
353
|
}
|
|
346
354
|
//#endregion
|
|
355
|
+
//#region ../shared/src/validation.ts
|
|
356
|
+
/**
|
|
357
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
358
|
+
* @license MIT
|
|
359
|
+
*/
|
|
360
|
+
const i18nPluginId$1 = namespacedId("i18n");
|
|
361
|
+
const missingValueHintKey = "err:arg:missing-value:hint";
|
|
362
|
+
/**
|
|
363
|
+
* Localize the message of a validation error, as gunshi shows it to the user: with the i18n plugin when
|
|
364
|
+
* it is installed, and otherwise the message of the error.
|
|
365
|
+
*
|
|
366
|
+
* NOTE(kazupon): the renderer plugin renders the validation errors for people, and the global options
|
|
367
|
+
* plugin prints them as JSON with `--json`. Both take the messages from here, so that a message is the
|
|
368
|
+
* same in both.
|
|
369
|
+
*
|
|
370
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
371
|
+
* @param error - A validation error: an {@link ArgsValidationError}, a command-not-found error, or a
|
|
372
|
+
* command-resolution error
|
|
373
|
+
* @returns The localized message
|
|
374
|
+
*/
|
|
375
|
+
async function localizeValidationError(ctx, error) {
|
|
376
|
+
if (isCommandNotFoundError(error) && error.code) {
|
|
377
|
+
const message = await localize(ctx, error.code, error.values);
|
|
378
|
+
if (message && message !== error.code) return message;
|
|
379
|
+
}
|
|
380
|
+
if (isCommandResolutionError(error) && error.code) {
|
|
381
|
+
const message = await localize(ctx, error.code, error.values);
|
|
382
|
+
if (message && message !== error.code) return message;
|
|
383
|
+
}
|
|
384
|
+
if (isArgsValidationError(error) && error.code) {
|
|
385
|
+
const values = await resolveValidationValues(ctx, error);
|
|
386
|
+
const message = await localize(ctx, error.code, values);
|
|
387
|
+
if (message && message !== error.code) {
|
|
388
|
+
const hint = await localizeHint(ctx, error, values);
|
|
389
|
+
return hint ? `${message} ${hint}` : message;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return error.message;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Localize the hint that args-tokens adds to the message of an error.
|
|
396
|
+
*
|
|
397
|
+
* A translation replaces the whole message, hint included, so the hint is translated by a key of
|
|
398
|
+
* its own and follows the translated error. args-tokens gives one today: for an option given without
|
|
399
|
+
* a value, when the argument after it may be the value, the long form with `=` (`--port=-5` for
|
|
400
|
+
* `--port -5`), in `values.next` and `values.suggestion`.
|
|
401
|
+
*
|
|
402
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
403
|
+
* @param error - An args validation error
|
|
404
|
+
* @param values - The values of the error, as given to its translation
|
|
405
|
+
* @returns The localized hint, or `undefined` when the error has none.
|
|
406
|
+
*/
|
|
407
|
+
async function localizeHint(ctx, error, values) {
|
|
408
|
+
if (error.code !== ArgsValidationErrorKeys.missingValue || typeof values.next !== "string" || typeof values.suggestion !== "string") return;
|
|
409
|
+
const hint = await localize(ctx, missingValueHintKey, values);
|
|
410
|
+
return hint && hint !== missingValueHintKey ? hint : void 0;
|
|
411
|
+
}
|
|
412
|
+
async function resolveValidationValues(ctx, error) {
|
|
413
|
+
if (error.code !== ArgsValidationErrorKeys.customParse) return error.values;
|
|
414
|
+
const { reasonKey, reasonValues } = error.values;
|
|
415
|
+
if (typeof reasonKey !== "string") return error.values;
|
|
416
|
+
const key = resolveKey(reasonKey, ctx.name);
|
|
417
|
+
const localizedReason = await localize(ctx, key, toRecord(reasonValues));
|
|
418
|
+
if (!localizedReason || localizedReason === key) return error.values;
|
|
419
|
+
return {
|
|
420
|
+
...error.values,
|
|
421
|
+
reason: localizedReason
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
async function localize(ctx, key, values) {
|
|
425
|
+
const i18n = ctx.extensions?.[i18nPluginId$1];
|
|
426
|
+
if (i18n?.translate) return i18n.translate(key, values);
|
|
427
|
+
return key;
|
|
428
|
+
}
|
|
429
|
+
function toRecord(value) {
|
|
430
|
+
return value != null && typeof value === "object" ? value : Object.create(null);
|
|
431
|
+
}
|
|
432
|
+
//#endregion
|
|
347
433
|
//#region ../shared/src/index.ts
|
|
348
434
|
/**
|
|
349
435
|
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
@@ -795,8 +881,6 @@ function isRequiredSinglePositionalArg(arg) {
|
|
|
795
881
|
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
796
882
|
* @license MIT
|
|
797
883
|
*/
|
|
798
|
-
const i18nPluginId$1 = namespacedId("i18n");
|
|
799
|
-
const missingValueHintKey = "err:arg:missing-value:hint";
|
|
800
884
|
/**
|
|
801
885
|
* Render the validation errors.
|
|
802
886
|
*
|
|
@@ -806,66 +890,9 @@ const missingValueHintKey = "err:arg:missing-value:hint";
|
|
|
806
890
|
*/
|
|
807
891
|
async function renderValidationErrors(ctx, error) {
|
|
808
892
|
const messages = [];
|
|
809
|
-
for (const err of error.errors) messages.push(await
|
|
893
|
+
for (const err of error.errors) messages.push(await localizeValidationError(ctx, err));
|
|
810
894
|
return messages.join("\n");
|
|
811
895
|
}
|
|
812
|
-
async function renderValidationError(ctx, error) {
|
|
813
|
-
if (isCommandNotFoundError(error) && error.code) {
|
|
814
|
-
const message = await localize(ctx, error.code, error.values);
|
|
815
|
-
if (message && message !== error.code) return message;
|
|
816
|
-
}
|
|
817
|
-
if (isCommandResolutionError(error) && error.code) {
|
|
818
|
-
const message = await localize(ctx, error.code, error.values);
|
|
819
|
-
if (message && message !== error.code) return message;
|
|
820
|
-
}
|
|
821
|
-
if (isArgsValidationError(error) && error.code) {
|
|
822
|
-
const values = await resolveValidationValues(ctx, error);
|
|
823
|
-
const message = await localize(ctx, error.code, values);
|
|
824
|
-
if (message && message !== error.code) {
|
|
825
|
-
const hint = await localizeHint(ctx, error, values);
|
|
826
|
-
return hint ? `${message} ${hint}` : message;
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
return error.message;
|
|
830
|
-
}
|
|
831
|
-
/**
|
|
832
|
-
* Localize the hint that args-tokens adds to the message of an error.
|
|
833
|
-
*
|
|
834
|
-
* A translation replaces the whole message, hint included, so the hint is translated by a key of
|
|
835
|
-
* its own and follows the translated error. args-tokens gives one today: for an option given without
|
|
836
|
-
* a value, when the argument after it may be the value, the long form with `=` (`--port=-5` for
|
|
837
|
-
* `--port -5`), in `values.next` and `values.suggestion`.
|
|
838
|
-
*
|
|
839
|
-
* @param ctx - A {@link CommandContext | command context}
|
|
840
|
-
* @param error - An args validation error
|
|
841
|
-
* @param values - The values of the error, as given to its translation
|
|
842
|
-
* @returns The localized hint, or `undefined` when the error has none.
|
|
843
|
-
*/
|
|
844
|
-
async function localizeHint(ctx, error, values) {
|
|
845
|
-
if (error.code !== ArgsValidationErrorKeys.missingValue || typeof values.next !== "string" || typeof values.suggestion !== "string") return;
|
|
846
|
-
const hint = await localize(ctx, missingValueHintKey, values);
|
|
847
|
-
return hint && hint !== missingValueHintKey ? hint : void 0;
|
|
848
|
-
}
|
|
849
|
-
async function resolveValidationValues(ctx, error) {
|
|
850
|
-
if (error.code !== ArgsValidationErrorKeys.customParse) return error.values;
|
|
851
|
-
const { reasonKey, reasonValues } = error.values;
|
|
852
|
-
if (typeof reasonKey !== "string") return error.values;
|
|
853
|
-
const key = resolveKey(reasonKey, ctx.name);
|
|
854
|
-
const localizedReason = await localize(ctx, key, toRecord(reasonValues));
|
|
855
|
-
if (!localizedReason || localizedReason === key) return error.values;
|
|
856
|
-
return {
|
|
857
|
-
...error.values,
|
|
858
|
-
reason: localizedReason
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
async function localize(ctx, key, values) {
|
|
862
|
-
const i18n = ctx.extensions?.[i18nPluginId$1];
|
|
863
|
-
if (i18n?.translate) return i18n.translate(key, values);
|
|
864
|
-
return key;
|
|
865
|
-
}
|
|
866
|
-
function toRecord(value) {
|
|
867
|
-
return value != null && typeof value === "object" ? value : Object.create(null);
|
|
868
|
-
}
|
|
869
896
|
//#endregion
|
|
870
897
|
//#region src/index.ts
|
|
871
898
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gunshi/plugin-renderer",
|
|
3
3
|
"description": "usage renderer plugin for gunshi",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.6",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "kazuya kawaguchi",
|
|
7
7
|
"email": "kawakazu80@gmail.com"
|
|
@@ -53,13 +53,13 @@
|
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@gunshi/plugin": "1.0.0-rc.
|
|
56
|
+
"@gunshi/plugin": "1.0.0-rc.6"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@gunshi/plugin-i18n": "1.0.0-rc.
|
|
59
|
+
"@gunshi/plugin-i18n": "1.0.0-rc.6"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@gunshi/shared": "1.0.0-rc.
|
|
62
|
+
"@gunshi/shared": "1.0.0-rc.6",
|
|
63
63
|
"publint": "^0.3.20",
|
|
64
64
|
"tsdown": "0.22.14"
|
|
65
65
|
},
|