cli-kiss 0.2.1 → 0.2.3

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.
@@ -1,114 +1,89 @@
1
- import { Option, OptionUsage } from "./Option";
2
- import { Positional, PositionalUsage } from "./Positional";
1
+ import { Option, OptionDecoder, OptionUsage } from "./Option";
2
+ import { Positional, PositionalDecoder, PositionalUsage } from "./Positional";
3
3
  import { ReaderArgs } from "./Reader";
4
4
 
5
5
  /**
6
- * Describes an operation the combination of options, positional arguments, and an
7
- * async execution handler that together form the core logic of a CLI command.
6
+ * Options, positionals, and an async handler that together form the logic of a CLI command.
8
7
  *
9
- * An `OperationDescriptor` is created with {@link operation} and passed to
10
- * {@link command}, {@link commandWithSubcommands}, or {@link commandChained} to build
11
- * a full {@link CommandDescriptor}.
8
+ * Created with {@link operation} and passed to {@link command},
9
+ * {@link commandWithSubcommands}, or {@link commandChained}.
12
10
  *
13
- * @typeParam Input - The context value the handler receives at execution time (forwarded
14
- * from the parent command's context or from a preceding chained operation).
15
- * @typeParam Output - The value the handler produces. For leaf operations this is
16
- * typically `void`; for intermediate stages it is the payload forwarded to the next
17
- * command in a chain.
11
+ * @typeParam Context - Injected at execution time; forwarded to handlers. Use to inject dependencies.
12
+ * @typeParam Result - Value produced on execution; typically `void` for leaf commands.
18
13
  */
19
- export type OperationDescriptor<Input, Output> = {
14
+ export type Operation<Context, Result> = {
20
15
  /**
21
- * Returns usage metadata (options and positionals) without consuming any arguments.
22
- * Called by the parent command factory when building the help/usage output.
16
+ * Returns usage metadata without consuming any arguments.
23
17
  */
24
18
  generateUsage(): OperationUsage;
25
19
  /**
26
- * Parses options and positionals from `readerArgs` and returns an
27
- * {@link OperationFactory} that can create a ready-to-execute
28
- * {@link OperationInstance}.
29
- *
30
- * Any parse error (unknown option, type mismatch, etc.) is captured and re-thrown
31
- * when {@link OperationFactory.createInstance} is called.
32
- *
33
- * @param readerArgs - The shared argument reader. Options are registered on it and
34
- * positionals are consumed in declaration order.
20
+ * Consumes args from `readerArgs` and returns an {@link OperationDecoder}.
35
21
  */
36
- createFactory(readerArgs: ReaderArgs): OperationFactory<Input, Output>;
22
+ consumeAndMakeDecoder(
23
+ readerArgs: ReaderArgs,
24
+ ): OperationDecoder<Context, Result>;
37
25
  };
38
26
 
39
27
  /**
40
- * Produced by {@link OperationDescriptor.createFactory} after argument parsing.
41
- * Instantiating it finalises value extraction and produces an {@link OperationInstance}.
28
+ * Produced by {@link Operation.consumeAndMakeDecoder}.
42
29
  *
43
- * @typeParam Input - Forwarded from the parent {@link OperationDescriptor}.
44
- * @typeParam Output - Forwarded from the parent {@link OperationDescriptor}.
30
+ * @typeParam Context - See {@link Operation}.
31
+ * @typeParam Result - See {@link Operation}.
45
32
  */
46
- export type OperationFactory<Input, Output> = {
33
+ export type OperationDecoder<Context, Result> = {
47
34
  /**
48
- * Extracts the final parsed values for all options and returns an
49
- * {@link OperationInstance} ready for execution.
35
+ * Creates a ready-to-execute {@link OperationInterpreter}.
50
36
  *
51
- * @throws {@link TypoError} if any option or positional validation failed during
52
- * {@link OperationDescriptor.createFactory}.
37
+ * @throws {@link TypoError} if parsing or decoding failed.
53
38
  */
54
- createInstance(): OperationInstance<Input, Output>;
39
+ decodeAndMakeInterpreter(): OperationInterpreter<Context, Result>;
55
40
  };
56
41
 
57
42
  /**
58
- * A fully parsed, ready-to-execute operation.
43
+ * A fully parsed, decoded and ready-to-execute operation.
59
44
  *
60
- * @typeParam Input - The value the caller must supply as context.
61
- * @typeParam Output - The value produced on successful execution.
45
+ * @typeParam Context - Caller-supplied context.
46
+ * @typeParam Result - Value produced on success.
62
47
  */
63
- export type OperationInstance<Input, Output> = {
48
+ export type OperationInterpreter<Context, Result> = {
64
49
  /**
65
- * Runs the operation handler with the provided input context and the parsed
66
- * option/positional values.
67
- *
68
- * @param input - Context from the parent command (or the root context supplied to
69
- * {@link runAndExit}).
70
- * @returns A promise resolving to the handler's return value.
50
+ * Executes with the provided context.
71
51
  */
72
- executeWithContext(input: Input): Promise<Output>;
52
+ executeWithContext(context: Context): Promise<Result>;
73
53
  };
74
54
 
75
55
  /**
76
- * Collected usage metadata produced by {@link OperationDescriptor.generateUsage}.
77
- * Consumed by the parent command factory when building {@link CommandUsage}.
56
+ * Usage metadata produced by {@link Operation.generateUsage}.
57
+ * Consumed when building {@link CommandUsage}.
78
58
  */
79
59
  export type OperationUsage = {
80
- /** Usage descriptors for all options registered by this operation. */
60
+ /**
61
+ * Usage descriptors for all registered options.
62
+ */
81
63
  options: Array<OptionUsage>;
82
- /** Usage descriptors for all positionals declared by this operation, in order. */
64
+ /**
65
+ * Usage descriptors for all declared positionals, in order.
66
+ */
83
67
  positionals: Array<PositionalUsage>;
84
68
  };
85
69
 
86
70
  /**
87
- * Creates an {@link OperationDescriptor} from a set of options, positionals, and an
88
- * async handler function.
71
+ * Creates an {@link Operation} from options, positionals, and an async handler.
89
72
  *
90
- * The `handler` receives:
91
- * - `context` the value passed down from the parent command (or from
92
- * {@link runAndExit}).
93
- * - `inputs.options` — an object whose keys match those declared in `inputs.options` and whose values are
94
- * the parsed option values.
95
- * - `inputs.positionals` — a tuple whose elements match `inputs.positionals` and whose
96
- * values are the parsed positional values, in declaration order.
73
+ * The `handler` receives the parent `context` and an `inputs` object with
74
+ * `options` (keyed by the same names declared in `inputs.options`) and
75
+ * `positionals` (a tuple in declaration order).
97
76
  *
98
- * @typeParam Context - The context type accepted by the handler.
99
- * @typeParam Result - The return type of the handler.
100
- * @typeParam Options - Object type mapping option keys to their parsed value types.
101
- * @typeParam Positionals - Tuple type of parsed positional value types, in order.
77
+ * @typeParam Context - Context type accepted by the handler.
78
+ * @typeParam Result - Return type of the handler.
79
+ * @typeParam Options - Map of option keys to parsed value types.
80
+ * @typeParam Positionals - Tuple of parsed positional value types, in order.
102
81
  *
103
- * @param inputs - Declares the options and positionals this operation accepts.
104
- * @param inputs.options - A map from arbitrary keys to {@link Option} descriptors.
105
- * The same keys appear in `handler`'s `inputs.options` argument.
106
- * @param inputs.positionals - An ordered array of {@link Positional} descriptors.
107
- * Their parsed values appear in `handler`'s `inputs.positionals` argument, in the
108
- * same order.
109
- * @param handler - The async function that implements the command logic. Receives the
110
- * execution context and all parsed inputs.
111
- * @returns An {@link OperationDescriptor} ready to be composed into a command.
82
+ * @param inputs - Options and positionals this operation accepts.
83
+ * @param inputs.options - Map of keys to {@link Option} descriptors.
84
+ * @param inputs.positionals - Ordered array of {@link Positional} descriptors.
85
+ * @param handler - Async function implementing the command logic.
86
+ * @returns An {@link Operation} ready to be composed into a command.
112
87
  *
113
88
  * @example
114
89
  * ```ts
@@ -140,9 +115,12 @@ export function operation<
140
115
  },
141
116
  handler: (
142
117
  context: Context,
143
- inputs: { options: Options; positionals: Positionals },
118
+ inputs: {
119
+ options: Options;
120
+ positionals: Positionals;
121
+ },
144
122
  ) => Promise<Result>,
145
- ): OperationDescriptor<Context, Result> {
123
+ ): Operation<Context, Result> {
146
124
  return {
147
125
  generateUsage() {
148
126
  const optionsUsage = new Array<OptionUsage>();
@@ -158,21 +136,29 @@ export function operation<
158
136
  }
159
137
  return { options: optionsUsage, positionals: positionalsUsage };
160
138
  },
161
- createFactory(readerArgs: ReaderArgs) {
162
- const optionsGetters: any = {};
139
+ consumeAndMakeDecoder(readerArgs: ReaderArgs) {
140
+ const optionsDecoders: Record<string, OptionDecoder<any>> = {};
163
141
  for (const optionKey in inputs.options) {
164
142
  const optionInput = inputs.options[optionKey]!;
165
- optionsGetters[optionKey] = optionInput.createGetter(readerArgs);
143
+ optionsDecoders[optionKey] =
144
+ optionInput.registerAndMakeDecoder(readerArgs);
166
145
  }
167
- const positionalsValues: any = [];
146
+ const positionalsDecoders: Array<PositionalDecoder<any>> = [];
168
147
  for (const positionalInput of inputs.positionals) {
169
- positionalsValues.push(positionalInput.consumePositionals(readerArgs));
148
+ positionalsDecoders.push(
149
+ positionalInput.consumeAndMakeDecoder(readerArgs),
150
+ );
170
151
  }
171
152
  return {
172
- createInstance() {
153
+ decodeAndMakeInterpreter() {
173
154
  const optionsValues: any = {};
174
- for (const optionKey in optionsGetters) {
175
- optionsValues[optionKey] = optionsGetters[optionKey]!.getValue();
155
+ for (const optionKey in optionsDecoders) {
156
+ optionsValues[optionKey] =
157
+ optionsDecoders[optionKey]!.getAndDecodeValue();
158
+ }
159
+ const positionalsValues: any = [];
160
+ for (const positionalDecoder of positionalsDecoders) {
161
+ positionalsValues.push(positionalDecoder.decodeValue());
176
162
  }
177
163
  return {
178
164
  executeWithContext(context: Context) {
package/src/lib/Option.ts CHANGED
@@ -10,95 +10,78 @@ import {
10
10
  } from "./Typo";
11
11
 
12
12
  /**
13
- * Describes a single CLI option (a flag or a valued option) together with its parsing
14
- * and usage-generation logic.
13
+ * A CLI option (flag or valued) with its parsing and usage-generation logic.
15
14
  *
16
- * Options are created with {@link optionFlag}, {@link optionSingleValue}, or
17
- * {@link optionRepeatable} and are passed via the `options` map of {@link operation}.
15
+ * Created with {@link optionFlag}, {@link optionSingleValue}, or
16
+ * {@link optionRepeatable} and passed via the `options` map of {@link operation}.
18
17
  *
19
- * @typeParam Value - The TypeScript type of the parsed option value.
20
- * - `boolean` for flags created with {@link optionFlag}.
21
- * - `T` for single-value options created with {@link optionSingleValue}.
22
- * - `Array<T>` for repeatable options created with {@link optionRepeatable}.
18
+ * @typeParam Value - Decoded value type.
23
19
  */
24
20
  export type Option<Value> = {
25
- /** Returns human-readable metadata used to render the `Options:` section of help. */
21
+ /**
22
+ * Returns metadata used to render the `Options:` section of help.
23
+ */
26
24
  generateUsage(): OptionUsage;
27
25
  /**
28
- * Registers the option on `readerOptions` so the argument reader recognises it, and
29
- * returns an {@link OptionGetter} that can later retrieve the parsed value(s).
26
+ * Registers the option on `readerOptions` and returns an {@link OptionDecoder}.
27
+ */
28
+ registerAndMakeDecoder(readerOptions: ReaderOptions): OptionDecoder<Value>;
29
+ };
30
+
31
+ /**
32
+ * Produced by {@link Option.registerAndMakeDecoder}.
33
+ *
34
+ * @typeParam Value - Decoded value type.
35
+ */
36
+ export type OptionDecoder<Value> = {
37
+ /**
38
+ * Returns the decoded option value.
30
39
  *
31
- * @param readerOptions - The shared {@link ReaderArgs} that will parse the raw
32
- * command-line tokens.
40
+ * @throws {@link TypoError} if decoding failed.
33
41
  */
34
- createGetter(readerOptions: ReaderOptions): OptionGetter<Value>;
42
+ getAndDecodeValue(): Value;
35
43
  };
36
44
 
37
45
  /**
38
- * Human-readable metadata for a single CLI option, used to render the `Options:` section
46
+ * Human-readable metadata for a single option, used to render the `Options:` section
39
47
  * of the help output produced by {@link usageToStyledLines}.
40
48
  */
41
49
  export type OptionUsage = {
42
- /** Short description of what the option does. */
43
- description: string | undefined;
44
50
  /**
45
- * Optional supplementary note shown in parentheses next to the description.
46
- * Suitable for short caveats such as `"required"` or `"defaults to 42"`.
51
+ * Long-form name without `--` (e.g. `"verbose"`).
47
52
  */
48
- hint: string | undefined;
53
+ long: Lowercase<string>;
49
54
  /**
50
- * The primary long-form name of the option, without the `--` prefix (e.g. `"verbose"`).
51
- * The user passes this as `--verbose` on the command line.
55
+ * Short-form name without `-` (e.g. `"v"`).
52
56
  */
53
- long: Lowercase<string>; // TODO - better type for long option names ?
57
+ short: string | undefined;
54
58
  /**
55
- * The optional short-form name of the option, without the `-` prefix (e.g. `"v"`).
56
- * The user passes this as `-v` on the command line.
59
+ * Help text in usage.
57
60
  */
58
- short: string | undefined;
61
+ description: string | undefined;
59
62
  /**
60
- * The value placeholder label shown after the long option name in the help output
61
- * (e.g. `"<FILE>"`). `undefined` for flags that take no value.
63
+ * Short note shown in parentheses.
62
64
  */
63
- label: Uppercase<string> | undefined;
64
- };
65
-
66
- /**
67
- * Retrieves the parsed value for a registered option after argument parsing is complete.
68
- *
69
- * Returned by {@link Option.createGetter} and called by {@link OperationFactory.createInstance}.
70
- *
71
- * @typeParam Value - The TypeScript type of the parsed value.
72
- */
73
- export type OptionGetter<Value> = {
65
+ hint: string | undefined;
74
66
  /**
75
- * Returns the fully decoded and validated value for the option.
76
- *
77
- * @throws {@link TypoError} if the option appeared more times than allowed, the value
78
- * failed type decoding, or a required default could not be computed.
67
+ * Value placeholder in help (e.g. `"<FILE>"`). `undefined` for flags.
79
68
  */
80
- getValue(): Value;
69
+ label: Uppercase<string> | undefined;
81
70
  };
82
71
 
83
72
  /**
84
- * Creates a boolean flag option an option that the user passes without a value (e.g.
85
- * `--verbose`) to signal `true`, or can explicitly set with `--flag=true` / `--flag=no`.
73
+ * Creates a boolean flag option (`--verbose`, optionally `--flag=no`).
86
74
  *
87
- * **Parsing rules:**
88
- * - Absent `false` (or the return value of `default()` when provided).
89
- * - `--flag` / `--flag=true` / `--flag=yes` → `true`.
90
- * - `--flag=false` / `--flag=no` → `false`.
91
- * - Specified more than once → {@link TypoError} ("Must not be set multiple times").
75
+ * Parsing: absent → `false`; `--flag` / `--flag=yes` → `true`; `--flag=no` → `false`;
76
+ * specified more than once {@link TypoError}.
92
77
  *
93
- * @param definition - Configuration for the flag.
94
- * @param definition.long - Primary long-form name (without `--`). Must be lowercase.
95
- * @param definition.short - Optional short-form name (without `-`).
96
- * @param definition.description - Human-readable description for the help output.
97
- * @param definition.hint - Optional supplementary note shown in parentheses.
98
- * @param definition.aliases - Additional long/short names that the parser also
99
- * recognises as this flag.
100
- * @param definition.default - Factory for the default value when the flag is absent.
101
- * Defaults to `() => false` when omitted.
78
+ * @param definition - Flag configuration.
79
+ * @param definition.long - Long-form name (without `--`).
80
+ * @param definition.short - Short-form name (without `-`).
81
+ * @param definition.description - Help text.
82
+ * @param definition.hint - Short note shown in parentheses.
83
+ * @param definition.aliases - Additional names.
84
+ * @param definition.default - Default when absent. Defaults to `() => false`.
102
85
  * @returns An {@link Option}`<boolean>`.
103
86
  *
104
87
  * @example
@@ -129,13 +112,13 @@ export function optionFlag(definition: {
129
112
  label: undefined,
130
113
  };
131
114
  },
132
- createGetter(readerOptions: ReaderOptions) {
115
+ registerAndMakeDecoder(readerOptions: ReaderOptions) {
133
116
  const key = registerOption(readerOptions, {
134
117
  ...definition,
135
118
  valued: false,
136
119
  });
137
120
  return {
138
- getValue() {
121
+ getAndDecodeValue() {
139
122
  const optionValues = readerOptions.getOptionValues(key);
140
123
  if (optionValues.length > 1) {
141
124
  throw new TypoError(
@@ -159,7 +142,13 @@ export function optionFlag(definition: {
159
142
  );
160
143
  }
161
144
  }
162
- return decodeValue(definition.long, label, typeBoolean, optionValue);
145
+ return decodeValue({
146
+ long: definition.long,
147
+ short: definition.short,
148
+ label,
149
+ type: typeBoolean,
150
+ input: optionValue,
151
+ });
163
152
  },
164
153
  };
165
154
  },
@@ -167,32 +156,22 @@ export function optionFlag(definition: {
167
156
  }
168
157
 
169
158
  /**
170
- * Creates an option that accepts exactly one value (e.g. `--output dist/` or
171
- * `--output=dist/`).
159
+ * Creates an option that accepts exactly one value (e.g. `--output dist/`).
172
160
  *
173
- * **Parsing rules:**
174
- * - Absent `definition.default()` is called. If the default factory throws, a
175
- * {@link TypoError} is produced.
176
- * - Specified once → the value is decoded with `definition.type`.
177
- * - Specified more than once → {@link TypoError} ("Requires a single value, but got
178
- * multiple").
161
+ * Parsing: absent → `default()`; once → decoded with `type`; more than once → {@link TypoError}.
162
+ * Value syntax: `--long value`, `--long=value`, `-s value`, `-s=value`, `-svalue`.
179
163
  *
180
- * **Value syntax:** `--long value`, `--long=value`, or (if `short` is set) `-s value`,
181
- * `-s=value`, or `-svalue`.
164
+ * @typeParam Value - Type produced by the decoder.
182
165
  *
183
- * @typeParam Value - The TypeScript type produced by the type decoder.
184
- *
185
- * @param definition - Configuration for the option.
186
- * @param definition.long - Primary long-form name (without `--`). Must be lowercase.
187
- * @param definition.short - Optional short-form name (without `-`).
188
- * @param definition.description - Human-readable description for the help output.
189
- * @param definition.hint - Optional supplementary note shown in parentheses.
190
- * @param definition.aliases - Additional long/short names the parser also recognises.
191
- * @param definition.label - Custom label shown in the help output (e.g. `"FILE"`).
192
- * Defaults to the uppercased `type.content`.
193
- * @param definition.type - The {@link Type} used to decode the raw string value.
194
- * @param definition.default - Factory for the default value when the option is absent.
195
- * Throw an error from this factory to make the option effectively required.
166
+ * @param definition - Option configuration.
167
+ * @param definition.long - Long-form name (without `--`).
168
+ * @param definition.short - Short-form name (without `-`).
169
+ * @param definition.description - Help text.
170
+ * @param definition.hint - Short note shown in parentheses.
171
+ * @param definition.aliases - Additional names.
172
+ * @param definition.label - Value placeholder in help. Defaults to uppercased `type.content`.
173
+ * @param definition.type - Decoder for the raw string value.
174
+ * @param definition.default - Default when absent. Throw to make the option required.
196
175
  * @returns An {@link Option}`<Value>`.
197
176
  *
198
177
  * @example
@@ -228,13 +207,13 @@ export function optionSingleValue<Value>(definition: {
228
207
  label: label as Uppercase<string>,
229
208
  };
230
209
  },
231
- createGetter(readerOptions: ReaderOptions) {
210
+ registerAndMakeDecoder(readerOptions: ReaderOptions) {
232
211
  const key = registerOption(readerOptions, {
233
212
  ...definition,
234
213
  valued: true,
235
214
  });
236
215
  return {
237
- getValue() {
216
+ getAndDecodeValue() {
238
217
  const optionValues = readerOptions.getOptionValues(key);
239
218
  if (optionValues.length > 1) {
240
219
  throw new TypoError(
@@ -258,12 +237,13 @@ export function optionSingleValue<Value>(definition: {
258
237
  );
259
238
  }
260
239
  }
261
- return decodeValue(
262
- definition.long,
240
+ return decodeValue({
241
+ long: definition.long,
242
+ short: definition.short,
263
243
  label,
264
- definition.type,
265
- optionValue,
266
- );
244
+ type: definition.type,
245
+ input: optionValue,
246
+ });
267
247
  },
268
248
  };
269
249
  },
@@ -271,30 +251,21 @@ export function optionSingleValue<Value>(definition: {
271
251
  }
272
252
 
273
253
  /**
274
- * Creates an option that can be specified any number of times, collecting all provided
275
- * values into an array (e.g. `--file a.ts --file b.ts`).
254
+ * Creates an option that collects every occurrence into an array (e.g. `--file a.ts --file b.ts`).
276
255
  *
277
- * **Parsing rules:**
278
- * - Absent empty array `[]`.
279
- * - Specified N times → array of N decoded values, in the order they appear on the
280
- * command line.
281
- * - Each occurrence is decoded independently with `definition.type`.
256
+ * Parsing: absent → `[]`; N occurrences → array of N decoded values in order.
257
+ * Value syntax: `--long value`, `--long=value`, `-s value`, `-s=value`, `-svalue`.
282
258
  *
283
- * **Value syntax:** `--long value`, `--long=value`, or (if `short` is set) `-s value`,
284
- * `-s=value`, or `-svalue`.
259
+ * @typeParam Value - Type produced by the decoder for each occurrence.
285
260
  *
286
- * @typeParam Value - The TypeScript type produced by the type decoder for each
287
- * occurrence.
288
- *
289
- * @param definition - Configuration for the option.
290
- * @param definition.long - Primary long-form name (without `--`). Must be lowercase.
291
- * @param definition.short - Optional short-form name (without `-`).
292
- * @param definition.description - Human-readable description for the help output.
293
- * @param definition.hint - Optional supplementary note shown in parentheses.
294
- * @param definition.aliases - Additional long/short names the parser also recognises.
295
- * @param definition.label - Custom label shown in the help output (e.g. `"FILE"`).
296
- * Defaults to the uppercased `type.content`.
297
- * @param definition.type - The {@link Type} used to decode each raw string value.
261
+ * @param definition - Option configuration.
262
+ * @param definition.long - Long-form name (without `--`).
263
+ * @param definition.short - Short-form name (without `-`).
264
+ * @param definition.description - Help text.
265
+ * @param definition.hint - Short note shown in parentheses.
266
+ * @param definition.aliases - Additional names.
267
+ * @param definition.label - Value placeholder in help. Defaults to uppercased `type.content`.
268
+ * @param definition.type - Decoder applied to each raw string value.
298
269
  * @returns An {@link Option}`<Array<Value>>`.
299
270
  *
300
271
  * @example
@@ -330,16 +301,22 @@ export function optionRepeatable<Value>(definition: {
330
301
  label: label as Uppercase<string>,
331
302
  };
332
303
  },
333
- createGetter(readerOptions: ReaderOptions) {
304
+ registerAndMakeDecoder(readerOptions: ReaderOptions) {
334
305
  const key = registerOption(readerOptions, {
335
306
  ...definition,
336
307
  valued: true,
337
308
  });
338
309
  return {
339
- getValue() {
310
+ getAndDecodeValue() {
340
311
  const optionValues = readerOptions.getOptionValues(key);
341
312
  return optionValues.map((optionValue) =>
342
- decodeValue(definition.long, label, definition.type, optionValue),
313
+ decodeValue({
314
+ long: definition.long,
315
+ short: definition.short,
316
+ label,
317
+ type: definition.type,
318
+ input: optionValue,
319
+ }),
343
320
  );
344
321
  },
345
322
  };
@@ -347,22 +324,28 @@ export function optionRepeatable<Value>(definition: {
347
324
  };
348
325
  }
349
326
 
350
- function decodeValue<Value>(
351
- long: string,
352
- label: string,
353
- type: Type<Value>,
354
- value: string,
355
- ): Value {
327
+ function decodeValue<Value>(params: {
328
+ long: string;
329
+ short: string | undefined;
330
+ label: string;
331
+ type: Type<Value>;
332
+ input: string;
333
+ }): Value {
356
334
  return TypoError.tryWithContext(
357
- () => type.decoder(value),
358
- () =>
359
- new TypoText(
360
- new TypoString(`--${long}`, typoStyleConstants),
361
- new TypoString(`: `),
362
- new TypoString(label, typoStyleUserInput),
363
- new TypoString(`: `),
364
- new TypoString(type.content, typoStyleLogic),
365
- ),
335
+ () => params.type.decoder(params.input),
336
+ () => {
337
+ const text = new TypoText();
338
+ if (params.short) {
339
+ text.pushString(new TypoString(`-${params.short}`, typoStyleConstants));
340
+ text.pushString(new TypoString(`, `));
341
+ }
342
+ text.pushString(new TypoString(`--${params.long}`, typoStyleConstants));
343
+ text.pushString(new TypoString(`: `));
344
+ text.pushString(new TypoString(params.label, typoStyleUserInput));
345
+ text.pushString(new TypoString(`: `));
346
+ text.pushString(new TypoString(params.type.content, typoStyleLogic));
347
+ return text;
348
+ },
366
349
  );
367
350
  }
368
351