@gunshi/plugin-renderer 0.27.0-alpha.9 → 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 +49 -21
- package/lib/index.js +160 -69
- 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,10 +1,11 @@
|
|
|
1
1
|
import { Args, Command, CommandContext, DefaultGunshiParams, GunshiParams, PluginWithExtension } from "@gunshi/plugin";
|
|
2
2
|
import { Args as Args$1 } from "args-tokens";
|
|
3
3
|
|
|
4
|
-
//#region
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
//#region ../shared/src/constants.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
8
9
|
/**
|
|
9
10
|
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
10
11
|
* @license MIT
|
|
@@ -13,9 +14,6 @@ declare const BUILT_IN_PREFIX = "_";
|
|
|
13
14
|
declare const PLUGIN_PREFIX = "g";
|
|
14
15
|
declare const ARG_PREFIX = "arg";
|
|
15
16
|
declare const BUILT_IN_KEY_SEPARATOR = ":";
|
|
16
|
-
declare const BUILD_IN_PREFIX_AND_KEY_SEPARATOR: string;
|
|
17
|
-
declare const ARG_PREFIX_AND_KEY_SEPARATOR: string;
|
|
18
|
-
declare const ARG_NEGATABLE_PREFIX = "no-";
|
|
19
17
|
type CommonArgType = {
|
|
20
18
|
readonly help: {
|
|
21
19
|
readonly type: 'boolean';
|
|
@@ -33,10 +31,17 @@ declare const COMMAND_BUILTIN_RESOURCE_KEYS: readonly ["USAGE", "COMMAND", "SUBC
|
|
|
33
31
|
//#endregion
|
|
34
32
|
//#region ../shared/src/types.d.ts
|
|
35
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
|
+
|
|
36
38
|
/**
|
|
37
39
|
* Remove index signature from object or record type.
|
|
38
40
|
*/
|
|
39
41
|
type RemovedIndex<T> = RemoveIndexSignature<{ [K in keyof T]: T[K] }>;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a key on {@link Args}.
|
|
44
|
+
*/
|
|
40
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];
|
|
41
46
|
/**
|
|
42
47
|
* Generate a namespaced key.
|
|
@@ -45,11 +50,11 @@ type GenerateNamespacedKey<Key extends string, Prefixed extends string = typeof
|
|
|
45
50
|
/**
|
|
46
51
|
* Command i18n built-in arguments keys.
|
|
47
52
|
*/
|
|
48
|
-
type CommandBuiltinArgsKeys = keyof
|
|
53
|
+
type CommandBuiltinArgsKeys = keyof typeof COMMON_ARGS;
|
|
49
54
|
/**
|
|
50
55
|
* Command i18n built-in resource keys.
|
|
51
56
|
*/
|
|
52
|
-
type CommandBuiltinResourceKeys = (typeof
|
|
57
|
+
type CommandBuiltinResourceKeys = (typeof COMMAND_BUILTIN_RESOURCE_KEYS)[number];
|
|
53
58
|
/**
|
|
54
59
|
* Built-in resource keys.
|
|
55
60
|
*/
|
|
@@ -75,18 +80,33 @@ E extends Record<string, string> = {},
|
|
|
75
80
|
R extends string = keyof RemovedIndex<E>, T extends string = (C extends {
|
|
76
81
|
name: infer N;
|
|
77
82
|
} ? N extends string ? GenerateNamespacedKey<R, N> : R : R | CommandBuiltinKeys), O = CommandArgKeys<A, C>> = CommandBuiltinKeys | O | T;
|
|
78
|
-
/**
|
|
79
|
-
* Translation function interface
|
|
80
|
-
*/
|
|
81
83
|
//#endregion
|
|
82
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;
|
|
83
93
|
/**
|
|
84
94
|
* Extended command context which provides utilities via usage renderer plugin.
|
|
85
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.
|
|
86
98
|
*/
|
|
87
|
-
interface
|
|
99
|
+
interface UsageRendererExtension<G extends GunshiParams<any> = DefaultGunshiParams> {
|
|
88
100
|
/**
|
|
89
|
-
* 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.
|
|
90
110
|
*/
|
|
91
111
|
text: <A extends Args = G['args'], C = {},
|
|
92
112
|
// for CommandContext
|
|
@@ -95,7 +115,10 @@ interface UsageRendererCommandContext<G extends GunshiParams<any> = DefaultGunsh
|
|
|
95
115
|
K = ResolveTranslationKeys<A, C, E>>(key: K, values?: Record<string, unknown>) => string;
|
|
96
116
|
/**
|
|
97
117
|
* Load commands
|
|
98
|
-
*
|
|
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.
|
|
99
122
|
*/
|
|
100
123
|
loadCommands: <G extends GunshiParams = DefaultGunshiParams>() => Promise<Command<G>[]>;
|
|
101
124
|
}
|
|
@@ -103,7 +126,8 @@ interface UsageRendererCommandContext<G extends GunshiParams<any> = DefaultGunsh
|
|
|
103
126
|
//#region src/header.d.ts
|
|
104
127
|
/**
|
|
105
128
|
* Render the header.
|
|
106
|
-
*
|
|
129
|
+
*
|
|
130
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
107
131
|
* @returns A rendered header.
|
|
108
132
|
*/
|
|
109
133
|
declare function renderHeader<G extends GunshiParams = DefaultGunshiParams>(ctx: Readonly<CommandContext<G>>): Promise<string>;
|
|
@@ -111,7 +135,8 @@ declare function renderHeader<G extends GunshiParams = DefaultGunshiParams>(ctx:
|
|
|
111
135
|
//#region src/usage.d.ts
|
|
112
136
|
/**
|
|
113
137
|
* Render the usage.
|
|
114
|
-
*
|
|
138
|
+
*
|
|
139
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
115
140
|
* @returns A rendered usage.
|
|
116
141
|
*/
|
|
117
142
|
declare function renderUsage<G extends GunshiParams = DefaultGunshiParams>(ctx: Readonly<CommandContext<G>>): Promise<string>;
|
|
@@ -119,8 +144,9 @@ declare function renderUsage<G extends GunshiParams = DefaultGunshiParams>(ctx:
|
|
|
119
144
|
//#region src/validation.d.ts
|
|
120
145
|
/**
|
|
121
146
|
* Render the validation errors.
|
|
122
|
-
*
|
|
123
|
-
* @param
|
|
147
|
+
*
|
|
148
|
+
* @param _ctx - A {@link CommandContext | command context}
|
|
149
|
+
* @param error - An {@link AggregateError} of option in `args-token` validation
|
|
124
150
|
* @returns A rendered validation error.
|
|
125
151
|
*/
|
|
126
152
|
declare function renderValidationErrors<G extends GunshiParams = DefaultGunshiParams>(_ctx: CommandContext<G>, error: AggregateError): Promise<string>;
|
|
@@ -128,7 +154,9 @@ declare function renderValidationErrors<G extends GunshiParams = DefaultGunshiPa
|
|
|
128
154
|
//#region src/index.d.ts
|
|
129
155
|
/**
|
|
130
156
|
* usage renderer plugin
|
|
157
|
+
*
|
|
158
|
+
* @returns A defined plugin as usage renderer
|
|
131
159
|
*/
|
|
132
|
-
declare function renderer(): PluginWithExtension<
|
|
160
|
+
declare function renderer(): PluginWithExtension<UsageRendererExtension>;
|
|
133
161
|
//#endregion
|
|
134
|
-
export {
|
|
162
|
+
export { PluginId, UsageRendererExtension, renderer as default, pluginId, renderHeader, renderUsage, renderValidationErrors };
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
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 { ANONYMOUS_COMMAND_NAME, 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)) {
|
|
@@ -127,8 +160,12 @@ var en_US_default = {
|
|
|
127
160
|
//#region ../shared/src/utils.ts
|
|
128
161
|
/**
|
|
129
162
|
* Resolve a namespaced key for built-in resources.
|
|
163
|
+
*
|
|
130
164
|
* Built-in keys are prefixed with "_:".
|
|
131
|
-
*
|
|
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.
|
|
132
169
|
* @returns Prefixed built-in key.
|
|
133
170
|
*/
|
|
134
171
|
function resolveBuiltInKey(key) {
|
|
@@ -136,34 +173,66 @@ function resolveBuiltInKey(key) {
|
|
|
136
173
|
}
|
|
137
174
|
/**
|
|
138
175
|
* Resolve a namespaced key for argument resources.
|
|
176
|
+
*
|
|
139
177
|
* Argument keys are prefixed with "arg:".
|
|
140
178
|
* If the command name is provided, it will be prefixed with the command name (e.g. "cmd1:arg:foo").
|
|
141
|
-
*
|
|
142
|
-
* @
|
|
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.
|
|
143
184
|
* @returns Prefixed argument key.
|
|
144
185
|
*/
|
|
145
|
-
function resolveArgKey(key,
|
|
146
|
-
return `${
|
|
186
|
+
function resolveArgKey(key, name) {
|
|
187
|
+
return `${name ? `${name}${BUILT_IN_KEY_SEPARATOR}` : ""}${ARG_PREFIX}${BUILT_IN_KEY_SEPARATOR}${key}`;
|
|
147
188
|
}
|
|
148
189
|
/**
|
|
149
190
|
* Resolve a namespaced key for non-built-in resources.
|
|
191
|
+
*
|
|
150
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").
|
|
151
|
-
*
|
|
152
|
-
* @
|
|
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.
|
|
153
198
|
* @returns Prefixed non-built-in key.
|
|
154
199
|
*/
|
|
155
|
-
function resolveKey(key,
|
|
156
|
-
return `${
|
|
200
|
+
function resolveKey(key, name) {
|
|
201
|
+
return `${name ? `${name}${BUILT_IN_KEY_SEPARATOR}` : ""}${key}`;
|
|
157
202
|
}
|
|
158
|
-
|
|
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) {
|
|
159
213
|
return typeof examples === "string" ? examples : typeof examples === "function" ? await examples(ctx) : "";
|
|
160
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
|
+
*/
|
|
161
223
|
function namespacedId(id) {
|
|
162
224
|
return `${PLUGIN_PREFIX}${BUILT_IN_KEY_SEPARATOR}${id}`;
|
|
163
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
|
+
*/
|
|
164
234
|
function makeShortLongOptionPair(schema, name, toKebab) {
|
|
165
|
-
|
|
166
|
-
let key = `--${displayName}`;
|
|
235
|
+
let key = `--${toKebab || schema.toKebab ? kebabnize(name) : name}`;
|
|
167
236
|
if (schema.short) key = `-${schema.short}, ${key}`;
|
|
168
237
|
return key;
|
|
169
238
|
}
|
|
@@ -172,20 +241,23 @@ function makeShortLongOptionPair(schema, name, toKebab) {
|
|
|
172
241
|
//#region ../shared/src/localization.ts
|
|
173
242
|
/**
|
|
174
243
|
* Create a localizable function for a command.
|
|
244
|
+
*
|
|
175
245
|
* This function will resolve the translation key based on the command context and the provided translation function.
|
|
176
|
-
*
|
|
177
|
-
* @
|
|
178
|
-
* @
|
|
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
|
|
179
254
|
* @returns Localizable function
|
|
180
255
|
*/
|
|
181
256
|
function localizable(ctx, cmd, translate) {
|
|
182
257
|
async function localize(key, values) {
|
|
183
258
|
if (translate) return translate(key, values);
|
|
184
|
-
if (key.startsWith(BUILD_IN_PREFIX_AND_KEY_SEPARATOR))
|
|
185
|
-
|
|
186
|
-
return en_US_default[resKey] || key;
|
|
187
|
-
}
|
|
188
|
-
const namaspacedArgKey = resolveKey(ARG_PREFIX_AND_KEY_SEPARATOR, ctx);
|
|
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);
|
|
189
261
|
if (key.startsWith(namaspacedArgKey)) {
|
|
190
262
|
let argKey = key.slice(namaspacedArgKey.length);
|
|
191
263
|
let negatable = false;
|
|
@@ -197,8 +269,8 @@ function localizable(ctx, cmd, translate) {
|
|
|
197
269
|
if (!schema) return argKey;
|
|
198
270
|
return negatable && schema.type === "boolean" && schema.negatable ? `${en_US_default["NEGATABLE"]} ${makeShortLongOptionPair(schema, argKey, ctx.toKebab)}` : schema.description || "";
|
|
199
271
|
}
|
|
200
|
-
if (key === resolveKey("description", ctx)) return "";
|
|
201
|
-
else if (key === resolveKey("examples", ctx)) return await resolveExamples
|
|
272
|
+
if (key === resolveKey("description", ctx.name)) return "";
|
|
273
|
+
else if (key === resolveKey("examples", ctx.name)) return await resolveExamples(ctx, cmd.examples);
|
|
202
274
|
else return key;
|
|
203
275
|
}
|
|
204
276
|
return localize;
|
|
@@ -208,7 +280,8 @@ function localizable(ctx, cmd, translate) {
|
|
|
208
280
|
//#region src/header.ts
|
|
209
281
|
/**
|
|
210
282
|
* Render the header.
|
|
211
|
-
*
|
|
283
|
+
*
|
|
284
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
212
285
|
* @returns A rendered header.
|
|
213
286
|
*/
|
|
214
287
|
function renderHeader(ctx) {
|
|
@@ -228,7 +301,8 @@ const pluginId = namespacedId("renderer");
|
|
|
228
301
|
const COMMON_ARGS_KEYS = Object.keys(COMMON_ARGS);
|
|
229
302
|
/**
|
|
230
303
|
* Render the usage.
|
|
231
|
-
*
|
|
304
|
+
*
|
|
305
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
232
306
|
* @returns A rendered usage.
|
|
233
307
|
*/
|
|
234
308
|
async function renderUsage(ctx) {
|
|
@@ -247,7 +321,8 @@ async function renderUsage(ctx) {
|
|
|
247
321
|
}
|
|
248
322
|
/**
|
|
249
323
|
* Render the positional arguments section
|
|
250
|
-
*
|
|
324
|
+
*
|
|
325
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
251
326
|
* @returns A rendered arguments section
|
|
252
327
|
*/
|
|
253
328
|
async function renderPositionalArgsSection(ctx) {
|
|
@@ -258,7 +333,8 @@ async function renderPositionalArgsSection(ctx) {
|
|
|
258
333
|
}
|
|
259
334
|
/**
|
|
260
335
|
* Render the optional arguments section
|
|
261
|
-
*
|
|
336
|
+
*
|
|
337
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
262
338
|
* @returns A rendered options section
|
|
263
339
|
*/
|
|
264
340
|
async function renderOptionalArgsSection(ctx) {
|
|
@@ -269,12 +345,13 @@ async function renderOptionalArgsSection(ctx) {
|
|
|
269
345
|
}
|
|
270
346
|
/**
|
|
271
347
|
* Render the examples section
|
|
272
|
-
*
|
|
348
|
+
*
|
|
349
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
273
350
|
* @returns A rendered examples section
|
|
274
351
|
*/
|
|
275
352
|
async function renderExamplesSection(ctx) {
|
|
276
353
|
const messages = [];
|
|
277
|
-
const resolvedExamples = await resolveExamples(ctx);
|
|
354
|
+
const resolvedExamples = await resolveExamples$1(ctx);
|
|
278
355
|
if (resolvedExamples) {
|
|
279
356
|
const examples = resolvedExamples.split("\n").map((example) => example.padStart(ctx.env.leftMargin + example.length));
|
|
280
357
|
messages.push(`${await ctx.extensions[pluginId].text(resolveBuiltInKey("EXAMPLES"))}:`, ...examples);
|
|
@@ -283,7 +360,8 @@ async function renderExamplesSection(ctx) {
|
|
|
283
360
|
}
|
|
284
361
|
/**
|
|
285
362
|
* Render the usage section
|
|
286
|
-
*
|
|
363
|
+
*
|
|
364
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
287
365
|
* @returns A rendered usage section
|
|
288
366
|
*/
|
|
289
367
|
async function renderUsageSection(ctx) {
|
|
@@ -305,7 +383,8 @@ async function makeUsageSymbols(ctx) {
|
|
|
305
383
|
}
|
|
306
384
|
/**
|
|
307
385
|
* Render the commands section
|
|
308
|
-
*
|
|
386
|
+
*
|
|
387
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
309
388
|
* @returns A rendered commands section
|
|
310
389
|
*/
|
|
311
390
|
async function renderCommandsSection(ctx) {
|
|
@@ -346,7 +425,8 @@ async function makeCommandSymbol(ctx, cmd) {
|
|
|
346
425
|
}
|
|
347
426
|
/**
|
|
348
427
|
* Resolve the entry command name
|
|
349
|
-
*
|
|
428
|
+
*
|
|
429
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
350
430
|
* @returns The entry command name
|
|
351
431
|
*/
|
|
352
432
|
async function resolveEntry(ctx) {
|
|
@@ -354,7 +434,8 @@ async function resolveEntry(ctx) {
|
|
|
354
434
|
}
|
|
355
435
|
/**
|
|
356
436
|
* Resolve the sub command name
|
|
357
|
-
*
|
|
437
|
+
*
|
|
438
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
358
439
|
* @returns The sub command name
|
|
359
440
|
*/
|
|
360
441
|
async function resolveSubCommand(ctx) {
|
|
@@ -362,35 +443,38 @@ async function resolveSubCommand(ctx) {
|
|
|
362
443
|
}
|
|
363
444
|
/**
|
|
364
445
|
* Resolve the command description
|
|
365
|
-
*
|
|
446
|
+
*
|
|
447
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
366
448
|
* @returns resolved command description
|
|
367
449
|
*/
|
|
368
450
|
async function resolveDescription(ctx) {
|
|
369
|
-
return await ctx.extensions[pluginId].text(resolveKey("description", ctx)) || ctx.description || "";
|
|
451
|
+
return await ctx.extensions[pluginId].text(resolveKey("description", ctx.name)) || ctx.description || "";
|
|
370
452
|
}
|
|
371
453
|
/**
|
|
372
454
|
* Resolve the command examples
|
|
373
|
-
*
|
|
455
|
+
*
|
|
456
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
374
457
|
* @returns resolved command examples, if not resolved, return empty string
|
|
375
458
|
*/
|
|
376
|
-
async function resolveExamples(ctx) {
|
|
377
|
-
const ret = await ctx.extensions[pluginId].text(resolveKey("examples", ctx));
|
|
459
|
+
async function resolveExamples$1(ctx) {
|
|
460
|
+
const ret = await ctx.extensions[pluginId].text(resolveKey("examples", ctx.name));
|
|
378
461
|
if (ret) return ret;
|
|
379
462
|
const command = ctx.env.subCommands?.get(ctx.name || "");
|
|
380
|
-
return await resolveExamples
|
|
463
|
+
return await resolveExamples(ctx, command?.examples);
|
|
381
464
|
}
|
|
382
465
|
/**
|
|
383
466
|
* Check if the command has sub commands
|
|
384
|
-
*
|
|
467
|
+
*
|
|
468
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
385
469
|
* @returns True if the command has sub commands
|
|
386
470
|
*/
|
|
387
471
|
async function hasCommands(ctx) {
|
|
388
|
-
|
|
389
|
-
return loadedCommands.length > 1;
|
|
472
|
+
return (await ctx.extensions?.[pluginId].loadCommands() || []).length > 1;
|
|
390
473
|
}
|
|
391
474
|
/**
|
|
392
475
|
* Check if the command has optional arguments
|
|
393
|
-
*
|
|
476
|
+
*
|
|
477
|
+
* @param args - A {@link Args | command optional arguments}
|
|
394
478
|
* @returns True if the command has options
|
|
395
479
|
*/
|
|
396
480
|
function hasOptionalArgs(args) {
|
|
@@ -398,7 +482,8 @@ function hasOptionalArgs(args) {
|
|
|
398
482
|
}
|
|
399
483
|
/**
|
|
400
484
|
* Check if the command has positional arguments
|
|
401
|
-
*
|
|
485
|
+
*
|
|
486
|
+
* @param args - A {@link Args | command positional arguments}
|
|
402
487
|
* @returns True if the command has options
|
|
403
488
|
*/
|
|
404
489
|
function hasPositionalArgs(args) {
|
|
@@ -406,7 +491,8 @@ function hasPositionalArgs(args) {
|
|
|
406
491
|
}
|
|
407
492
|
/**
|
|
408
493
|
* Check if all options have default values
|
|
409
|
-
*
|
|
494
|
+
*
|
|
495
|
+
* @param args - An {@link Args | command argument}
|
|
410
496
|
* @returns True if all options have default values
|
|
411
497
|
*/
|
|
412
498
|
function hasAllDefaultOptions(args) {
|
|
@@ -414,7 +500,9 @@ function hasAllDefaultOptions(args) {
|
|
|
414
500
|
}
|
|
415
501
|
/**
|
|
416
502
|
* Generate options symbols for usage
|
|
417
|
-
*
|
|
503
|
+
*
|
|
504
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
505
|
+
* @param args - {@link Args | command arguments}
|
|
418
506
|
* @returns Options symbols for usage
|
|
419
507
|
*/
|
|
420
508
|
async function generateOptionsSymbols(ctx, args) {
|
|
@@ -422,7 +510,8 @@ async function generateOptionsSymbols(ctx, args) {
|
|
|
422
510
|
}
|
|
423
511
|
/**
|
|
424
512
|
* Get optional arguments pairs for usage
|
|
425
|
-
*
|
|
513
|
+
*
|
|
514
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
426
515
|
* @returns Options pairs for usage
|
|
427
516
|
*/
|
|
428
517
|
function getOptionalArgsPairs(ctx) {
|
|
@@ -453,7 +542,7 @@ async function resolveDisplayValue(ctx, key) {
|
|
|
453
542
|
const schema = ctx.args[key];
|
|
454
543
|
if ((schema.type === "boolean" || schema.type === "number" || schema.type === "string" || schema.type === "custom") && schema.default !== void 0) return `(${await generateDefaultDisplayValue(ctx, schema)})`;
|
|
455
544
|
if (schema.type === "enum") {
|
|
456
|
-
const _default = schema.default
|
|
545
|
+
const _default = schema.default === void 0 ? "" : await generateDefaultDisplayValue(ctx, schema);
|
|
457
546
|
const choices = `${await ctx.extensions[pluginId].text(resolveBuiltInKey("CHOICES"))}: ${schema.choices.join(" | ")}`;
|
|
458
547
|
return `(${_default ? `${_default}, ${choices}` : choices})`;
|
|
459
548
|
}
|
|
@@ -461,15 +550,16 @@ async function resolveDisplayValue(ctx, key) {
|
|
|
461
550
|
}
|
|
462
551
|
/**
|
|
463
552
|
* Generate optional arguments usage
|
|
464
|
-
*
|
|
465
|
-
* @param
|
|
553
|
+
*
|
|
554
|
+
* @param ctx - A {@link CommandContext | command context}
|
|
555
|
+
* @param optionsPairs - Options pairs for usage
|
|
466
556
|
* @returns Generated options usage
|
|
467
557
|
*/
|
|
468
558
|
async function generateOptionalArgsUsage(ctx, optionsPairs) {
|
|
469
559
|
const optionsMaxLength = Math.max(...Object.entries(optionsPairs).map(([_, value]) => value.length));
|
|
470
560
|
const optionSchemaMaxLength = ctx.env.usageOptionType ? Math.max(...Object.entries(optionsPairs).map(([key]) => resolveNegatableType(key, ctx).length)) : 0;
|
|
471
|
-
|
|
472
|
-
let rawDesc = await ctx.extensions[pluginId].text(resolveArgKey(key, ctx));
|
|
561
|
+
return (await Promise.all(Object.entries(optionsPairs).map(async ([key, value]) => {
|
|
562
|
+
let rawDesc = await ctx.extensions[pluginId].text(resolveArgKey(key, ctx.name));
|
|
473
563
|
if (!rawDesc && key.startsWith(ARG_NEGATABLE_PREFIX)) {
|
|
474
564
|
const name = resolveNegatableKey(key);
|
|
475
565
|
const schema = ctx.args[name];
|
|
@@ -482,8 +572,7 @@ async function generateOptionalArgsUsage(ctx, optionsPairs) {
|
|
|
482
572
|
const descLength = desc.length + valueDesc.length;
|
|
483
573
|
const option = `${value.padEnd((descLength > 0 ? optionsMaxLength : 0) + ctx.env.middleMargin)}${desc}${valueDesc ? ` ${valueDesc}` : ""}`;
|
|
484
574
|
return `${option.padStart(ctx.env.leftMargin + option.length)}`;
|
|
485
|
-
}));
|
|
486
|
-
return usages.join("\n");
|
|
575
|
+
}))).join("\n");
|
|
487
576
|
}
|
|
488
577
|
function getPositionalArgs(args) {
|
|
489
578
|
return Object.entries(args).filter(([_, schema]) => schema.type === "positional");
|
|
@@ -491,12 +580,11 @@ function getPositionalArgs(args) {
|
|
|
491
580
|
async function generatePositionalArgsUsage(ctx) {
|
|
492
581
|
const positionals = getPositionalArgs(ctx.args);
|
|
493
582
|
const argsMaxLength = Math.max(...positionals.map(([name]) => name.length));
|
|
494
|
-
|
|
495
|
-
const desc = await ctx.extensions[pluginId].text(resolveArgKey(name, ctx)) || 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 || "";
|
|
496
585
|
const arg = `${name.padEnd(argsMaxLength + ctx.env.middleMargin)} ${desc}`;
|
|
497
586
|
return `${arg.padStart(ctx.env.leftMargin + arg.length)}`;
|
|
498
|
-
}));
|
|
499
|
-
return usages.join("\n");
|
|
587
|
+
}))).join("\n");
|
|
500
588
|
}
|
|
501
589
|
function generatePositionalSymbols(args) {
|
|
502
590
|
return hasPositionalArgs(args) ? getPositionalArgs(args).map(([name]) => `<${name}>`).join(" ") : "";
|
|
@@ -506,8 +594,9 @@ function generatePositionalSymbols(args) {
|
|
|
506
594
|
//#region src/validation.ts
|
|
507
595
|
/**
|
|
508
596
|
* Render the validation errors.
|
|
509
|
-
*
|
|
510
|
-
* @param
|
|
597
|
+
*
|
|
598
|
+
* @param _ctx - A {@link CommandContext | command context}
|
|
599
|
+
* @param error - An {@link AggregateError} of option in `args-token` validation
|
|
511
600
|
* @returns A rendered validation error.
|
|
512
601
|
*/
|
|
513
602
|
function renderValidationErrors(_ctx, error) {
|
|
@@ -519,25 +608,27 @@ function renderValidationErrors(_ctx, error) {
|
|
|
519
608
|
//#endregion
|
|
520
609
|
//#region src/index.ts
|
|
521
610
|
const i18nPluginId = namespacedId("i18n");
|
|
611
|
+
const dependencies = [{
|
|
612
|
+
id: i18nPluginId,
|
|
613
|
+
optional: true
|
|
614
|
+
}];
|
|
522
615
|
/**
|
|
523
616
|
* usage renderer plugin
|
|
617
|
+
*
|
|
618
|
+
* @returns A defined plugin as usage renderer
|
|
524
619
|
*/
|
|
525
620
|
function renderer() {
|
|
526
621
|
return plugin({
|
|
527
622
|
id: pluginId,
|
|
528
623
|
name: "usage renderer",
|
|
529
|
-
dependencies
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
}],
|
|
533
|
-
extension: (ctx, cmd) => {
|
|
534
|
-
const { extensions: { [i18nPluginId]: i18n } } = ctx;
|
|
624
|
+
dependencies,
|
|
625
|
+
extension: async (ctx, cmd) => {
|
|
626
|
+
const i18n = ctx.extensions[i18nPluginId];
|
|
535
627
|
let cachedCommands;
|
|
536
628
|
async function loadCommands() {
|
|
537
629
|
if (cachedCommands) return cachedCommands;
|
|
538
630
|
const subCommands = [...ctx.env.subCommands || []];
|
|
539
|
-
|
|
540
|
-
cachedCommands = allCommands.filter((cmd$1) => !cmd$1.internal).filter(Boolean);
|
|
631
|
+
cachedCommands = (await Promise.all(subCommands.map(async ([name, cmd$1]) => await resolveLazyCommand(cmd$1, name)))).filter((cmd$1) => !cmd$1.internal).filter(Boolean);
|
|
541
632
|
cachedCommands.sort((a, b) => {
|
|
542
633
|
if (a.entry && !b.entry) return -1;
|
|
543
634
|
if (!a.entry && b.entry) return 1;
|
|
@@ -562,4 +653,4 @@ function renderer() {
|
|
|
562
653
|
}
|
|
563
654
|
|
|
564
655
|
//#endregion
|
|
565
|
-
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",
|