@gunshi/plugin-renderer 0.27.0-alpha.8 → 0.27.0-beta.0
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 +20 -45
- package/lib/index.d.ts +69 -74
- package/lib/index.js +250 -93
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @gunshi/plugin-renderer
|
|
2
2
|
|
|
3
|
+
[![Version][npm-version-src]][npm-version-href]
|
|
4
|
+
[![InstallSize][install-size-src]][install-size-src]
|
|
5
|
+
[![JSR][jsr-src]][jsr-href]
|
|
6
|
+
|
|
3
7
|
> usage renderer plugin for gunshi.
|
|
4
8
|
|
|
5
9
|
This plugin provides customizable rendering for CLI help messages, usage information, and validation errors. It automatically formats command descriptions, arguments, options, examples, and error messages in a consistent and readable format.
|
|
@@ -26,8 +30,8 @@ bun add @gunshi/plugin-renderer
|
|
|
26
30
|
## 🚀 Usage
|
|
27
31
|
|
|
28
32
|
```ts
|
|
29
|
-
import renderer from '@gunshi/plugin-renderer'
|
|
30
33
|
import { cli, define } from 'gunshi'
|
|
34
|
+
import renderer from '@gunshi/plugin-renderer'
|
|
31
35
|
|
|
32
36
|
const command = define({
|
|
33
37
|
name: 'deploy',
|
|
@@ -80,7 +84,7 @@ This plugin automatically handles rendering for:
|
|
|
80
84
|
|
|
81
85
|
The plugin provides smart text rendering with automatic fallback:
|
|
82
86
|
|
|
83
|
-
- **With i18n plugin**: Uses translations from the i18n
|
|
87
|
+
- **With i18n plugin**: Uses translations from the `@gunshi/plugin-i18n`
|
|
84
88
|
- **Without i18n plugin**: Falls back to default English messages and descriptions
|
|
85
89
|
|
|
86
90
|
### Rendered Example
|
|
@@ -130,8 +134,8 @@ Available extensions:
|
|
|
130
134
|
### Usage Example
|
|
131
135
|
|
|
132
136
|
```ts
|
|
133
|
-
import renderer from '@gunshi/plugin-renderer'
|
|
134
137
|
import { cli, define } from 'gunshi'
|
|
138
|
+
import renderer, { pluginId as rendererId } from '@gunshi/plugin-renderer'
|
|
135
139
|
|
|
136
140
|
const deploy = define({
|
|
137
141
|
name: 'deploy',
|
|
@@ -153,7 +157,7 @@ const entry = define({
|
|
|
153
157
|
name: 'tools',
|
|
154
158
|
run: async ctx => {
|
|
155
159
|
// Access renderer extensions
|
|
156
|
-
const { text, loadCommands } = ctx.extensions[
|
|
160
|
+
const { text, loadCommands } = ctx.extensions[rendererId]
|
|
157
161
|
|
|
158
162
|
// Render built-in message
|
|
159
163
|
const usageHeader = await text('_:USAGE') // "USAGE" or translated
|
|
@@ -191,58 +195,21 @@ await cli(process.argv.slice(2), command, {
|
|
|
191
195
|
|
|
192
196
|
### Integration with i18n Plugin
|
|
193
197
|
|
|
194
|
-
The renderer plugin has an optional dependency on the i18n
|
|
198
|
+
The renderer plugin has an optional dependency on the `@gunshi/plugin-i18n`. When both plugins are used together, all rendered text automatically uses translations:
|
|
195
199
|
|
|
196
200
|
```ts
|
|
197
|
-
import renderer from '@gunshi/plugin-renderer'
|
|
198
|
-
import i18n from '@gunshi/plugin-i18n'
|
|
199
|
-
import resources from '@gunshi/resources'
|
|
200
201
|
import { cli } from 'gunshi'
|
|
201
|
-
|
|
202
|
-
await cli(args, command, {
|
|
203
|
-
plugins: [
|
|
204
|
-
i18n({
|
|
205
|
-
locale: 'ja-JP',
|
|
206
|
-
resources // Uses built-in resources from @gunshi/resources
|
|
207
|
-
}),
|
|
208
|
-
renderer() // Will use Japanese translations
|
|
209
|
-
]
|
|
210
|
-
})
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
#### With Custom Resources
|
|
214
|
-
|
|
215
|
-
You can extend the built-in resources with your own translations:
|
|
216
|
-
|
|
217
|
-
```ts
|
|
218
202
|
import renderer from '@gunshi/plugin-renderer'
|
|
219
203
|
import i18n from '@gunshi/plugin-i18n'
|
|
220
204
|
import resources from '@gunshi/resources'
|
|
221
|
-
import { cli } from 'gunshi'
|
|
222
|
-
|
|
223
|
-
// Extend built-in resources with custom messages
|
|
224
|
-
const customResources = {
|
|
225
|
-
'en-US': {
|
|
226
|
-
...resources['en-US'],
|
|
227
|
-
// Custom messages for your app
|
|
228
|
-
APP_WELCOME: 'Welcome to My CLI Tool!',
|
|
229
|
-
APP_PROCESSING: 'Processing your request...'
|
|
230
|
-
},
|
|
231
|
-
'ja-JP': {
|
|
232
|
-
...resources['ja-JP'],
|
|
233
|
-
// Custom messages in Japanese
|
|
234
|
-
APP_WELCOME: '私のCLIツールへようこそ!',
|
|
235
|
-
APP_PROCESSING: 'リクエストを処理しています...'
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
205
|
|
|
239
206
|
await cli(args, command, {
|
|
240
207
|
plugins: [
|
|
241
208
|
i18n({
|
|
242
209
|
locale: 'ja-JP',
|
|
243
|
-
resources
|
|
210
|
+
resources // Uses built-in resources from `@gunshi/resources`
|
|
244
211
|
}),
|
|
245
|
-
renderer() // Will use Japanese translations
|
|
212
|
+
renderer() // Will use Japanese translations
|
|
246
213
|
]
|
|
247
214
|
})
|
|
248
215
|
```
|
|
@@ -286,4 +253,12 @@ See the [API References](./docs/index.md)
|
|
|
286
253
|
|
|
287
254
|
## ©️ License
|
|
288
255
|
|
|
289
|
-
[MIT](http://opensource.org/licenses/MIT)
|
|
256
|
+
[MIT](http://opensource.org/licenses/MIT)
|
|
257
|
+
|
|
258
|
+
<!-- Badges -->
|
|
259
|
+
|
|
260
|
+
[npm-version-src]: https://img.shields.io/npm/v/@gunshi/plugin-renderer?style=flat
|
|
261
|
+
[npm-version-href]: https://npmjs.com/package/@gunshi/plugin-renderer@alpha
|
|
262
|
+
[jsr-src]: https://jsr.io/badges/@gunshi/plugin-renderer
|
|
263
|
+
[jsr-href]: https://jsr.io/@gunshi/plugin-renderer
|
|
264
|
+
[install-size-src]: https://pkg-size.dev/badge/install/101075
|
package/lib/index.d.ts
CHANGED
|
@@ -1,53 +1,11 @@
|
|
|
1
|
-
import { Command, CommandContext, DefaultGunshiParams, GunshiParams, PluginWithExtension } from "@gunshi/plugin";
|
|
2
|
-
import { Args
|
|
1
|
+
import { Args, Command, CommandContext, DefaultGunshiParams, GunshiParams, PluginWithExtension } from "@gunshi/plugin";
|
|
2
|
+
import { Args as Args$1 } from "args-tokens";
|
|
3
3
|
|
|
4
|
-
//#region
|
|
5
|
-
|
|
6
|
-
//#endregion
|
|
7
|
-
//#region ../gunshi/src/types.d.ts
|
|
8
|
-
/**
|
|
9
|
-
* Extend command context type. This type is used to extend the command context with additional properties at {@link CommandContext.extensions}.
|
|
10
|
-
* @since v0.27.0
|
|
11
|
-
*/
|
|
12
|
-
type ExtendContext = Record<string, unknown>;
|
|
13
|
-
/**
|
|
14
|
-
* Gunshi unified parameter type.
|
|
15
|
-
* This type combines both argument definitions and command context extensions.
|
|
16
|
-
* @since v0.27.0
|
|
17
|
-
*/
|
|
18
|
-
interface GunshiParams$1<P extends {
|
|
19
|
-
args?: Args;
|
|
20
|
-
extensions?: ExtendContext;
|
|
21
|
-
} = {
|
|
22
|
-
args: Args;
|
|
23
|
-
extensions: {};
|
|
24
|
-
}> {
|
|
25
|
-
/**
|
|
26
|
-
* Command argument definitions
|
|
27
|
-
*/
|
|
28
|
-
args: P extends {
|
|
29
|
-
args: infer A extends Args;
|
|
30
|
-
} ? A : Args;
|
|
31
|
-
/**
|
|
32
|
-
* Command context extensions
|
|
33
|
-
*/
|
|
34
|
-
extensions: P extends {
|
|
35
|
-
extensions: infer E extends ExtendContext;
|
|
36
|
-
} ? E : {};
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Default Gunshi parameters
|
|
40
|
-
* @since v0.27.0
|
|
41
|
-
*/
|
|
42
|
-
type DefaultGunshiParams$1 = GunshiParams$1;
|
|
4
|
+
//#region ../shared/src/constants.d.ts
|
|
43
5
|
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
*/
|
|
48
|
-
declare namespace constants_d_exports {
|
|
49
|
-
export { ARG_NEGATABLE_PREFIX, ARG_PREFIX, ARG_PREFIX_AND_KEY_SEPARATOR, BUILD_IN_PREFIX_AND_KEY_SEPARATOR, BUILT_IN_KEY_SEPARATOR, BUILT_IN_PREFIX, COMMAND_BUILTIN_RESOURCE_KEYS, COMMON_ARGS, PLUGIN_PREFIX };
|
|
50
|
-
}
|
|
6
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
51
9
|
/**
|
|
52
10
|
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
53
11
|
* @license MIT
|
|
@@ -56,9 +14,6 @@ declare const BUILT_IN_PREFIX = "_";
|
|
|
56
14
|
declare const PLUGIN_PREFIX = "g";
|
|
57
15
|
declare const ARG_PREFIX = "arg";
|
|
58
16
|
declare const BUILT_IN_KEY_SEPARATOR = ":";
|
|
59
|
-
declare const BUILD_IN_PREFIX_AND_KEY_SEPARATOR: string;
|
|
60
|
-
declare const ARG_PREFIX_AND_KEY_SEPARATOR: string;
|
|
61
|
-
declare const ARG_NEGATABLE_PREFIX = "no-";
|
|
62
17
|
type CommonArgType = {
|
|
63
18
|
readonly help: {
|
|
64
19
|
readonly type: 'boolean';
|
|
@@ -76,10 +31,17 @@ declare const COMMAND_BUILTIN_RESOURCE_KEYS: readonly ["USAGE", "COMMAND", "SUBC
|
|
|
76
31
|
//#endregion
|
|
77
32
|
//#region ../shared/src/types.d.ts
|
|
78
33
|
type RemoveIndexSignature<T> = { [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K] };
|
|
34
|
+
/**
|
|
35
|
+
* Make all properties in T deeply writeable (not readonly)
|
|
36
|
+
*/
|
|
37
|
+
|
|
79
38
|
/**
|
|
80
39
|
* Remove index signature from object or record type.
|
|
81
40
|
*/
|
|
82
41
|
type RemovedIndex<T> = RemoveIndexSignature<{ [K in keyof T]: T[K] }>;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a key on {@link Args}.
|
|
44
|
+
*/
|
|
83
45
|
type KeyOfArgs<A extends Args$1> = keyof A | { [K in keyof A]: A[K]['type'] extends 'boolean' ? A[K]['negatable'] extends true ? `no-${Extract<K, string>}` : never : never }[keyof A];
|
|
84
46
|
/**
|
|
85
47
|
* Generate a namespaced key.
|
|
@@ -88,47 +50,75 @@ type GenerateNamespacedKey<Key extends string, Prefixed extends string = typeof
|
|
|
88
50
|
/**
|
|
89
51
|
* Command i18n built-in arguments keys.
|
|
90
52
|
*/
|
|
91
|
-
type CommandBuiltinArgsKeys = keyof
|
|
53
|
+
type CommandBuiltinArgsKeys = keyof typeof COMMON_ARGS;
|
|
92
54
|
/**
|
|
93
55
|
* Command i18n built-in resource keys.
|
|
94
56
|
*/
|
|
95
|
-
type CommandBuiltinResourceKeys = (typeof
|
|
57
|
+
type CommandBuiltinResourceKeys = (typeof COMMAND_BUILTIN_RESOURCE_KEYS)[number];
|
|
96
58
|
/**
|
|
97
|
-
*
|
|
59
|
+
* Built-in resource keys.
|
|
98
60
|
*/
|
|
99
61
|
type BuiltinResourceKeys = CommandBuiltinArgsKeys | CommandBuiltinResourceKeys;
|
|
100
62
|
/**
|
|
101
|
-
* Command
|
|
102
|
-
* The command i18n built-in keys are used by the i18n plugin for translation.
|
|
63
|
+
* Command built-in keys.
|
|
103
64
|
*/
|
|
104
|
-
type CommandBuiltinKeys = GenerateNamespacedKey<BuiltinResourceKeys
|
|
65
|
+
type CommandBuiltinKeys = GenerateNamespacedKey<BuiltinResourceKeys>;
|
|
105
66
|
/**
|
|
106
67
|
* Command i18n option keys.
|
|
107
68
|
* The command i18n option keys are used by the i18n plugin for translation.
|
|
108
69
|
*/
|
|
109
|
-
type CommandArgKeys<A extends Args$1
|
|
70
|
+
type CommandArgKeys<A extends Args$1, C = {}, K extends string = GenerateNamespacedKey<Extract<KeyOfArgs<RemovedIndex<A>>, string>, typeof ARG_PREFIX>> = C extends {
|
|
71
|
+
name: infer N;
|
|
72
|
+
} ? (N extends string ? GenerateNamespacedKey<K, N> : K) : K;
|
|
110
73
|
/**
|
|
111
|
-
*
|
|
74
|
+
* Resolve translation keys for command context.
|
|
112
75
|
*/
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
76
|
+
type ResolveTranslationKeys<A extends Args$1, C = {},
|
|
77
|
+
// for CommandContext
|
|
78
|
+
E extends Record<string, string> = {},
|
|
79
|
+
// for extended resources
|
|
80
|
+
R extends string = keyof RemovedIndex<E>, T extends string = (C extends {
|
|
81
|
+
name: infer N;
|
|
82
|
+
} ? N extends string ? GenerateNamespacedKey<R, N> : R : R | CommandBuiltinKeys), O = CommandArgKeys<A, C>> = CommandBuiltinKeys | O | T;
|
|
118
83
|
//#endregion
|
|
119
84
|
//#region src/types.d.ts
|
|
85
|
+
/**
|
|
86
|
+
* The unique identifier for usage renderer plugin.
|
|
87
|
+
*/
|
|
88
|
+
declare const pluginId: GenerateNamespacedKey<'renderer', typeof PLUGIN_PREFIX>;
|
|
89
|
+
/**
|
|
90
|
+
* Type representing the unique identifier for usage renderer plugin.
|
|
91
|
+
*/
|
|
92
|
+
type PluginId = typeof pluginId;
|
|
120
93
|
/**
|
|
121
94
|
* Extended command context which provides utilities via usage renderer plugin.
|
|
122
95
|
* These utilities are available via `CommandContext.extensions['g:renderer']`.
|
|
96
|
+
*
|
|
97
|
+
* @typeParam G - A type extending {@link GunshiParams} to specify the shape of command parameters.
|
|
123
98
|
*/
|
|
124
|
-
interface
|
|
99
|
+
interface UsageRendererExtension<G extends GunshiParams<any> = DefaultGunshiParams> {
|
|
125
100
|
/**
|
|
126
|
-
* Render the text message
|
|
101
|
+
* Render the text message.
|
|
102
|
+
*
|
|
103
|
+
* @typeParam A - The type of {@linkcode Args | arguments} defined in the command parameters.
|
|
104
|
+
* @typeParam C - The type representing the command context.
|
|
105
|
+
* @typeParam E - The type representing extended resources for localization.
|
|
106
|
+
*
|
|
107
|
+
* @param key - The translation key to be resolved.
|
|
108
|
+
* @param values - An optional record of values to interpolate into the translation string.
|
|
109
|
+
* @returns The resolved translation string with interpolated values if provided.
|
|
127
110
|
*/
|
|
128
|
-
text:
|
|
111
|
+
text: <A extends Args = G['args'], C = {},
|
|
112
|
+
// for CommandContext
|
|
113
|
+
E extends Record<string, string> = {},
|
|
114
|
+
// for extended resources
|
|
115
|
+
K = ResolveTranslationKeys<A, C, E>>(key: K, values?: Record<string, unknown>) => string;
|
|
129
116
|
/**
|
|
130
117
|
* Load commands
|
|
131
|
-
*
|
|
118
|
+
*
|
|
119
|
+
* @typeParam G - A type extending {@link GunshiParams} to specify the shape of command parameters.
|
|
120
|
+
*
|
|
121
|
+
* @returns A list of commands loaded from the usage renderer plugin.
|
|
132
122
|
*/
|
|
133
123
|
loadCommands: <G extends GunshiParams = DefaultGunshiParams>() => Promise<Command<G>[]>;
|
|
134
124
|
}
|
|
@@ -136,7 +126,8 @@ interface UsageRendererCommandContext<G extends GunshiParams<any> = DefaultGunsh
|
|
|
136
126
|
//#region src/header.d.ts
|
|
137
127
|
/**
|
|
138
128
|
* Render the header.
|
|
139
|
-
*
|
|
129
|
+
*
|
|
130
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
140
131
|
* @returns A rendered header.
|
|
141
132
|
*/
|
|
142
133
|
declare function renderHeader<G extends GunshiParams = DefaultGunshiParams>(ctx: Readonly<CommandContext<G>>): Promise<string>;
|
|
@@ -144,7 +135,8 @@ declare function renderHeader<G extends GunshiParams = DefaultGunshiParams>(ctx:
|
|
|
144
135
|
//#region src/usage.d.ts
|
|
145
136
|
/**
|
|
146
137
|
* Render the usage.
|
|
147
|
-
*
|
|
138
|
+
*
|
|
139
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
148
140
|
* @returns A rendered usage.
|
|
149
141
|
*/
|
|
150
142
|
declare function renderUsage<G extends GunshiParams = DefaultGunshiParams>(ctx: Readonly<CommandContext<G>>): Promise<string>;
|
|
@@ -152,8 +144,9 @@ declare function renderUsage<G extends GunshiParams = DefaultGunshiParams>(ctx:
|
|
|
152
144
|
//#region src/validation.d.ts
|
|
153
145
|
/**
|
|
154
146
|
* Render the validation errors.
|
|
155
|
-
*
|
|
156
|
-
* @param
|
|
147
|
+
*
|
|
148
|
+
* @param _ctx - A {@link CommandContext | command context}
|
|
149
|
+
* @param error - An {@link AggregateError} of option in `args-token` validation
|
|
157
150
|
* @returns A rendered validation error.
|
|
158
151
|
*/
|
|
159
152
|
declare function renderValidationErrors<G extends GunshiParams = DefaultGunshiParams>(_ctx: CommandContext<G>, error: AggregateError): Promise<string>;
|
|
@@ -161,7 +154,9 @@ declare function renderValidationErrors<G extends GunshiParams = DefaultGunshiPa
|
|
|
161
154
|
//#region src/index.d.ts
|
|
162
155
|
/**
|
|
163
156
|
* usage renderer plugin
|
|
157
|
+
*
|
|
158
|
+
* @returns A defined plugin as usage renderer
|
|
164
159
|
*/
|
|
165
|
-
declare function renderer(): PluginWithExtension<
|
|
160
|
+
declare function renderer(): PluginWithExtension<UsageRendererExtension>;
|
|
166
161
|
//#endregion
|
|
167
|
-
export {
|
|
162
|
+
export { PluginId, UsageRendererExtension, renderer as default, pluginId, renderHeader, renderUsage, renderValidationErrors };
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { plugin } from "@gunshi/plugin";
|
|
1
|
+
import { ANONYMOUS_COMMAND_NAME, plugin } from "@gunshi/plugin";
|
|
2
2
|
|
|
3
|
-
//#region ../../node_modules/.pnpm/args-tokens@0.
|
|
3
|
+
//#region ../../node_modules/.pnpm/args-tokens@0.23.0/node_modules/args-tokens/lib/utils-1LQrGCWG.js
|
|
4
4
|
/**
|
|
5
5
|
* Entry point of utils.
|
|
6
6
|
*
|
|
@@ -12,15 +12,35 @@ import { plugin } from "@gunshi/plugin";
|
|
|
12
12
|
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
13
13
|
* @license MIT
|
|
14
14
|
*/
|
|
15
|
+
/**
|
|
16
|
+
* Convert a string to kebab-case.
|
|
17
|
+
*
|
|
18
|
+
* @param str - A string to convert
|
|
19
|
+
* @returns Converted string into kebab-case.
|
|
20
|
+
*/
|
|
15
21
|
function kebabnize(str) {
|
|
16
22
|
return str.replace(/[A-Z]/g, (match, offset) => (offset > 0 ? "-" : "") + match.toLowerCase());
|
|
17
23
|
}
|
|
18
24
|
|
|
19
25
|
//#endregion
|
|
20
26
|
//#region ../gunshi/src/utils.ts
|
|
27
|
+
/**
|
|
28
|
+
* Check if the given command is a {@link LazyCommand}.
|
|
29
|
+
*
|
|
30
|
+
* @param cmd - A command to check
|
|
31
|
+
* @returns `true` if the command is a {@link LazyCommand}, otherwise `false
|
|
32
|
+
*/
|
|
21
33
|
function isLazyCommand(cmd) {
|
|
22
34
|
return typeof cmd === "function" && "commandName" in cmd && !!cmd.commandName;
|
|
23
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Resolve a lazy command to a {@link Command}.
|
|
38
|
+
*
|
|
39
|
+
* @param cmd - A {@link Commandable} or {@link LazyCommand} to resolve
|
|
40
|
+
* @param name - Optional name of the command, if not provided, it will use the name from the command itself.
|
|
41
|
+
* @param needRunResolving - Whether to run the resolving function of the lazy command.
|
|
42
|
+
* @returns A resolved {@link Command}
|
|
43
|
+
*/
|
|
24
44
|
async function resolveLazyCommand(cmd, name, needRunResolving = false) {
|
|
25
45
|
let command;
|
|
26
46
|
if (isLazyCommand(cmd)) {
|
|
@@ -53,9 +73,22 @@ async function resolveLazyCommand(cmd, name, needRunResolving = false) {
|
|
|
53
73
|
if (command.name == null && name) command.name = name;
|
|
54
74
|
return deepFreeze(command);
|
|
55
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Create an object with the specified prototype. A shorthand for `Object.create`.
|
|
78
|
+
*
|
|
79
|
+
* @param obj - An object to use as the prototype for the new object. If `null`, it will create an object with no prototype.
|
|
80
|
+
* @returns A new object with the specified prototype
|
|
81
|
+
*/
|
|
56
82
|
function create(obj = null) {
|
|
57
83
|
return Object.create(obj);
|
|
58
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Deep freeze an object, making it immutable.
|
|
87
|
+
*
|
|
88
|
+
* @param obj - The object to freeze
|
|
89
|
+
* @param ignores - Properties to ignore during freezing
|
|
90
|
+
* @returns A frozen object
|
|
91
|
+
*/
|
|
59
92
|
function deepFreeze(obj, ignores = []) {
|
|
60
93
|
if (obj === null || typeof obj !== "object") return obj;
|
|
61
94
|
for (const key of Object.keys(obj)) {
|
|
@@ -125,35 +158,108 @@ var en_US_default = {
|
|
|
125
158
|
|
|
126
159
|
//#endregion
|
|
127
160
|
//#region ../shared/src/utils.ts
|
|
161
|
+
/**
|
|
162
|
+
* Resolve a namespaced key for built-in resources.
|
|
163
|
+
*
|
|
164
|
+
* Built-in keys are prefixed with "_:".
|
|
165
|
+
*
|
|
166
|
+
* @typeParam K - The type of the built-in key to resolve. Defaults to command built-in argument and resource keys.
|
|
167
|
+
*
|
|
168
|
+
* @param key - The built-in key to resolve.
|
|
169
|
+
* @returns Prefixed built-in key.
|
|
170
|
+
*/
|
|
128
171
|
function resolveBuiltInKey(key) {
|
|
129
172
|
return `${BUILT_IN_PREFIX}${BUILT_IN_KEY_SEPARATOR}${key}`;
|
|
130
173
|
}
|
|
131
|
-
|
|
132
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Resolve a namespaced key for argument resources.
|
|
176
|
+
*
|
|
177
|
+
* Argument keys are prefixed with "arg:".
|
|
178
|
+
* If the command name is provided, it will be prefixed with the command name (e.g. "cmd1:arg:foo").
|
|
179
|
+
*
|
|
180
|
+
* @typeParam A - The {@linkcode Args} type extracted from G
|
|
181
|
+
*
|
|
182
|
+
* @param key - The argument key to resolve.
|
|
183
|
+
* @param name - The command name.
|
|
184
|
+
* @returns Prefixed argument key.
|
|
185
|
+
*/
|
|
186
|
+
function resolveArgKey(key, name) {
|
|
187
|
+
return `${name ? `${name}${BUILT_IN_KEY_SEPARATOR}` : ""}${ARG_PREFIX}${BUILT_IN_KEY_SEPARATOR}${key}`;
|
|
133
188
|
}
|
|
134
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Resolve a namespaced key for non-built-in resources.
|
|
191
|
+
*
|
|
192
|
+
* Non-built-in keys are not prefixed with any special characters. If the command name is provided, it will be prefixed with the command name (e.g. "cmd1:foo").
|
|
193
|
+
*
|
|
194
|
+
* @typeParam T - The type of the non-built-in key to resolve. Defaults to string.
|
|
195
|
+
*
|
|
196
|
+
* @param key - The non-built-in key to resolve.
|
|
197
|
+
* @param name - The command name.
|
|
198
|
+
* @returns Prefixed non-built-in key.
|
|
199
|
+
*/
|
|
200
|
+
function resolveKey(key, name) {
|
|
201
|
+
return `${name ? `${name}${BUILT_IN_KEY_SEPARATOR}` : ""}${key}`;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Resolve command examples.
|
|
205
|
+
*
|
|
206
|
+
* @typeParam G - Type parameter extending {@linkcode GunshiParams}
|
|
207
|
+
*
|
|
208
|
+
* @param ctx - A {@linkcode CommandContext | command context}.
|
|
209
|
+
* @param examples - The examples to resolve, which can be a string or a {@linkcode CommandExamplesFetcher | function} that returns a string.
|
|
210
|
+
* @returns A resolved string of examples.
|
|
211
|
+
*/
|
|
212
|
+
async function resolveExamples(ctx, examples) {
|
|
135
213
|
return typeof examples === "string" ? examples : typeof examples === "function" ? await examples(ctx) : "";
|
|
136
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Generate a namespaced key for a plugin.
|
|
217
|
+
*
|
|
218
|
+
* @typeParam K - The type of the plugin id to generate a namespaced key for.
|
|
219
|
+
*
|
|
220
|
+
* @param id - A plugin id to generate a namespaced key.
|
|
221
|
+
* @returns A namespaced key for the plugin.
|
|
222
|
+
*/
|
|
137
223
|
function namespacedId(id) {
|
|
138
224
|
return `${PLUGIN_PREFIX}${BUILT_IN_KEY_SEPARATOR}${id}`;
|
|
139
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Generate a short and long option pair for command arguments.
|
|
228
|
+
*
|
|
229
|
+
* @param schema - The {@linkcode ArgSchema | argument schema} to generate the option pair.
|
|
230
|
+
* @param name - The name of the argument.
|
|
231
|
+
* @param toKebab - Whether to convert the name to kebab-case for display in help text.
|
|
232
|
+
* @returns A string representing the short and long option pair.
|
|
233
|
+
*/
|
|
140
234
|
function makeShortLongOptionPair(schema, name, toKebab) {
|
|
141
|
-
|
|
142
|
-
let key = `--${displayName}`;
|
|
235
|
+
let key = `--${toKebab || schema.toKebab ? kebabnize(name) : name}`;
|
|
143
236
|
if (schema.short) key = `-${schema.short}, ${key}`;
|
|
144
237
|
return key;
|
|
145
238
|
}
|
|
146
239
|
|
|
147
240
|
//#endregion
|
|
148
|
-
//#region ../shared/src/
|
|
241
|
+
//#region ../shared/src/localization.ts
|
|
242
|
+
/**
|
|
243
|
+
* Create a localizable function for a command.
|
|
244
|
+
*
|
|
245
|
+
* This function will resolve the translation key based on the command context and the provided translation function.
|
|
246
|
+
*
|
|
247
|
+
* @typeParam A - The {@linkcode Args} type extracted from Gunshi command.
|
|
248
|
+
* @typeParam C - Additional context type for command localization.
|
|
249
|
+
* @typeParam E - Extended resource keys type.
|
|
250
|
+
*
|
|
251
|
+
* @param ctx - Command context
|
|
252
|
+
* @param cmd - Command
|
|
253
|
+
* @param translate - Translation function
|
|
254
|
+
* @returns Localizable function
|
|
255
|
+
*/
|
|
149
256
|
function localizable(ctx, cmd, translate) {
|
|
150
257
|
async function localize(key, values) {
|
|
151
258
|
if (translate) return translate(key, values);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
let argKey = key.slice(ARG_PREFIX_AND_KEY_SEPARATOR.length);
|
|
259
|
+
if (key.startsWith(BUILD_IN_PREFIX_AND_KEY_SEPARATOR)) return en_US_default[key.slice(BUILD_IN_PREFIX_AND_KEY_SEPARATOR.length)] || key;
|
|
260
|
+
const namaspacedArgKey = resolveKey(ARG_PREFIX_AND_KEY_SEPARATOR, ctx.name);
|
|
261
|
+
if (key.startsWith(namaspacedArgKey)) {
|
|
262
|
+
let argKey = key.slice(namaspacedArgKey.length);
|
|
157
263
|
let negatable = false;
|
|
158
264
|
if (argKey.startsWith(ARG_NEGATABLE_PREFIX)) {
|
|
159
265
|
argKey = argKey.slice(ARG_NEGATABLE_PREFIX.length);
|
|
@@ -162,8 +268,9 @@ function localizable(ctx, cmd, translate) {
|
|
|
162
268
|
const schema = ctx.args[argKey];
|
|
163
269
|
if (!schema) return argKey;
|
|
164
270
|
return negatable && schema.type === "boolean" && schema.negatable ? `${en_US_default["NEGATABLE"]} ${makeShortLongOptionPair(schema, argKey, ctx.toKebab)}` : schema.description || "";
|
|
165
|
-
}
|
|
166
|
-
|
|
271
|
+
}
|
|
272
|
+
if (key === resolveKey("description", ctx.name)) return "";
|
|
273
|
+
else if (key === resolveKey("examples", ctx.name)) return await resolveExamples(ctx, cmd.examples);
|
|
167
274
|
else return key;
|
|
168
275
|
}
|
|
169
276
|
return localize;
|
|
@@ -173,7 +280,8 @@ function localizable(ctx, cmd, translate) {
|
|
|
173
280
|
//#region src/header.ts
|
|
174
281
|
/**
|
|
175
282
|
* Render the header.
|
|
176
|
-
*
|
|
283
|
+
*
|
|
284
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
177
285
|
* @returns A rendered header.
|
|
178
286
|
*/
|
|
179
287
|
function renderHeader(ctx) {
|
|
@@ -193,7 +301,8 @@ const pluginId = namespacedId("renderer");
|
|
|
193
301
|
const COMMON_ARGS_KEYS = Object.keys(COMMON_ARGS);
|
|
194
302
|
/**
|
|
195
303
|
* Render the usage.
|
|
196
|
-
*
|
|
304
|
+
*
|
|
305
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
197
306
|
* @returns A rendered usage.
|
|
198
307
|
*/
|
|
199
308
|
async function renderUsage(ctx) {
|
|
@@ -204,15 +313,16 @@ async function renderUsage(ctx) {
|
|
|
204
313
|
}
|
|
205
314
|
messages.push(...await renderUsageSection(ctx), "");
|
|
206
315
|
if (ctx.omitted && await hasCommands(ctx)) messages.push(...await renderCommandsSection(ctx), "");
|
|
207
|
-
if (hasPositionalArgs(ctx)) messages.push(...await renderPositionalArgsSection(ctx), "");
|
|
208
|
-
if (hasOptionalArgs(ctx)) messages.push(...await renderOptionalArgsSection(ctx), "");
|
|
316
|
+
if (hasPositionalArgs(ctx.args)) messages.push(...await renderPositionalArgsSection(ctx), "");
|
|
317
|
+
if (hasOptionalArgs(ctx.args)) messages.push(...await renderOptionalArgsSection(ctx), "");
|
|
209
318
|
const examples = await renderExamplesSection(ctx);
|
|
210
319
|
if (examples.length > 0) messages.push(...examples, "");
|
|
211
320
|
return messages.join("\n");
|
|
212
321
|
}
|
|
213
322
|
/**
|
|
214
323
|
* Render the positional arguments section
|
|
215
|
-
*
|
|
324
|
+
*
|
|
325
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
216
326
|
* @returns A rendered arguments section
|
|
217
327
|
*/
|
|
218
328
|
async function renderPositionalArgsSection(ctx) {
|
|
@@ -223,7 +333,8 @@ async function renderPositionalArgsSection(ctx) {
|
|
|
223
333
|
}
|
|
224
334
|
/**
|
|
225
335
|
* Render the optional arguments section
|
|
226
|
-
*
|
|
336
|
+
*
|
|
337
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
227
338
|
* @returns A rendered options section
|
|
228
339
|
*/
|
|
229
340
|
async function renderOptionalArgsSection(ctx) {
|
|
@@ -234,12 +345,13 @@ async function renderOptionalArgsSection(ctx) {
|
|
|
234
345
|
}
|
|
235
346
|
/**
|
|
236
347
|
* Render the examples section
|
|
237
|
-
*
|
|
348
|
+
*
|
|
349
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
238
350
|
* @returns A rendered examples section
|
|
239
351
|
*/
|
|
240
352
|
async function renderExamplesSection(ctx) {
|
|
241
353
|
const messages = [];
|
|
242
|
-
const resolvedExamples = await resolveExamples(ctx);
|
|
354
|
+
const resolvedExamples = await resolveExamples$1(ctx);
|
|
243
355
|
if (resolvedExamples) {
|
|
244
356
|
const examples = resolvedExamples.split("\n").map((example) => example.padStart(ctx.env.leftMargin + example.length));
|
|
245
357
|
messages.push(`${await ctx.extensions[pluginId].text(resolveBuiltInKey("EXAMPLES"))}:`, ...examples);
|
|
@@ -248,49 +360,73 @@ async function renderExamplesSection(ctx) {
|
|
|
248
360
|
}
|
|
249
361
|
/**
|
|
250
362
|
* Render the usage section
|
|
251
|
-
*
|
|
363
|
+
*
|
|
364
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
252
365
|
* @returns A rendered usage section
|
|
253
366
|
*/
|
|
254
367
|
async function renderUsageSection(ctx) {
|
|
255
368
|
const messages = [`${await ctx.extensions[pluginId].text(resolveBuiltInKey("USAGE"))}:`];
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
messages.push(defaultCommand.padStart(ctx.env.leftMargin + defaultCommand.length));
|
|
259
|
-
if (await hasCommands(ctx)) {
|
|
260
|
-
const commandsUsage = `${await resolveEntry(ctx)} <${await ctx.extensions[pluginId].text(resolveBuiltInKey("COMMANDS"))}>`;
|
|
261
|
-
messages.push(commandsUsage.padStart(ctx.env.leftMargin + commandsUsage.length));
|
|
262
|
-
}
|
|
263
|
-
} else {
|
|
264
|
-
const usageStr = `${await resolveEntry(ctx)} ${await resolveSubCommand(ctx)} ${[await generateOptionsSymbols(ctx), generatePositionalSymbols(ctx)].filter(Boolean).join(" ")}`;
|
|
265
|
-
messages.push(usageStr.padStart(ctx.env.leftMargin + usageStr.length));
|
|
266
|
-
}
|
|
369
|
+
const usageStr = await makeUsageSymbols(ctx);
|
|
370
|
+
messages.push(usageStr.padStart(ctx.env.leftMargin + usageStr.length));
|
|
267
371
|
return messages;
|
|
268
372
|
}
|
|
373
|
+
async function makeUsageSymbols(ctx) {
|
|
374
|
+
const messages = [await resolveEntry(ctx)];
|
|
375
|
+
if (ctx.omitted) if (await hasCommands(ctx)) messages.push(` [${await ctx.extensions[pluginId].text(resolveBuiltInKey("COMMANDS"))}]`);
|
|
376
|
+
else messages.push(`${ctx.callMode === "subCommand" ? ` ${await resolveSubCommand(ctx)}` : ""}`);
|
|
377
|
+
else messages.push(`${ctx.callMode === "subCommand" ? ` ${await resolveSubCommand(ctx)}` : ""}`);
|
|
378
|
+
const optionsSymbols = await generateOptionsSymbols(ctx, ctx.args);
|
|
379
|
+
if (optionsSymbols) messages.push(" ", optionsSymbols);
|
|
380
|
+
const positionalSymbols = generatePositionalSymbols(ctx.args);
|
|
381
|
+
if (positionalSymbols) messages.push(" ", positionalSymbols);
|
|
382
|
+
return messages.join("");
|
|
383
|
+
}
|
|
269
384
|
/**
|
|
270
385
|
* Render the commands section
|
|
271
|
-
*
|
|
386
|
+
*
|
|
387
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
272
388
|
* @returns A rendered commands section
|
|
273
389
|
*/
|
|
274
390
|
async function renderCommandsSection(ctx) {
|
|
275
391
|
const messages = [`${await ctx.extensions[pluginId].text(resolveBuiltInKey("COMMANDS"))}:`];
|
|
276
392
|
const loadedCommands = await ctx.extensions?.[pluginId].loadCommands() || [];
|
|
277
393
|
const commandMaxLength = Math.max(...loadedCommands.map((cmd) => (cmd.name || "").length));
|
|
278
|
-
const commandsStr = await Promise.all(loadedCommands.map((cmd) => {
|
|
279
|
-
const key = cmd.name || "";
|
|
394
|
+
const commandsStr = await Promise.all(loadedCommands.map(async (cmd) => {
|
|
280
395
|
const desc = cmd.description || "";
|
|
281
|
-
const
|
|
282
|
-
|
|
396
|
+
const optionSymbol = await generateOptionsSymbols(ctx, ctx.args);
|
|
397
|
+
const positionalSymbol = generatePositionalSymbols(ctx.args);
|
|
398
|
+
const commandStr = await makeCommandSymbol(ctx, cmd);
|
|
399
|
+
const symbolLength = desc.length > 0 ? commandMaxLength + optionSymbol.length + positionalSymbol.length : 0;
|
|
400
|
+
const command = `${commandStr.padEnd(symbolLength + ctx.env.middleMargin)}${desc}`;
|
|
401
|
+
return `${command.padStart(ctx.env.leftMargin + command.length)}`;
|
|
283
402
|
}));
|
|
284
403
|
messages.push(...commandsStr, "", `${await ctx.extensions[pluginId].text(resolveBuiltInKey("FORMORE"))}:`);
|
|
285
404
|
messages.push(...loadedCommands.map((cmd) => {
|
|
286
|
-
|
|
405
|
+
let commandStr = cmd.entry ? "" : cmd.name || "";
|
|
406
|
+
if (commandStr) commandStr += " ";
|
|
407
|
+
const commandHelp = `${ctx.env.name} ${commandStr}--help`;
|
|
287
408
|
return `${commandHelp.padStart(ctx.env.leftMargin + commandHelp.length)}`;
|
|
288
409
|
}));
|
|
289
410
|
return messages;
|
|
290
411
|
}
|
|
412
|
+
async function makeCommandSymbol(ctx, cmd) {
|
|
413
|
+
const optionSymbol = await generateOptionsSymbols(ctx, ctx.args);
|
|
414
|
+
const positionalSymbol = generatePositionalSymbols(ctx.args);
|
|
415
|
+
let commandStr = cmd.entry ? cmd.name === void 0 || cmd.name === ANONYMOUS_COMMAND_NAME ? "" : `[${cmd.name}]` : cmd.name || "";
|
|
416
|
+
if (optionSymbol) {
|
|
417
|
+
if (commandStr) commandStr += " ";
|
|
418
|
+
commandStr += `${optionSymbol}`;
|
|
419
|
+
}
|
|
420
|
+
if (positionalSymbol) {
|
|
421
|
+
if (commandStr) commandStr += " ";
|
|
422
|
+
commandStr += `${positionalSymbol}`;
|
|
423
|
+
}
|
|
424
|
+
return commandStr;
|
|
425
|
+
}
|
|
291
426
|
/**
|
|
292
427
|
* Resolve the entry command name
|
|
293
|
-
*
|
|
428
|
+
*
|
|
429
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
294
430
|
* @returns The entry command name
|
|
295
431
|
*/
|
|
296
432
|
async function resolveEntry(ctx) {
|
|
@@ -298,7 +434,8 @@ async function resolveEntry(ctx) {
|
|
|
298
434
|
}
|
|
299
435
|
/**
|
|
300
436
|
* Resolve the sub command name
|
|
301
|
-
*
|
|
437
|
+
*
|
|
438
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
302
439
|
* @returns The sub command name
|
|
303
440
|
*/
|
|
304
441
|
async function resolveSubCommand(ctx) {
|
|
@@ -306,67 +443,75 @@ async function resolveSubCommand(ctx) {
|
|
|
306
443
|
}
|
|
307
444
|
/**
|
|
308
445
|
* Resolve the command description
|
|
309
|
-
*
|
|
446
|
+
*
|
|
447
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
310
448
|
* @returns resolved command description
|
|
311
449
|
*/
|
|
312
450
|
async function resolveDescription(ctx) {
|
|
313
|
-
return await ctx.extensions[pluginId].text("description") || ctx.description || "";
|
|
451
|
+
return await ctx.extensions[pluginId].text(resolveKey("description", ctx.name)) || ctx.description || "";
|
|
314
452
|
}
|
|
315
453
|
/**
|
|
316
454
|
* Resolve the command examples
|
|
317
|
-
*
|
|
455
|
+
*
|
|
456
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
318
457
|
* @returns resolved command examples, if not resolved, return empty string
|
|
319
458
|
*/
|
|
320
|
-
async function resolveExamples(ctx) {
|
|
321
|
-
const ret = await ctx.extensions[pluginId].text("examples");
|
|
459
|
+
async function resolveExamples$1(ctx) {
|
|
460
|
+
const ret = await ctx.extensions[pluginId].text(resolveKey("examples", ctx.name));
|
|
322
461
|
if (ret) return ret;
|
|
323
462
|
const command = ctx.env.subCommands?.get(ctx.name || "");
|
|
324
|
-
return await resolveExamples
|
|
463
|
+
return await resolveExamples(ctx, command?.examples);
|
|
325
464
|
}
|
|
326
465
|
/**
|
|
327
466
|
* Check if the command has sub commands
|
|
328
|
-
*
|
|
467
|
+
*
|
|
468
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
329
469
|
* @returns True if the command has sub commands
|
|
330
470
|
*/
|
|
331
471
|
async function hasCommands(ctx) {
|
|
332
|
-
|
|
333
|
-
return loadedCommands.length > 1;
|
|
472
|
+
return (await ctx.extensions?.[pluginId].loadCommands() || []).length > 1;
|
|
334
473
|
}
|
|
335
474
|
/**
|
|
336
475
|
* Check if the command has optional arguments
|
|
337
|
-
*
|
|
476
|
+
*
|
|
477
|
+
* @param args - A {@link Args | command optional arguments}
|
|
338
478
|
* @returns True if the command has options
|
|
339
479
|
*/
|
|
340
|
-
function hasOptionalArgs(
|
|
341
|
-
return
|
|
480
|
+
function hasOptionalArgs(args) {
|
|
481
|
+
return Object.values(args).some((arg) => arg.type !== "positional");
|
|
342
482
|
}
|
|
343
483
|
/**
|
|
344
484
|
* Check if the command has positional arguments
|
|
345
|
-
*
|
|
485
|
+
*
|
|
486
|
+
* @param args - A {@link Args | command positional arguments}
|
|
346
487
|
* @returns True if the command has options
|
|
347
488
|
*/
|
|
348
|
-
function hasPositionalArgs(
|
|
349
|
-
return
|
|
489
|
+
function hasPositionalArgs(args) {
|
|
490
|
+
return Object.values(args).some((arg) => arg.type === "positional");
|
|
350
491
|
}
|
|
351
492
|
/**
|
|
352
493
|
* Check if all options have default values
|
|
353
|
-
*
|
|
494
|
+
*
|
|
495
|
+
* @param args - An {@link Args | command argument}
|
|
354
496
|
* @returns True if all options have default values
|
|
355
497
|
*/
|
|
356
|
-
function hasAllDefaultOptions(
|
|
357
|
-
return !!(
|
|
498
|
+
function hasAllDefaultOptions(args) {
|
|
499
|
+
return !!(args && Object.values(args).every((arg) => arg.default));
|
|
358
500
|
}
|
|
359
501
|
/**
|
|
360
502
|
* Generate options symbols for usage
|
|
361
|
-
*
|
|
503
|
+
*
|
|
504
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
505
|
+
* @param args - {@link Args | command arguments}
|
|
362
506
|
* @returns Options symbols for usage
|
|
363
507
|
*/
|
|
364
|
-
async function generateOptionsSymbols(ctx) {
|
|
365
|
-
return hasOptionalArgs(
|
|
508
|
+
async function generateOptionsSymbols(ctx, args) {
|
|
509
|
+
return hasOptionalArgs(args) ? hasAllDefaultOptions(args) ? `[${await ctx.extensions[pluginId].text(resolveBuiltInKey("OPTIONS"))}]` : `<${await ctx.extensions[pluginId].text(resolveBuiltInKey("OPTIONS"))}>` : "";
|
|
366
510
|
}
|
|
367
511
|
/**
|
|
368
512
|
* Get optional arguments pairs for usage
|
|
369
|
-
*
|
|
513
|
+
*
|
|
514
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
370
515
|
* @returns Options pairs for usage
|
|
371
516
|
*/
|
|
372
517
|
function getOptionalArgsPairs(ctx) {
|
|
@@ -397,7 +542,7 @@ async function resolveDisplayValue(ctx, key) {
|
|
|
397
542
|
const schema = ctx.args[key];
|
|
398
543
|
if ((schema.type === "boolean" || schema.type === "number" || schema.type === "string" || schema.type === "custom") && schema.default !== void 0) return `(${await generateDefaultDisplayValue(ctx, schema)})`;
|
|
399
544
|
if (schema.type === "enum") {
|
|
400
|
-
const _default = schema.default
|
|
545
|
+
const _default = schema.default === void 0 ? "" : await generateDefaultDisplayValue(ctx, schema);
|
|
401
546
|
const choices = `${await ctx.extensions[pluginId].text(resolveBuiltInKey("CHOICES"))}: ${schema.choices.join(" | ")}`;
|
|
402
547
|
return `(${_default ? `${_default}, ${choices}` : choices})`;
|
|
403
548
|
}
|
|
@@ -405,15 +550,16 @@ async function resolveDisplayValue(ctx, key) {
|
|
|
405
550
|
}
|
|
406
551
|
/**
|
|
407
552
|
* Generate optional arguments usage
|
|
408
|
-
*
|
|
409
|
-
* @param
|
|
553
|
+
*
|
|
554
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
555
|
+
* @param optionsPairs - Options pairs for usage
|
|
410
556
|
* @returns Generated options usage
|
|
411
557
|
*/
|
|
412
558
|
async function generateOptionalArgsUsage(ctx, optionsPairs) {
|
|
413
559
|
const optionsMaxLength = Math.max(...Object.entries(optionsPairs).map(([_, value]) => value.length));
|
|
414
560
|
const optionSchemaMaxLength = ctx.env.usageOptionType ? Math.max(...Object.entries(optionsPairs).map(([key]) => resolveNegatableType(key, ctx).length)) : 0;
|
|
415
|
-
|
|
416
|
-
let rawDesc = await ctx.extensions[pluginId].text(resolveArgKey(key));
|
|
561
|
+
return (await Promise.all(Object.entries(optionsPairs).map(async ([key, value]) => {
|
|
562
|
+
let rawDesc = await ctx.extensions[pluginId].text(resolveArgKey(key, ctx.name));
|
|
417
563
|
if (!rawDesc && key.startsWith(ARG_NEGATABLE_PREFIX)) {
|
|
418
564
|
const name = resolveNegatableKey(key);
|
|
419
565
|
const schema = ctx.args[name];
|
|
@@ -423,34 +569,34 @@ async function generateOptionalArgsUsage(ctx, optionsPairs) {
|
|
|
423
569
|
const optionsSchema = ctx.env.usageOptionType ? `[${resolveNegatableType(key, ctx)}] ` : "";
|
|
424
570
|
const valueDesc = key.startsWith(ARG_NEGATABLE_PREFIX) ? "" : await resolveDisplayValue(ctx, key);
|
|
425
571
|
const desc = `${optionsSchema ? optionsSchema.padEnd(optionSchemaMaxLength + 3) : ""}${rawDesc}`;
|
|
426
|
-
const
|
|
572
|
+
const descLength = desc.length + valueDesc.length;
|
|
573
|
+
const option = `${value.padEnd((descLength > 0 ? optionsMaxLength : 0) + ctx.env.middleMargin)}${desc}${valueDesc ? ` ${valueDesc}` : ""}`;
|
|
427
574
|
return `${option.padStart(ctx.env.leftMargin + option.length)}`;
|
|
428
|
-
}));
|
|
429
|
-
return usages.join("\n");
|
|
575
|
+
}))).join("\n");
|
|
430
576
|
}
|
|
431
|
-
function getPositionalArgs(
|
|
432
|
-
return Object.entries(
|
|
577
|
+
function getPositionalArgs(args) {
|
|
578
|
+
return Object.entries(args).filter(([_, schema]) => schema.type === "positional");
|
|
433
579
|
}
|
|
434
580
|
async function generatePositionalArgsUsage(ctx) {
|
|
435
|
-
const positionals = getPositionalArgs(ctx);
|
|
581
|
+
const positionals = getPositionalArgs(ctx.args);
|
|
436
582
|
const argsMaxLength = Math.max(...positionals.map(([name]) => name.length));
|
|
437
|
-
|
|
438
|
-
const desc = await ctx.extensions[pluginId].text(resolveArgKey(name)) || ctx.args[name].description || "";
|
|
583
|
+
return (await Promise.all(positionals.map(async ([name]) => {
|
|
584
|
+
const desc = await ctx.extensions[pluginId].text(resolveArgKey(name, ctx.name)) || ctx.args[name].description || "";
|
|
439
585
|
const arg = `${name.padEnd(argsMaxLength + ctx.env.middleMargin)} ${desc}`;
|
|
440
586
|
return `${arg.padStart(ctx.env.leftMargin + arg.length)}`;
|
|
441
|
-
}));
|
|
442
|
-
return usages.join("\n");
|
|
587
|
+
}))).join("\n");
|
|
443
588
|
}
|
|
444
|
-
function generatePositionalSymbols(
|
|
445
|
-
return hasPositionalArgs(
|
|
589
|
+
function generatePositionalSymbols(args) {
|
|
590
|
+
return hasPositionalArgs(args) ? getPositionalArgs(args).map(([name]) => `<${name}>`).join(" ") : "";
|
|
446
591
|
}
|
|
447
592
|
|
|
448
593
|
//#endregion
|
|
449
594
|
//#region src/validation.ts
|
|
450
595
|
/**
|
|
451
596
|
* Render the validation errors.
|
|
452
|
-
*
|
|
453
|
-
* @param
|
|
597
|
+
*
|
|
598
|
+
* @param _ctx - A {@link CommandContext | command context}
|
|
599
|
+
* @param error - An {@link AggregateError} of option in `args-token` validation
|
|
454
600
|
* @returns A rendered validation error.
|
|
455
601
|
*/
|
|
456
602
|
function renderValidationErrors(_ctx, error) {
|
|
@@ -462,25 +608,36 @@ function renderValidationErrors(_ctx, error) {
|
|
|
462
608
|
//#endregion
|
|
463
609
|
//#region src/index.ts
|
|
464
610
|
const i18nPluginId = namespacedId("i18n");
|
|
611
|
+
const dependencies = [{
|
|
612
|
+
id: i18nPluginId,
|
|
613
|
+
optional: true
|
|
614
|
+
}];
|
|
465
615
|
/**
|
|
466
616
|
* usage renderer plugin
|
|
617
|
+
*
|
|
618
|
+
* @returns A defined plugin as usage renderer
|
|
467
619
|
*/
|
|
468
620
|
function renderer() {
|
|
469
621
|
return plugin({
|
|
470
622
|
id: pluginId,
|
|
471
623
|
name: "usage renderer",
|
|
472
|
-
dependencies
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}],
|
|
476
|
-
extension: (ctx, cmd) => {
|
|
477
|
-
const { extensions: { [i18nPluginId]: i18n } } = ctx;
|
|
624
|
+
dependencies,
|
|
625
|
+
extension: async (ctx, cmd) => {
|
|
626
|
+
const i18n = ctx.extensions[i18nPluginId];
|
|
478
627
|
let cachedCommands;
|
|
479
628
|
async function loadCommands() {
|
|
480
629
|
if (cachedCommands) return cachedCommands;
|
|
481
630
|
const subCommands = [...ctx.env.subCommands || []];
|
|
482
|
-
|
|
483
|
-
|
|
631
|
+
cachedCommands = (await Promise.all(subCommands.map(async ([name, cmd$1]) => await resolveLazyCommand(cmd$1, name)))).filter((cmd$1) => !cmd$1.internal).filter(Boolean);
|
|
632
|
+
cachedCommands.sort((a, b) => {
|
|
633
|
+
if (a.entry && !b.entry) return -1;
|
|
634
|
+
if (!a.entry && b.entry) return 1;
|
|
635
|
+
if (a.name && b.name) return a.name.localeCompare(b.name);
|
|
636
|
+
if (a.name && !b.name) return -1;
|
|
637
|
+
if (!a.name && b.name) return 1;
|
|
638
|
+
return 0;
|
|
639
|
+
});
|
|
640
|
+
return cachedCommands;
|
|
484
641
|
}
|
|
485
642
|
return {
|
|
486
643
|
text: localizable(ctx, cmd, i18n?.translate),
|
|
@@ -496,4 +653,4 @@ function renderer() {
|
|
|
496
653
|
}
|
|
497
654
|
|
|
498
655
|
//#endregion
|
|
499
|
-
export { renderer as default, renderHeader, renderUsage, renderValidationErrors };
|
|
656
|
+
export { renderer as default, pluginId, renderHeader, renderUsage, renderValidationErrors };
|
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": "0.27.0-
|
|
4
|
+
"version": "0.27.0-beta.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "kazuya kawaguchi",
|
|
7
7
|
"email": "kawakazu80@gmail.com"
|
|
@@ -53,20 +53,20 @@
|
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@gunshi/plugin": "0.27.0-
|
|
56
|
+
"@gunshi/plugin": "0.27.0-beta.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@gunshi/plugin-i18n": "0.27.0-
|
|
59
|
+
"@gunshi/plugin-i18n": "0.27.0-beta.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"deno": "^2.4
|
|
62
|
+
"deno": "^2.5.4",
|
|
63
63
|
"jsr": "^0.13.5",
|
|
64
64
|
"jsr-exports-lint": "^0.4.1",
|
|
65
|
-
"publint": "^0.3.
|
|
66
|
-
"tsdown": "^0.
|
|
67
|
-
"typedoc": "^0.28.
|
|
68
|
-
"typedoc-plugin-markdown": "^4.
|
|
69
|
-
"@gunshi/shared": "0.27.0-
|
|
65
|
+
"publint": "^0.3.14",
|
|
66
|
+
"tsdown": "^0.15.6",
|
|
67
|
+
"typedoc": "^0.28.13",
|
|
68
|
+
"typedoc-plugin-markdown": "^4.9.0",
|
|
69
|
+
"@gunshi/shared": "0.27.0-beta.0"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
72
|
"build": "tsdown",
|