@gunshi/bone 0.26.3 → 0.27.0-alpha.10

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.
Files changed (3) hide show
  1. package/lib/index.d.ts +239 -109
  2. package/lib/index.js +82 -20
  3. package/package.json +8 -8
package/lib/index.d.ts CHANGED
@@ -1,153 +1,188 @@
1
- //#region ../../node_modules/.pnpm/args-tokens@0.20.1/node_modules/args-tokens/lib/parser-FiQIAw-2.d.ts
1
+ //#region ../../node_modules/.pnpm/args-tokens@0.22.0/node_modules/args-tokens/lib/parser-Cbxholql.d.ts
2
2
  //#region src/parser.d.ts
3
3
  /**
4
- * Entry point of argument parser.
5
- * @module
6
- */
7
- /**
8
- * forked from `nodejs/node` (`pkgjs/parseargs`)
9
- * repository url: https://github.com/nodejs/node (https://github.com/pkgjs/parseargs)
10
- * code url: https://github.com/nodejs/node/blob/main/lib/internal/util/parse_args/parse_args.js
11
- *
12
- * @author kazuya kawaguchi (a.k.a. kazupon)
13
- * @license MIT
14
- */
15
- /**
16
- * Argument token Kind.
17
- * - `option`: option token, support short option (e.g. `-x`) and long option (e.g. `--foo`)
18
- * - `option-terminator`: option terminator (`--`) token, see guideline 10 in https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
19
- * - `positional`: positional token
20
- */
21
- type ArgTokenKind = "option" | "option-terminator" | "positional";
22
- /**
23
- * Argument token.
24
- */
4
+ * Entry point of argument parser.
5
+ * @module
6
+ */
7
+ /**
8
+ * forked from `nodejs/node` (`pkgjs/parseargs`)
9
+ * repository url: https://github.com/nodejs/node (https://github.com/pkgjs/parseargs)
10
+ * code url: https://github.com/nodejs/node/blob/main/lib/internal/util/parse_args/parse_args.js
11
+ *
12
+ * @author kazuya kawaguchi (a.k.a. kazupon)
13
+ * @license MIT
14
+ */
15
+ /**
16
+ * Argument token Kind.
17
+ * - `option`: option token, support short option (e.g. `-x`) and long option (e.g. `--foo`)
18
+ * - `option-terminator`: option terminator (`--`) token, see guideline 10 in https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
19
+ * - `positional`: positional token
20
+ */
21
+ type ArgTokenKind = 'option' | 'option-terminator' | 'positional';
22
+ /**
23
+ * Argument token.
24
+ */
25
25
  interface ArgToken {
26
26
  /**
27
- * Argument token kind.
28
- */
27
+ * Argument token kind.
28
+ */
29
29
  kind: ArgTokenKind;
30
30
  /**
31
- * Argument token index, e.g `--foo bar` => `--foo` index is 0, `bar` index is 1.
32
- */
31
+ * Argument token index, e.g `--foo bar` => `--foo` index is 0, `bar` index is 1.
32
+ */
33
33
  index: number;
34
34
  /**
35
- * Option name, e.g. `--foo` => `foo`, `-x` => `x`.
36
- */
35
+ * Option name, e.g. `--foo` => `foo`, `-x` => `x`.
36
+ */
37
37
  name?: string;
38
38
  /**
39
- * Raw option name, e.g. `--foo` => `--foo`, `-x` => `-x`.
40
- */
39
+ * Raw option name, e.g. `--foo` => `--foo`, `-x` => `-x`.
40
+ */
41
41
  rawName?: string;
42
42
  /**
43
- * Option value, e.g. `--foo=bar` => `bar`, `-x=bar` => `bar`.
44
- * If the `allowCompatible` option is `true`, short option value will be same as Node.js `parseArgs` behavior.
45
- */
43
+ * Option value, e.g. `--foo=bar` => `bar`, `-x=bar` => `bar`.
44
+ * If the `allowCompatible` option is `true`, short option value will be same as Node.js `parseArgs` behavior.
45
+ */
46
46
  value?: string;
47
47
  /**
48
- * Inline value, e.g. `--foo=bar` => `true`, `-x=bar` => `true`.
49
- */
48
+ * Inline value, e.g. `--foo=bar` => `true`, `-x=bar` => `true`.
49
+ */
50
50
  inlineValue?: boolean;
51
- } //#endregion
52
- //#region ../../node_modules/.pnpm/args-tokens@0.20.1/node_modules/args-tokens/lib/resolver-U72Jg6Ll.d.ts
53
-
51
+ }
54
52
  /**
55
- * Parser Options.
56
- */
53
+ * Parser Options.
54
+ */
55
+ //#endregion
56
+ //#region ../../node_modules/.pnpm/args-tokens@0.22.0/node_modules/args-tokens/lib/resolver-BoS-UnqX.d.ts
57
57
  //#region src/resolver.d.ts
58
+
58
59
  /**
59
- * An argument schema
60
- * This schema is similar to the schema of the `node:utils`.
61
- * difference is that:
62
- * - `required` property and `description` property are added
63
- * - `type` is not only 'string' and 'boolean', but also 'number', 'enum', 'positional', 'custom' too.
64
- * - `default` property type, not support multiple types
65
- */
60
+ * An argument schema
61
+ * This schema is similar to the schema of the `node:utils`.
62
+ * difference is that:
63
+ * - `required` property and `description` property are added
64
+ * - `type` is not only 'string' and 'boolean', but also 'number', 'enum', 'positional', 'custom' too.
65
+ * - `default` property type, not support multiple types
66
+ */
66
67
  interface ArgSchema {
67
68
  /**
68
- * Type of argument.
69
- */
70
- type: "string" | "boolean" | "number" | "enum" | "positional" | "custom";
69
+ * Type of argument.
70
+ */
71
+ type: 'string' | 'boolean' | 'number' | 'enum' | 'positional' | 'custom';
71
72
  /**
72
- * A single character alias for the argument.
73
- */
73
+ * A single character alias for the argument.
74
+ */
74
75
  short?: string;
75
76
  /**
76
- * A description of the argument.
77
- */
77
+ * A description of the argument.
78
+ */
78
79
  description?: string;
79
80
  /**
80
- * Whether the argument is required or not.
81
- */
81
+ * Whether the argument is required or not.
82
+ */
82
83
  required?: true;
83
84
  /**
84
- * Whether the argument allow multiple values or not.
85
- */
85
+ * Whether the argument allow multiple values or not.
86
+ */
86
87
  multiple?: true;
87
88
  /**
88
- * Whether the negatable option for `boolean` type
89
- */
89
+ * Whether the negatable option for `boolean` type
90
+ */
90
91
  negatable?: boolean;
91
92
  /**
92
- * The allowed values of the argument, and string only. This property is only used when the type is 'enum'.
93
- */
93
+ * The allowed values of the argument, and string only. This property is only used when the type is 'enum'.
94
+ */
94
95
  choices?: string[] | readonly string[];
95
96
  /**
96
- * The default value of the argument.
97
- * if the type is 'enum', the default value must be one of the allowed values.
98
- */
97
+ * The default value of the argument.
98
+ * if the type is 'enum', the default value must be one of the allowed values.
99
+ */
99
100
  default?: string | boolean | number;
100
101
  /**
101
- * Whether to convert the argument name to kebab-case.
102
- */
102
+ * Whether to convert the argument name to kebab-case.
103
+ */
103
104
  toKebab?: true;
104
105
  /**
105
- * A function to parse the value of the argument. if the type is 'custom', this function is required.
106
- * If argument value will be invalid, this function have to throw an error.
107
- * @param value
108
- * @returns Parsed value
109
- * @throws An Error, If the value is invalid. Error type should be `Error` or extends it
110
- */
111
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
106
+ * A function to parse the value of the argument. if the type is 'custom', this function is required.
107
+ * If argument value will be invalid, this function have to throw an error.
108
+ * @param value
109
+ * @returns Parsed value
110
+ * @throws An Error, If the value is invalid. Error type should be `Error` or extends it
111
+ */
112
112
  parse?: (value: string) => any;
113
113
  }
114
114
  /**
115
- * An object that contains {@link ArgSchema | argument schema}.
116
- */
115
+ * An object that contains {@link ArgSchema | argument schema}.
116
+ */
117
117
  interface Args {
118
118
  [option: string]: ArgSchema;
119
119
  }
120
120
  /**
121
- * An object that contains the values of the arguments.
122
- */
121
+ * An object that contains the values of the arguments.
122
+ */
123
123
  type ArgValues<T> = T extends Args ? ResolveArgValues<T, { [Arg in keyof T]: ExtractOptionValue<T[Arg]> }> : {
124
124
  [option: string]: string | boolean | number | (string | boolean | number)[] | undefined;
125
125
  };
126
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
126
  type IsFunction<T> = T extends ((...args: any[]) => any) ? true : false;
128
127
  /**
129
- * @internal
130
- */
131
- type ExtractOptionValue<A extends ArgSchema> = A["type"] extends "string" ? ResolveOptionValue<A, string> : A["type"] extends "boolean" ? ResolveOptionValue<A, boolean> : A["type"] extends "number" ? ResolveOptionValue<A, number> : A["type"] extends "positional" ? ResolveOptionValue<A, string> : A["type"] extends "enum" ? A["choices"] extends string[] | readonly string[] ? ResolveOptionValue<A, A["choices"][number]> : never : A["type"] extends "custom" ? IsFunction<A["parse"]> extends true ? ResolveOptionValue<A, ReturnType<NonNullable<A["parse"]>>> : never : ResolveOptionValue<A, string | boolean | number>;
132
- type ResolveOptionValue<A extends ArgSchema, T> = A["multiple"] extends true ? T[] : T;
128
+ * @internal
129
+ */
130
+ type ExtractOptionValue<A extends ArgSchema> = A['type'] extends 'string' ? ResolveOptionValue<A, string> : A['type'] extends 'boolean' ? ResolveOptionValue<A, boolean> : A['type'] extends 'number' ? ResolveOptionValue<A, number> : A['type'] extends 'positional' ? ResolveOptionValue<A, string> : A['type'] extends 'enum' ? A['choices'] extends string[] | readonly string[] ? ResolveOptionValue<A, A['choices'][number]> : never : A['type'] extends 'custom' ? IsFunction<A['parse']> extends true ? ResolveOptionValue<A, ReturnType<NonNullable<A['parse']>>> : never : ResolveOptionValue<A, string | boolean | number>;
131
+ type ResolveOptionValue<A extends ArgSchema, T> = A['multiple'] extends true ? T[] : T;
133
132
  /**
134
- * @internal
135
- */
136
- type ResolveArgValues<A extends Args, V extends Record<keyof A, unknown>> = { -readonly [Arg in keyof A]?: V[Arg] } & FilterArgs<A, V, "default"> & FilterArgs<A, V, "required"> & FilterPositionalArgs<A, V> extends infer P ? { [K in keyof P]: P[K] } : never;
133
+ * @internal
134
+ */
135
+ type ResolveArgValues<A extends Args, V extends Record<keyof A, unknown>> = { -readonly [Arg in keyof A]?: V[Arg] } & FilterArgs<A, V, 'default'> & FilterArgs<A, V, 'required'> & FilterPositionalArgs<A, V> extends infer P ? { [K in keyof P]: P[K] } : never;
137
136
  /**
138
- * @internal
139
- */
137
+ * @internal
138
+ */
140
139
  type FilterArgs<A extends Args, V extends Record<keyof A, unknown>, K extends keyof ArgSchema> = { [Arg in keyof A as A[Arg][K] extends {} ? Arg : never]: V[Arg] };
141
140
  /**
142
- * @internal
143
- */
144
- type FilterPositionalArgs<A extends Args, V extends Record<keyof A, unknown>> = { [Arg in keyof A as A[Arg]["type"] extends "positional" ? Arg : never]: V[Arg] };
141
+ * @internal
142
+ */
143
+ type FilterPositionalArgs<A extends Args, V extends Record<keyof A, unknown>> = { [Arg in keyof A as A[Arg]['type'] extends 'positional' ? Arg : never]: V[Arg] };
144
+ /**
145
+ * An arguments for {@link resolveArgs | resolve arguments}.
146
+ */
145
147
 
148
+ /**
149
+ * Tracks which arguments were explicitly provided by the user.
150
+ *
151
+ * Each property indicates whether the corresponding argument was explicitly
152
+ * provided (true) or is using a default value or not provided (false).
153
+ */
154
+ type ArgExplicitlyProvided<A extends Args> = { [K in keyof A]: boolean };
155
+ /**
156
+ * Resolve command line arguments.
157
+ * @param args - An arguments that contains {@link ArgSchema | arguments schema}.
158
+ * @param tokens - An array of {@link ArgToken | tokens}.
159
+ * @param resolveArgs - An arguments that contains {@link ResolveArgs | resolve arguments}.
160
+ * @returns An object that contains the values of the arguments, positional arguments, rest arguments, {@link AggregateError | validation errors}, and explicit provision status.
161
+ *
162
+ * @example
163
+ * ```typescript
164
+ * // passed tokens: --port 3000
165
+ *
166
+ * const { values, explicit } = resolveArgs({
167
+ * port: {
168
+ * type: 'number',
169
+ * default: 8080
170
+ * },
171
+ * host: {
172
+ * type: 'string',
173
+ * default: 'localhost'
174
+ * }
175
+ * }, parsedTokens)
176
+ *
177
+ * values.port // 3000
178
+ * values.host // 'localhost'
179
+ *
180
+ * explicit.port // true (explicitly provided)
181
+ * explicit.host // false (not provided, fallback to default)
182
+ * ```
183
+ */
146
184
  //#endregion
147
185
  //#region ../gunshi/src/plugin/context.d.ts
148
- /**
149
- * An arguments for {@link resolveArgs | resolve arguments}.
150
- */
151
186
  /**
152
187
  * Type helper to create GunshiParams from extracted args and extensions
153
188
  * @internal
@@ -158,6 +193,7 @@ type ExtractedParams<G extends GunshiParamsConstraint, L extends Record<string,
158
193
  };
159
194
  /**
160
195
  * Gunshi plugin context interface.
196
+ * @since v0.27.0
161
197
  */
162
198
  interface PluginContext<G extends GunshiParamsConstraint = DefaultGunshiParams> {
163
199
  /**
@@ -165,12 +201,29 @@ interface PluginContext<G extends GunshiParamsConstraint = DefaultGunshiParams>
165
201
  * @returns A map of global options.
166
202
  */
167
203
  readonly globalOptions: Map<string, ArgSchema>;
204
+ /**
205
+ * Get the registered sub commands
206
+ * @returns A map of sub commands.
207
+ */
208
+ readonly subCommands: ReadonlyMap<string, Command<G> | LazyCommand<G>>;
168
209
  /**
169
210
  * Add a global option.
170
211
  * @param name An option name
171
212
  * @param schema An {@link ArgSchema} for the option
172
213
  */
173
214
  addGlobalOption(name: string, schema: ArgSchema): void;
215
+ /**
216
+ * Add a sub command.
217
+ * @param name Command name
218
+ * @param command Command definition
219
+ */
220
+ addCommand(name: string, command: Command<G> | LazyCommand<G>): void;
221
+ /**
222
+ * Check if a command exists.
223
+ * @param name Command name
224
+ * @returns True if the command exists, false otherwise
225
+ */
226
+ hasCommand(name: string): boolean;
174
227
  /**
175
228
  * Decorate the header renderer.
176
229
  * @param decorator - A decorator function that wraps the base header renderer.
@@ -193,16 +246,17 @@ interface PluginContext<G extends GunshiParamsConstraint = DefaultGunshiParams>
193
246
  */
194
247
  decorateCommand<L extends Record<string, unknown> = DefaultGunshiParams['extensions']>(decorator: (baseRunner: (ctx: Readonly<CommandContext<ExtractedParams<G, L>>>) => Awaitable<void | string>) => (ctx: Readonly<CommandContext<ExtractedParams<G, L>>>) => Awaitable<void | string>): void;
195
248
  }
196
-
197
- //#endregion
198
- //#region ../gunshi/src/plugin/core.d.ts
199
249
  /**
200
250
  * Factory function for creating a plugin context.
201
251
  * @param decorators - A {@link Decorators} instance.
252
+ * @param initialSubCommands - Initial sub commands map.
202
253
  * @returns A new {@link PluginContext} instance.
203
254
  */
255
+ //#endregion
256
+ //#region ../gunshi/src/plugin/core.d.ts
204
257
  /**
205
258
  * Plugin dependency definition
259
+ * @since v0.27.0
206
260
  */
207
261
  interface PluginDependency {
208
262
  /**
@@ -216,17 +270,20 @@ interface PluginDependency {
216
270
  optional?: boolean;
217
271
  }
218
272
  /**
219
- * Plugin function type
273
+ * Plugin function type
274
+ * @since v0.27.0
220
275
  */
221
276
  type PluginFunction<G extends GunshiParams = DefaultGunshiParams> = (ctx: Readonly<PluginContext<G>>) => Awaitable<void>;
222
277
  /**
223
278
  * Plugin extension for CommandContext
279
+ * @since v0.27.0
224
280
  */
225
281
 
226
282
  /**
227
283
  * Gunshi plugin, which is a function that receives a PluginContext.
228
284
  * @param ctx - A {@link PluginContext}.
229
285
  * @returns An {@link Awaitable} that resolves when the plugin is loaded.
286
+ * @since v0.27.0
230
287
  */
231
288
  type Plugin<E extends GunshiParams['extensions'] = DefaultGunshiParams['extensions']> = PluginFunction & {
232
289
  id: string;
@@ -234,21 +291,22 @@ type Plugin<E extends GunshiParams['extensions'] = DefaultGunshiParams['extensio
234
291
  dependencies?: (PluginDependency | string)[];
235
292
  extension?: CommandContextExtension<E>;
236
293
  };
237
-
238
- //#endregion
239
- //#region ../gunshi/src/types.d.ts
240
294
  /**
241
295
  * Plugin return type with extension
242
296
  * @internal
243
297
  */
298
+ //#endregion
299
+ //#region ../gunshi/src/types.d.ts
244
300
  type Awaitable<T> = T | Promise<T>;
245
301
  /**
246
302
  * Extend command context type. This type is used to extend the command context with additional properties at {@link CommandContext.extensions}.
303
+ * @since v0.27.0
247
304
  */
248
305
  type ExtendContext = Record<string, unknown>;
249
306
  /**
250
307
  * Gunshi unified parameter type.
251
308
  * This type combines both argument definitions and command context extensions.
309
+ * @since v0.27.0
252
310
  */
253
311
  interface GunshiParams<P extends {
254
312
  args?: Args;
@@ -272,11 +330,13 @@ interface GunshiParams<P extends {
272
330
  }
273
331
  /**
274
332
  * Default Gunshi parameters
333
+ * @since v0.27.0
275
334
  */
276
335
  type DefaultGunshiParams = GunshiParams;
277
336
  /**
278
337
  * Generic constraint for command-related types.
279
338
  * This type constraint allows both GunshiParams and objects with extensions.
339
+ * @since v0.27.0
280
340
  */
281
341
  type GunshiParamsConstraint = GunshiParams<any> | {
282
342
  extensions: ExtendContext;
@@ -286,6 +346,11 @@ type GunshiParamsConstraint = GunshiParams<any> | {
286
346
  * @internal
287
347
  */
288
348
  type ExtractArgs<G> = G extends GunshiParams<any> ? G['args'] : Args;
349
+ /**
350
+ * Type helper to extract explicitly provided argument flags from G
351
+ * @internal
352
+ */
353
+ type ExtractArgExplicitlyProvided<G> = ArgExplicitlyProvided<ExtractArgs<G>>;
289
354
  /**
290
355
  * Type helper to extract extensions from G
291
356
  * @internal
@@ -378,16 +443,19 @@ interface CommandEnvironment<G extends GunshiParamsConstraint = DefaultGunshiPar
378
443
  /**
379
444
  * Hook that runs before any command execution
380
445
  * @see {@link CliOptions.onBeforeCommand}
446
+ * @since v0.27.0
381
447
  */
382
448
  onBeforeCommand: ((ctx: Readonly<CommandContext<G>>) => Awaitable<void>) | undefined;
383
449
  /**
384
450
  * Hook that runs after successful command execution
385
451
  * @see {@link CliOptions.onAfterCommand}
452
+ * @since v0.27.0
386
453
  */
387
- onAfterCommand: ((ctx: Readonly<CommandContext<G>>, result: string | void) => Awaitable<void>) | undefined;
454
+ onAfterCommand: ((ctx: Readonly<CommandContext<G>>, result: string | undefined) => Awaitable<void>) | undefined;
388
455
  /**
389
456
  * Hook that runs when a command throws an error
390
457
  * @see {@link CliOptions.onErrorCommand}
458
+ * @since v0.27.0
391
459
  */
392
460
  onErrorCommand: ((ctx: Readonly<CommandContext<G>>, error: Error) => Awaitable<void>) | undefined;
393
461
  }
@@ -450,23 +518,27 @@ interface CliOptions<G extends GunshiParamsConstraint = DefaultGunshiParams> {
450
518
  renderValidationErrors?: ((ctx: Readonly<CommandContext<G>>, error: AggregateError) => Promise<string>) | null;
451
519
  /**
452
520
  * User plugins.
521
+ * @since v0.27.0
453
522
  */
454
523
  plugins?: Plugin[];
455
524
  /**
456
525
  * Hook that runs before any command execution
457
526
  * @param ctx - The command context
527
+ * @since v0.27.0
458
528
  */
459
529
  onBeforeCommand?: (ctx: Readonly<CommandContext<G>>) => Awaitable<void>;
460
530
  /**
461
531
  * Hook that runs after successful command execution
462
532
  * @param ctx - The command context
463
533
  * @param result - The command execution result
534
+ * @since v0.27.0
464
535
  */
465
- onAfterCommand?: (ctx: Readonly<CommandContext<G>>, result: string | void) => Awaitable<void>;
536
+ onAfterCommand?: (ctx: Readonly<CommandContext<G>>, result: string | undefined) => Awaitable<void>;
466
537
  /**
467
538
  * Hook that runs when a command throws an error
468
539
  * @param ctx - The command context
469
540
  * @param error - The error thrown during execution
541
+ * @since v0.27.0
470
542
  */
471
543
  onErrorCommand?: (ctx: Readonly<CommandContext<G>>, error: Error) => Awaitable<void>;
472
544
  }
@@ -499,6 +571,15 @@ interface CommandContext<G extends GunshiParamsConstraint = DefaultGunshiParams>
499
571
  * The command arguments is same {@link Command.args}.
500
572
  */
501
573
  args: ExtractArgs<G>;
574
+ /**
575
+ * Whether arguments were explicitly provided by the user.
576
+ *
577
+ * - `true`: The argument was explicitly provided via command line
578
+ * - `false`: The argument was not explicitly provided. This means either:
579
+ * - The value comes from a default value defined in the argument schema
580
+ * - The value is `undefined` (no explicit input and no default value)
581
+ */
582
+ explicit: ExtractArgExplicitlyProvided<G>;
502
583
  /**
503
584
  * Command values, that is the values of the command that is executed.
504
585
  * Resolve values with `resolveArgs` from command arguments and {@link Command.args}.
@@ -545,7 +626,8 @@ interface CommandContext<G extends GunshiParamsConstraint = DefaultGunshiParams>
545
626
  */
546
627
  log: (message?: any, ...optionalParams: any[]) => void;
547
628
  /**
548
- * Command context extensions.
629
+ * Command context extensions.
630
+ * @since v0.27.0
549
631
  */
550
632
  extensions: keyof ExtractExtensions<G> extends never ? undefined : ExtractExtensions<G>;
551
633
  /**
@@ -556,16 +638,45 @@ interface CommandContext<G extends GunshiParamsConstraint = DefaultGunshiParams>
556
638
  }
557
639
  /**
558
640
  * CommandContextCore type (base type without extensions)
641
+ * @since v0.27.0
559
642
  */
560
643
  type CommandContextCore<G extends GunshiParamsConstraint = DefaultGunshiParams> = Readonly<CommandContext<G>>;
561
644
  /**
562
645
  * Command context extension
646
+ * @since v0.27.0
563
647
  */
564
648
  interface CommandContextExtension<E extends GunshiParams['extensions'] = DefaultGunshiParams['extensions']> {
565
649
  readonly key: symbol;
566
650
  readonly factory: (ctx: CommandContextCore, cmd: Command) => Awaitable<E>;
567
651
  readonly onFactory?: (ctx: Readonly<CommandContext>, cmd: Readonly<Command>) => Awaitable<void>;
568
652
  }
653
+ /**
654
+ * Rendering control options
655
+ * @since v0.27.0
656
+ */
657
+ interface RenderingOptions<G extends GunshiParamsConstraint = DefaultGunshiParams> {
658
+ /**
659
+ * Header rendering configuration
660
+ * - `null`: Disable rendering
661
+ * - `function`: Use custom renderer
662
+ * - `undefined` (when omitted): Use default renderer
663
+ */
664
+ header?: ((ctx: Readonly<CommandContext<G>>) => Promise<string>) | null;
665
+ /**
666
+ * Usage rendering configuration
667
+ * - `null`: Disable rendering
668
+ * - `function`: Use custom renderer
669
+ * - `undefined` (when omitted): Use default renderer
670
+ */
671
+ usage?: ((ctx: Readonly<CommandContext<G>>) => Promise<string>) | null;
672
+ /**
673
+ * Validation errors rendering configuration
674
+ * - `null`: Disable rendering
675
+ * - `function`: Use custom renderer
676
+ * - `undefined` (when omitted): Use default renderer
677
+ */
678
+ validationErrors?: ((ctx: Readonly<CommandContext<G>>, error: AggregateError) => Promise<string>) | null;
679
+ }
569
680
  /**
570
681
  * Command interface.
571
682
  */
@@ -599,6 +710,24 @@ interface Command<G extends GunshiParamsConstraint = DefaultGunshiParams> {
599
710
  * If you will set to `true`, All {@link Command.args} names will be converted to kebab-case.
600
711
  */
601
712
  toKebab?: boolean;
713
+ /**
714
+ * Whether this is an internal command.
715
+ * Internal commands are not shown in help usage.
716
+ * @default false
717
+ * @since v0.27.0
718
+ */
719
+ internal?: boolean;
720
+ /**
721
+ * Whether this command is an entry command.
722
+ * @default undefined
723
+ * @since v0.27.0
724
+ */
725
+ entry?: boolean;
726
+ /**
727
+ * Rendering control options
728
+ * @since v0.27.0
729
+ */
730
+ rendering?: RenderingOptions<G>;
602
731
  }
603
732
  /**
604
733
  * Lazy command interface.
@@ -629,7 +758,7 @@ type CommandExamplesFetcher<G extends GunshiParamsConstraint = DefaultGunshiPara
629
758
  * @param ctx A {@link CommandContext | command context}
630
759
  * @returns void or string (for CLI output)
631
760
  */
632
- type CommandRunner<G extends GunshiParamsConstraint = DefaultGunshiParams> = (ctx: Readonly<CommandContext<G>>) => Awaitable<void | string>;
761
+ type CommandRunner<G extends GunshiParamsConstraint = DefaultGunshiParams> = (ctx: Readonly<CommandContext<G>>) => Awaitable<string | void>;
633
762
  /**
634
763
  * Command loader.
635
764
  * A function that returns a command or command runner.
@@ -642,14 +771,16 @@ type CommandLoader<G extends GunshiParamsConstraint = DefaultGunshiParams> = ()
642
771
  * A function that wraps a command runner to add or modify its behavior.
643
772
  * @param baseRunner The base command runner to decorate
644
773
  * @returns The decorated command runner
774
+ * @since v0.27.0
645
775
  */
646
- type CommandDecorator<G extends GunshiParamsConstraint = DefaultGunshiParams> = (baseRunner: (ctx: Readonly<CommandContext<G>>) => Awaitable<void | string>) => (ctx: Readonly<CommandContext<G>>) => Awaitable<void | string>;
776
+ type CommandDecorator<G extends GunshiParamsConstraint = DefaultGunshiParams> = (baseRunner: (ctx: Readonly<CommandContext<G>>) => Awaitable<string | void>) => (ctx: Readonly<CommandContext<G>>) => Awaitable<string | void>;
647
777
  /**
648
778
  * Renderer decorator type.
649
779
  * A function that wraps a base renderer to add or modify its behavior.
650
780
  * @param baseRenderer The base renderer function to decorate
651
781
  * @param ctx The command context
652
782
  * @returns The decorated result
783
+ * @since v0.27.0
653
784
  */
654
785
  type RendererDecorator<T, G extends GunshiParamsConstraint = DefaultGunshiParams> = (baseRenderer: (ctx: Readonly<CommandContext<G>>) => Promise<T>, ctx: Readonly<CommandContext<G>>) => Promise<T>;
655
786
  /**
@@ -659,9 +790,9 @@ type RendererDecorator<T, G extends GunshiParamsConstraint = DefaultGunshiParams
659
790
  * @param ctx The command context
660
791
  * @param error The aggregate error containing validation errors
661
792
  * @returns The decorated result
793
+ * @since v0.27.0
662
794
  */
663
795
  type ValidationErrorsDecorator<G extends GunshiParamsConstraint = DefaultGunshiParams> = (baseRenderer: (ctx: Readonly<CommandContext<G>>, error: AggregateError) => Promise<string>, ctx: Readonly<CommandContext<G>>, error: AggregateError) => Promise<string>;
664
-
665
796
  //#endregion
666
797
  //#region ../gunshi/src/cli/bone.d.ts
667
798
  /**
@@ -694,6 +825,5 @@ declare function cli<E extends ExtendContext = ExtendContext, G extends GunshiPa
694
825
  * @returns A rendered usage or undefined. if you will use {@link CliOptions.usageSilent} option, it will return rendered usage string.
695
826
  */
696
827
  declare function cli<G extends GunshiParams = DefaultGunshiParams>(argv: string[], entry: Command<G> | CommandRunner<G> | LazyCommand<G>, options?: CliOptions<G>): Promise<string | undefined>;
697
-
698
828
  //#endregion
699
- export { ArgSchema, ArgToken, ArgValues, Args, Awaitable, CliOptions, Command, CommandCallMode, CommandContext, CommandContextCore, CommandContextExtension, CommandDecorator, CommandEnvironment, CommandExamplesFetcher, CommandLoader, CommandRunner, Commandable, DefaultGunshiParams, ExtendContext, ExtractArgs, ExtractExtensions, GunshiParams, GunshiParamsConstraint, LazyCommand, NormalizeToGunshiParams, RendererDecorator, ValidationErrorsDecorator, cli };
829
+ export { type ArgSchema, type ArgToken, type ArgValues, type Args, Awaitable, CliOptions, Command, CommandCallMode, CommandContext, CommandContextCore, CommandContextExtension, CommandDecorator, CommandEnvironment, CommandExamplesFetcher, CommandLoader, CommandRunner, Commandable, DefaultGunshiParams, ExtendContext, ExtractArgExplicitlyProvided, ExtractArgs, ExtractExtensions, GunshiParams, GunshiParamsConstraint, LazyCommand, NormalizeToGunshiParams, RendererDecorator, RenderingOptions, ValidationErrorsDecorator, cli };
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region ../../node_modules/.pnpm/args-tokens@0.20.1/node_modules/args-tokens/lib/parser-Dr4iAGaX.js
1
+ //#region ../../node_modules/.pnpm/args-tokens@0.22.0/node_modules/args-tokens/lib/parser-Dr4iAGaX.js
2
2
  const HYPHEN_CHAR = "-";
3
3
  const HYPHEN_CODE = HYPHEN_CHAR.codePointAt(0);
4
4
  const EQUAL_CHAR = "=";
@@ -188,7 +188,7 @@ function hasOptionValue(value) {
188
188
  }
189
189
 
190
190
  //#endregion
191
- //#region ../../node_modules/.pnpm/args-tokens@0.20.1/node_modules/args-tokens/lib/utils-N7UlhLbz.js
191
+ //#region ../../node_modules/.pnpm/args-tokens@0.22.0/node_modules/args-tokens/lib/utils-N7UlhLbz.js
192
192
  /**
193
193
  * Entry point of utils.
194
194
  *
@@ -205,14 +205,36 @@ function kebabnize(str) {
205
205
  }
206
206
 
207
207
  //#endregion
208
- //#region ../../node_modules/.pnpm/args-tokens@0.20.1/node_modules/args-tokens/lib/resolver-Q4k2fgTW.js
208
+ //#region ../../node_modules/.pnpm/args-tokens@0.22.0/node_modules/args-tokens/lib/resolver-Bcd8oyPt.js
209
209
  const SKIP_POSITIONAL_DEFAULT = -1;
210
210
  /**
211
211
  * Resolve command line arguments.
212
212
  * @param args - An arguments that contains {@link ArgSchema | arguments schema}.
213
213
  * @param tokens - An array of {@link ArgToken | tokens}.
214
214
  * @param resolveArgs - An arguments that contains {@link ResolveArgs | resolve arguments}.
215
- * @returns An object that contains the values of the arguments, positional arguments, rest arguments, and {@link AggregateError | validation errors}.
215
+ * @returns An object that contains the values of the arguments, positional arguments, rest arguments, {@link AggregateError | validation errors}, and explicit provision status.
216
+ *
217
+ * @example
218
+ * ```typescript
219
+ * // passed tokens: --port 3000
220
+ *
221
+ * const { values, explicit } = resolveArgs({
222
+ * port: {
223
+ * type: 'number',
224
+ * default: 8080
225
+ * },
226
+ * host: {
227
+ * type: 'string',
228
+ * default: 'localhost'
229
+ * }
230
+ * }, parsedTokens)
231
+ *
232
+ * values.port // 3000
233
+ * values.host // 'localhost'
234
+ *
235
+ * explicit.port // true (explicitly provided)
236
+ * explicit.host // false (not provided, fallback to default)
237
+ * ```
216
238
  */
217
239
  function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKIP_POSITIONAL_DEFAULT, toKebab = false } = {}) {
218
240
  const skipPositionalIndex = typeof skipPositional === "number" ? Math.max(skipPositional, SKIP_POSITIONAL_DEFAULT) : SKIP_POSITIONAL_DEFAULT;
@@ -312,6 +334,7 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
312
334
  */
313
335
  const values = Object.create(null);
314
336
  const errors = [];
337
+ const explicit = Object.create(null);
315
338
  function checkTokenName(option, schema, token) {
316
339
  return token.name === (schema.type === "boolean" ? schema.negatable && token.name?.startsWith("no-") ? `no-${option}` : option : option);
317
340
  }
@@ -322,6 +345,7 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
322
345
  let positionalsCount = 0;
323
346
  for (const [rawArg, schema] of Object.entries(args)) {
324
347
  const arg = toKebab || schema.toKebab ? kebabnize(rawArg) : rawArg;
348
+ explicit[rawArg] = false;
325
349
  if (schema.required) {
326
350
  const found = optionTokens.find((token) => {
327
351
  return schema.short && token.name === schema.short || token.rawName && hasLongOptionPrefix(token.rawName) && token.name === arg;
@@ -347,6 +371,7 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
347
371
  errors.push(invalid);
348
372
  continue;
349
373
  }
374
+ explicit[rawArg] = true;
350
375
  if (schema.type === "boolean") token.value = void 0;
351
376
  const [parsedValue, error] = parse(token, arg, schema);
352
377
  if (error) errors.push(error);
@@ -362,7 +387,8 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
362
387
  values,
363
388
  positionals: positionalTokens.map((token) => token.value),
364
389
  rest,
365
- error: errors.length > 0 ? new AggregateError(errors) : void 0
390
+ error: errors.length > 0 ? new AggregateError(errors) : void 0,
391
+ explicit
366
392
  };
367
393
  }
368
394
  function parse(token, option, schema) {
@@ -421,7 +447,7 @@ function createTypeError(option, schema) {
421
447
  //#region ../gunshi/src/constants.ts
422
448
  const ANONYMOUS_COMMAND_NAME = "(anonymous)";
423
449
  const NOOP = () => {};
424
- const COMMAND_OPTIONS_DEFAULT = {
450
+ const CLI_OPTIONS_DEFAULT = {
425
451
  name: void 0,
426
452
  description: void 0,
427
453
  version: void 0,
@@ -450,7 +476,9 @@ async function resolveLazyCommand(cmd, name, needRunResolving = false) {
450
476
  name: cmd.commandName,
451
477
  description: cmd.description,
452
478
  args: cmd.args,
453
- examples: cmd.examples
479
+ examples: cmd.examples,
480
+ internal: cmd.internal,
481
+ entry: cmd.entry
454
482
  };
455
483
  if ("resource" in cmd && cmd.resource) baseCommand.resource = cmd.resource;
456
484
  command = Object.assign(create(), baseCommand);
@@ -464,6 +492,8 @@ async function resolveLazyCommand(cmd, name, needRunResolving = false) {
464
492
  command.description = loaded.description;
465
493
  command.args = loaded.args;
466
494
  command.examples = loaded.examples;
495
+ command.internal = loaded.internal;
496
+ command.entry = loaded.entry;
467
497
  if ("resource" in loaded && loaded.resource) command.resource = loaded.resource;
468
498
  } else throw new TypeError(`Cannot resolve command: ${cmd.name || name}`);
469
499
  }
@@ -494,7 +524,7 @@ function deepFreeze(obj, ignores = []) {
494
524
  * @param param A {@link CommandContextParams | parameters} to create a {@link CommandContext | command context}
495
525
  * @returns A {@link CommandContext | command context}, which is readonly
496
526
  */
497
- async function createCommandContext({ args, values, positionals, rest, argv, tokens, command, extensions = {}, cliOptions, callMode = "entry", omitted = false, validationError }) {
527
+ async function createCommandContext({ args, explicit, values, positionals, rest, argv, tokens, command, extensions = {}, cliOptions, callMode = "entry", omitted = false, validationError }) {
498
528
  /**
499
529
  * normailize the options schema and values, to avoid prototype pollution
500
530
  */
@@ -505,7 +535,16 @@ async function createCommandContext({ args, values, positionals, rest, argv, tok
505
535
  /**
506
536
  * setup the environment
507
537
  */
508
- const env = Object.assign(create(), COMMAND_OPTIONS_DEFAULT, cliOptions);
538
+ const env = Object.assign(create(), CLI_OPTIONS_DEFAULT, cliOptions);
539
+ /**
540
+ * apply Command definition's rendering option with highest priority
541
+ */
542
+ if (command.rendering) {
543
+ const { header, usage, validationErrors } = command.rendering;
544
+ if (header !== void 0) env.renderHeader = header;
545
+ if (usage !== void 0) env.renderUsage = usage;
546
+ if (validationErrors !== void 0) env.renderValidationErrors = validationErrors;
547
+ }
509
548
  /**
510
549
  * create the command context
511
550
  */
@@ -516,6 +555,7 @@ async function createCommandContext({ args, values, positionals, rest, argv, tok
516
555
  callMode,
517
556
  env,
518
557
  args: _args,
558
+ explicit,
519
559
  values,
520
560
  positionals,
521
561
  rest,
@@ -619,13 +659,15 @@ function createDecorators() {
619
659
  /**
620
660
  * Factory function for creating a plugin context.
621
661
  * @param decorators - A {@link Decorators} instance.
662
+ * @param initialSubCommands - Initial sub commands map.
622
663
  * @returns A new {@link PluginContext} instance.
623
664
  */
624
- function createPluginContext(decorators) {
665
+ function createPluginContext(decorators, initialSubCommands) {
625
666
  /**
626
667
  * private states
627
668
  */
628
669
  const globalOptions = new Map();
670
+ const subCommands = new Map(initialSubCommands || []);
629
671
  /**
630
672
  * public interfaces
631
673
  */
@@ -638,6 +680,17 @@ function createPluginContext(decorators) {
638
680
  if (globalOptions.has(name)) throw new Error(`Global option '${name}' is already registered`);
639
681
  globalOptions.set(name, schema);
640
682
  },
683
+ get subCommands() {
684
+ return new Map(subCommands);
685
+ },
686
+ addCommand(name, command) {
687
+ if (!name) throw new Error("Command name must be a non-empty string");
688
+ if (subCommands.has(name)) throw new Error(`Command '${name}' is already registered`);
689
+ subCommands.set(name, command);
690
+ },
691
+ hasCommand(name) {
692
+ return subCommands.has(name);
693
+ },
641
694
  decorateHeaderRenderer(decorator) {
642
695
  decorators.addHeaderDecorator(decorator);
643
696
  },
@@ -695,15 +748,16 @@ function resolveDependencies(plugins) {
695
748
  //#region ../gunshi/src/cli/core.ts
696
749
  async function cliCore(argv, entry, options, plugins) {
697
750
  const decorators = createDecorators();
698
- const pluginContext = createPluginContext(decorators);
751
+ const initialSubCommands = createInitialSubCommands(options, entry);
752
+ const pluginContext = createPluginContext(decorators, initialSubCommands);
699
753
  const resolvedPlugins = await applyPlugins(pluginContext, [...plugins, ...options.plugins || []]);
700
- const cliOptions = normalizeCliOptions(options, entry, decorators);
754
+ const cliOptions = normalizeCliOptions(options, decorators, pluginContext);
701
755
  const tokens = parseArgs(argv);
702
756
  const subCommand = getSubCommand(tokens);
703
757
  const { commandName: name, command, callMode } = await resolveCommand(subCommand, entry, cliOptions);
704
758
  if (!command) throw new Error(`Command not found: ${name || ""}`);
705
759
  const args = resolveArguments(pluginContext, getCommandArgs(command));
706
- const { values, positionals, rest, error } = resolveArgs(args, tokens, {
760
+ const { explicit, values, positionals, rest, error } = resolveArgs(args, tokens, {
707
761
  shortGrouping: true,
708
762
  toKebab: command.toKebab,
709
763
  skipPositional: cliOptions.subCommands.size > 0 ? 0 : -1
@@ -711,6 +765,7 @@ async function cliCore(argv, entry, options, plugins) {
711
765
  const omitted = !subCommand;
712
766
  const commandContext = await createCommandContext({
713
767
  args,
768
+ explicit,
714
769
  values,
715
770
  positionals,
716
771
  rest,
@@ -749,13 +804,17 @@ function getCommandArgs(cmd) {
749
804
  function resolveArguments(pluginContext, args) {
750
805
  return Object.assign(create(), Object.fromEntries(pluginContext.globalOptions), args);
751
806
  }
752
- function normalizeCliOptions(options, entry, decorators) {
807
+ function createInitialSubCommands(options, entryCmd) {
753
808
  const subCommands = new Map(options.subCommands);
754
- if (options.subCommands) {
755
- if (isLazyCommand(entry)) subCommands.set(entry.commandName, entry);
756
- else if (typeof entry === "object" && entry.name) subCommands.set(entry.name, entry);
809
+ if ((options.subCommands || subCommands.size > 0) && (isLazyCommand(entryCmd) || typeof entryCmd === "object")) {
810
+ entryCmd.entry = true;
811
+ subCommands.set(resolveEntryName(entryCmd), entryCmd);
757
812
  }
758
- const resolvedOptions = Object.assign(create(), COMMAND_OPTIONS_DEFAULT, options, { subCommands });
813
+ return subCommands;
814
+ }
815
+ function normalizeCliOptions(options, decorators, pluginContext) {
816
+ const subCommands = new Map(pluginContext.subCommands);
817
+ const resolvedOptions = Object.assign(create(), CLI_OPTIONS_DEFAULT, options, { subCommands });
759
818
  if (resolvedOptions.renderHeader === void 0) resolvedOptions.renderHeader = decorators.getHeaderRenderer();
760
819
  if (resolvedOptions.renderUsage === void 0) resolvedOptions.renderUsage = decorators.getUsageRenderer();
761
820
  if (resolvedOptions.renderValidationErrors === void 0) resolvedOptions.renderValidationErrors = decorators.getValidationErrorsRenderer();
@@ -775,7 +834,10 @@ async function resolveCommand(sub, entry, options) {
775
834
  callMode: "entry"
776
835
  };
777
836
  else return {
778
- command: { run: entry },
837
+ command: {
838
+ run: entry,
839
+ entry: true
840
+ },
779
841
  callMode: "entry"
780
842
  };
781
843
  else if (typeof entry === "object") return {
@@ -800,7 +862,7 @@ async function resolveCommand(sub, entry, options) {
800
862
  };
801
863
  }
802
864
  function resolveEntryName(entry) {
803
- return entry.name || ANONYMOUS_COMMAND_NAME;
865
+ return isLazyCommand(entry) ? entry.commandName || ANONYMOUS_COMMAND_NAME : entry.name || ANONYMOUS_COMMAND_NAME;
804
866
  }
805
867
  function getPluginExtensions(plugins) {
806
868
  const extensions = create();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gunshi/bone",
3
3
  "description": "gunshi minimum",
4
- "version": "0.26.3",
4
+ "version": "0.27.0-alpha.10",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
7
7
  "email": "kawakazu80@gmail.com"
@@ -51,15 +51,15 @@
51
51
  }
52
52
  },
53
53
  "devDependencies": {
54
- "deno": "^2.3.3",
55
- "jsr": "^0.13.4",
54
+ "deno": "^2.4.2",
55
+ "jsr": "^0.13.5",
56
56
  "jsr-exports-lint": "^0.4.1",
57
57
  "publint": "^0.3.12",
58
- "tsdown": "^0.12.3",
59
- "@gunshi/definition": "0.26.3",
60
- "@gunshi/plugin-renderer": "0.26.3",
61
- "@gunshi/plugin-global": "0.26.3",
62
- "gunshi": "0.26.3"
58
+ "tsdown": "^0.13.0",
59
+ "@gunshi/definition": "0.27.0-alpha.10",
60
+ "@gunshi/plugin-global": "0.27.0-alpha.10",
61
+ "@gunshi/plugin-renderer": "0.27.0-alpha.10",
62
+ "gunshi": "0.27.0-alpha.10"
63
63
  },
64
64
  "scripts": {
65
65
  "build": "tsdown",