cli-kiss 0.2.6 → 0.2.8
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 +62 -4
- package/dist/index.d.ts +135 -128
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/docs/.vitepress/config.mts +1 -1
- package/docs/.vitepress/theme/Layout.vue +16 -0
- package/docs/.vitepress/theme/index.ts +5 -1
- package/docs/.vitepress/theme/style.css +5 -1
- package/docs/guide/02_commands.md +1 -1
- package/docs/guide/03_options.md +11 -11
- package/docs/guide/05_input_types.md +9 -10
- package/docs/guide/06_run_as_cli.md +1 -1
- package/docs/index.md +2 -2
- package/docs/public/favicon.ico +0 -0
- package/docs/public/logo.png +0 -0
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/lib/Command.ts +50 -30
- package/src/lib/Operation.ts +29 -21
- package/src/lib/Option.ts +198 -133
- package/src/lib/Positional.ts +46 -24
- package/src/lib/Reader.ts +194 -207
- package/src/lib/Run.ts +19 -8
- package/src/lib/Suggest.ts +78 -0
- package/src/lib/Type.ts +46 -48
- package/src/lib/Typo.ts +72 -47
- package/src/lib/Usage.ts +13 -13
- package/tests/unit.Reader.commons.ts +92 -116
- package/tests/unit.Reader.parsings.ts +14 -26
- package/tests/unit.Reader.shortBig.ts +81 -96
- package/tests/unit.command.aliases.ts +100 -0
- package/tests/unit.command.execute.ts +1 -1
- package/tests/unit.command.usage.ts +12 -6
- package/tests/unit.fuzzed.alternatives.ts +43 -0
- package/tests/unit.runner.colors.ts +11 -35
- package/tests/unit.runner.cycle.ts +181 -128
- package/tests/unit.runner.errors.ts +26 -19
- package/docs/public/hero.png +0 -0
- package/tests/unit.Reader.aliases.ts +0 -62
package/src/lib/Type.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { statSync } from "fs";
|
|
2
|
+
import { suggestTextPushMessage } from "./Suggest";
|
|
2
3
|
import {
|
|
3
4
|
TypoError,
|
|
4
5
|
TypoString,
|
|
@@ -19,15 +20,15 @@ import {
|
|
|
19
20
|
*/
|
|
20
21
|
export type Type<Value> = {
|
|
21
22
|
/**
|
|
22
|
-
* Human-readable name shown in help and errors (e.g. `"name"`, `"
|
|
23
|
+
* Human-readable name shown in help and errors (e.g. `"name"`, `"context"`).
|
|
23
24
|
*/
|
|
24
|
-
content: string;
|
|
25
|
+
content: string; // TODO - add an enforcement mechanism for casing ?
|
|
25
26
|
/**
|
|
26
27
|
* Decodes a raw CLI string into `Value`.
|
|
27
28
|
*
|
|
28
29
|
* @param input - Raw string from the command line.
|
|
29
30
|
* @returns The decoded value.
|
|
30
|
-
* @throws
|
|
31
|
+
* @throws on invalid input.
|
|
31
32
|
*/
|
|
32
33
|
decoder(input: string): Value;
|
|
33
34
|
};
|
|
@@ -50,20 +51,19 @@ export function typeBoolean(name?: string): Type<boolean> {
|
|
|
50
51
|
return {
|
|
51
52
|
content: name ?? "boolean",
|
|
52
53
|
decoder(input: string) {
|
|
53
|
-
const
|
|
54
|
-
if (typeBooleanValuesTrue.has(
|
|
54
|
+
const lowerInput = input.toLowerCase();
|
|
55
|
+
if (typeBooleanValuesTrue.has(lowerInput)) {
|
|
55
56
|
return true;
|
|
56
57
|
}
|
|
57
|
-
if (typeBooleanValuesFalse.has(
|
|
58
|
+
if (typeBooleanValuesFalse.has(lowerInput)) {
|
|
58
59
|
return false;
|
|
59
60
|
}
|
|
60
61
|
throwInvalidValue("a boolean", input);
|
|
61
62
|
},
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
export const typeBooleanValuesFalse = new Set(["false", "no", "off", "n"]);
|
|
65
|
+
const typeBooleanValuesTrue = new Set(["true", "yes", "on", "y"]);
|
|
66
|
+
const typeBooleanValuesFalse = new Set(["false", "no", "off", "n"]);
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* Parses a date/time string via `Date.parse`.
|
|
@@ -94,7 +94,7 @@ export function typeDatetime(name?: string): Type<Date> {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
|
-
* Parses a string to `number` via `Number()`; `NaN` throws
|
|
97
|
+
* Parses a string to `number` via `Number()`; `NaN` throws.
|
|
98
98
|
*
|
|
99
99
|
* @example
|
|
100
100
|
* ```ts
|
|
@@ -122,7 +122,7 @@ export function typeNumber(name?: string): Type<number> {
|
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
124
|
* Parses an integer string to `bigint` via `BigInt()`.
|
|
125
|
-
* Floats and non-numeric strings
|
|
125
|
+
* Floats and non-numeric strings throws.
|
|
126
126
|
*
|
|
127
127
|
* @example
|
|
128
128
|
* ```ts
|
|
@@ -146,7 +146,7 @@ export function typeInteger(name?: string): Type<bigint> {
|
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
148
|
* Parses an absolute URL string to a `URL` object.
|
|
149
|
-
* Relative or malformed URLs
|
|
149
|
+
* Relative or malformed URLs throws.
|
|
150
150
|
*
|
|
151
151
|
* @example
|
|
152
152
|
* ```ts
|
|
@@ -315,7 +315,6 @@ export function typePath(
|
|
|
315
315
|
|
|
316
316
|
/**
|
|
317
317
|
* Creates a {@link Type}`<string>` that only accepts a fixed set of values.
|
|
318
|
-
* Out-of-set inputs throw {@link TypoError} listing up to 5 valid options.
|
|
319
318
|
*
|
|
320
319
|
* @param name - Name shown in help and errors.
|
|
321
320
|
* @param values - Ordered list of accepted values.
|
|
@@ -325,53 +324,50 @@ export function typePath(
|
|
|
325
324
|
* ```ts
|
|
326
325
|
* const typeEnv = typeChoice("environment", ["dev", "staging", "prod"]);
|
|
327
326
|
* typeEnv.decoder("prod") // → "prod"
|
|
328
|
-
* typeEnv.decoder("unknown") // throws
|
|
327
|
+
* typeEnv.decoder("unknown") // throws
|
|
329
328
|
* ```
|
|
330
329
|
*/
|
|
331
330
|
export function typeChoice<const Value extends string>(
|
|
332
331
|
name: string,
|
|
333
332
|
values: Array<Value>,
|
|
334
|
-
caseSensitive: boolean =
|
|
333
|
+
caseSensitive: boolean = true,
|
|
335
334
|
): Type<Value> {
|
|
335
|
+
if (values.length === 0) {
|
|
336
|
+
throw new Error("At least one value is required");
|
|
337
|
+
}
|
|
336
338
|
const normalize = caseSensitive
|
|
337
|
-
? (
|
|
338
|
-
: (
|
|
339
|
-
const
|
|
339
|
+
? (input: string) => input
|
|
340
|
+
: (input: string) => input.toLowerCase();
|
|
341
|
+
const valueByNormalized = new Map(
|
|
342
|
+
values.map((value) => [normalize(value), value]),
|
|
343
|
+
);
|
|
340
344
|
return {
|
|
341
345
|
content: name,
|
|
342
346
|
decoder(input: string) {
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
return original;
|
|
347
|
+
const value = valueByNormalized.get(normalize(input));
|
|
348
|
+
if (value !== undefined) {
|
|
349
|
+
return value;
|
|
347
350
|
}
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
throw new TypoError(
|
|
360
|
-
new TypoText(
|
|
361
|
-
new TypoString(`Invalid value: `),
|
|
362
|
-
new TypoString(`"${input}"`, typoStyleQuote),
|
|
363
|
-
new TypoString(` (expected one of: `),
|
|
364
|
-
...valuesPreview,
|
|
365
|
-
new TypoString(`)`),
|
|
366
|
-
),
|
|
351
|
+
const errorText = new TypoText();
|
|
352
|
+
errorText.push(new TypoString(`Unknown value: `));
|
|
353
|
+
errorText.push(new TypoString(`"${input}"`, typoStyleQuote));
|
|
354
|
+
errorText.push(new TypoString(`.`));
|
|
355
|
+
suggestTextPushMessage(
|
|
356
|
+
errorText,
|
|
357
|
+
input,
|
|
358
|
+
values.map((value) => ({
|
|
359
|
+
reference: value,
|
|
360
|
+
hint: new TypoString(`"${value}"`, typoStyleQuote),
|
|
361
|
+
})),
|
|
367
362
|
);
|
|
363
|
+
throw new TypoError(errorText);
|
|
368
364
|
},
|
|
369
365
|
};
|
|
370
366
|
}
|
|
371
367
|
|
|
372
368
|
/**
|
|
373
369
|
* Splits a delimited string into a typed tuple.
|
|
374
|
-
* Each part is decoded by the corresponding element type; wrong count or decode failure throws
|
|
370
|
+
* Each part is decoded by the corresponding element type; wrong count or decode failure throws.
|
|
375
371
|
*
|
|
376
372
|
* @typeParam Elements - Tuple of decoded value types (inferred from `elementTypes`).
|
|
377
373
|
*
|
|
@@ -383,8 +379,9 @@ export function typeChoice<const Value extends string>(
|
|
|
383
379
|
* ```ts
|
|
384
380
|
* const typePoint = typeTuple([typeNumber("x"), typeNumber("y")]);
|
|
385
381
|
* typePoint.decoder("3.14,2.71") // → [3.14, 2.71]
|
|
386
|
-
* typePoint.decoder("1
|
|
387
|
-
* typePoint.decoder("
|
|
382
|
+
* typePoint.decoder("1") // throws
|
|
383
|
+
* typePoint.decoder("1,2,3") // throws
|
|
384
|
+
* typePoint.decoder("x,2") // throws
|
|
388
385
|
* ```
|
|
389
386
|
*/
|
|
390
387
|
export function typeTuple<const Elements extends Array<any>>(
|
|
@@ -403,6 +400,7 @@ export function typeTuple<const Elements extends Array<any>>(
|
|
|
403
400
|
new TypoString(`Found ${splits.length} splits: `),
|
|
404
401
|
new TypoString(`Expected ${elementTypes.length} splits from: `),
|
|
405
402
|
new TypoString(`"${input}"`, typoStyleQuote),
|
|
403
|
+
new TypoString(`.`),
|
|
406
404
|
),
|
|
407
405
|
);
|
|
408
406
|
}
|
|
@@ -423,7 +421,7 @@ export function typeTuple<const Elements extends Array<any>>(
|
|
|
423
421
|
|
|
424
422
|
/**
|
|
425
423
|
* Splits a delimited string into a typed array.
|
|
426
|
-
* Each part is decoded by `elementType`; failed decodes
|
|
424
|
+
* Each part is decoded by `elementType`; failed decodes throws.
|
|
427
425
|
* Note: splitting an empty string yields one empty element — prefer {@link optionRepeatable} for a zero-default.
|
|
428
426
|
*
|
|
429
427
|
* @typeParam Value - Element type produced by `elementType.decoder`.
|
|
@@ -434,10 +432,9 @@ export function typeTuple<const Elements extends Array<any>>(
|
|
|
434
432
|
*
|
|
435
433
|
* @example
|
|
436
434
|
* ```ts
|
|
437
|
-
* const typeNumbers = typeList(typeNumber);
|
|
435
|
+
* const typeNumbers = typeList(typeNumber("v"));
|
|
438
436
|
* typeNumbers.decoder("1,2,3") // → [1, 2, 3]
|
|
439
|
-
* typeNumbers.decoder("1,x,3") // throws
|
|
440
|
-
*
|
|
437
|
+
* typeNumbers.decoder("1,x,3") // throws
|
|
441
438
|
* const typePaths = typeList(typePath(), ":");
|
|
442
439
|
* typePaths.decoder("/usr/bin:/usr/local/bin") // → ["/usr/bin", "/usr/local/bin"]
|
|
443
440
|
* ```
|
|
@@ -469,6 +466,7 @@ function throwInvalidValue(kind: string, input: string): never {
|
|
|
469
466
|
new TypoText(
|
|
470
467
|
new TypoString(`Not ${kind}: `),
|
|
471
468
|
new TypoString(`"${input}"`, typoStyleQuote),
|
|
469
|
+
new TypoString(`.`),
|
|
472
470
|
),
|
|
473
471
|
);
|
|
474
472
|
}
|
package/src/lib/Typo.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { typeBooleanValuesFalse } from "./Type";
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Color names for terminal styling, used by {@link TypoStyle}.
|
|
5
3
|
* `dark*` = standard ANSI (30–37); `bright*` = high-intensity (90–97).
|
|
@@ -125,14 +123,14 @@ export const typoStyleRegularWeaker: TypoStyle = {
|
|
|
125
123
|
*/
|
|
126
124
|
export class TypoString {
|
|
127
125
|
#value: string;
|
|
128
|
-
#
|
|
126
|
+
#style: TypoStyle | undefined;
|
|
129
127
|
/**
|
|
130
128
|
* @param value - Raw text content.
|
|
131
|
-
* @param
|
|
129
|
+
* @param style - Style to apply when rendering.
|
|
132
130
|
*/
|
|
133
|
-
constructor(value: string,
|
|
131
|
+
constructor(value: string, style?: TypoStyle) {
|
|
134
132
|
this.#value = value;
|
|
135
|
-
this.#
|
|
133
|
+
this.#style = style;
|
|
136
134
|
}
|
|
137
135
|
/**
|
|
138
136
|
* Returns the text styled by `typoSupport`.
|
|
@@ -140,48 +138,75 @@ export class TypoString {
|
|
|
140
138
|
* @param typoSupport - Rendering mode.
|
|
141
139
|
*/
|
|
142
140
|
computeStyledString(typoSupport: TypoSupport): string {
|
|
143
|
-
return typoSupport.computeStyledString(this.#value, this.#
|
|
141
|
+
return typoSupport.computeStyledString(this.#value, this.#style);
|
|
144
142
|
}
|
|
145
143
|
/**
|
|
146
144
|
* Returns the raw text.
|
|
147
145
|
*/
|
|
148
146
|
getRawString(): string {
|
|
147
|
+
// TODO - should there be a global config or smthg instead of passing support everywhere?
|
|
149
148
|
return this.#value;
|
|
150
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Predefined ellipsis segment with subtle styling.
|
|
152
|
+
*/
|
|
153
|
+
static elipsis = new TypoString("...", typoStyleRegularWeaker);
|
|
151
154
|
}
|
|
152
155
|
|
|
156
|
+
/**
|
|
157
|
+
* A segment of styled text, a string, or an array of segments.
|
|
158
|
+
*/
|
|
159
|
+
export type TypoSegment = TypoText | TypoString | Array<TypoSegment>;
|
|
160
|
+
|
|
153
161
|
/**
|
|
154
162
|
* Mutable sequence of {@link TypoString} segments.
|
|
155
163
|
*/
|
|
156
164
|
export class TypoText {
|
|
157
|
-
#
|
|
165
|
+
#strings: Array<TypoString>;
|
|
158
166
|
/**
|
|
159
167
|
* @param segments - Initial text segments
|
|
160
168
|
*/
|
|
161
|
-
constructor(
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
for (const typoPart of segments) {
|
|
166
|
-
this.push(typoPart);
|
|
169
|
+
constructor(...segments: Array<TypoSegment>) {
|
|
170
|
+
this.#strings = [];
|
|
171
|
+
for (const segment of segments) {
|
|
172
|
+
this.push(segment);
|
|
167
173
|
}
|
|
168
174
|
}
|
|
169
175
|
/**
|
|
170
176
|
* Appends new text segment(s).
|
|
171
|
-
*
|
|
172
|
-
* @param segment - Text segment(s) to append.
|
|
173
177
|
*/
|
|
174
|
-
push(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
this.#
|
|
178
|
+
push(...segments: Array<TypoSegment>): void {
|
|
179
|
+
for (const segment of segments) {
|
|
180
|
+
if (typeof segment === "string") {
|
|
181
|
+
this.#strings.push(new TypoString(segment));
|
|
182
|
+
} else if (segment instanceof TypoString) {
|
|
183
|
+
this.#strings.push(segment);
|
|
184
|
+
} else if (segment instanceof TypoText) {
|
|
185
|
+
this.#strings.push(...segment.#strings);
|
|
186
|
+
} else if (Array.isArray(segment)) {
|
|
187
|
+
for (const part of segment) {
|
|
188
|
+
this.push(part);
|
|
189
|
+
}
|
|
182
190
|
}
|
|
183
|
-
}
|
|
184
|
-
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Appends separated segments, optionally truncating with an ellipsis.
|
|
195
|
+
*/
|
|
196
|
+
pushJoined(
|
|
197
|
+
segments: Array<TypoSegment>,
|
|
198
|
+
separator: TypoSegment,
|
|
199
|
+
ellipsisLimit: number,
|
|
200
|
+
): void {
|
|
201
|
+
for (let index = 0; index < segments.length; index++) {
|
|
202
|
+
if (index > 0) {
|
|
203
|
+
this.push(separator);
|
|
204
|
+
}
|
|
205
|
+
if (ellipsisLimit !== undefined && index >= ellipsisLimit) {
|
|
206
|
+
this.push(TypoString.elipsis);
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
this.push(segments[index]!);
|
|
185
210
|
}
|
|
186
211
|
}
|
|
187
212
|
/**
|
|
@@ -191,7 +216,7 @@ export class TypoText {
|
|
|
191
216
|
* @returns Concatenated styled string.
|
|
192
217
|
*/
|
|
193
218
|
computeStyledString(typoSupport: TypoSupport): string {
|
|
194
|
-
return this.#
|
|
219
|
+
return this.#strings
|
|
195
220
|
.map((t) => t.computeStyledString(typoSupport))
|
|
196
221
|
.join("");
|
|
197
222
|
}
|
|
@@ -199,14 +224,14 @@ export class TypoText {
|
|
|
199
224
|
* Returns the concatenated raw text.
|
|
200
225
|
*/
|
|
201
226
|
computeRawString(): string {
|
|
202
|
-
return this.#
|
|
227
|
+
return this.#strings.map((t) => t.getRawString()).join("");
|
|
203
228
|
}
|
|
204
229
|
/**
|
|
205
230
|
* Returns the total raw character count.
|
|
206
231
|
*/
|
|
207
232
|
computeRawLength(): number {
|
|
208
233
|
let length = 0;
|
|
209
|
-
for (const typoString of this.#
|
|
234
|
+
for (const typoString of this.#strings) {
|
|
210
235
|
length += typoString.getRawString().length;
|
|
211
236
|
}
|
|
212
237
|
return length;
|
|
@@ -334,8 +359,8 @@ export class TypoError extends Error {
|
|
|
334
359
|
* Controls ANSI terminal styling. Create via the static factory methods.
|
|
335
360
|
*/
|
|
336
361
|
export class TypoSupport {
|
|
337
|
-
#kind:
|
|
338
|
-
private constructor(kind:
|
|
362
|
+
#kind: TypoSupportKind;
|
|
363
|
+
private constructor(kind: TypoSupportKind) {
|
|
339
364
|
this.#kind = kind;
|
|
340
365
|
}
|
|
341
366
|
/**
|
|
@@ -360,15 +385,6 @@ export class TypoSupport {
|
|
|
360
385
|
* Auto-detects styling mode from the process environment on best-effort basis.
|
|
361
386
|
*/
|
|
362
387
|
static inferFromEnv(): TypoSupport {
|
|
363
|
-
/*
|
|
364
|
-
console.warn({
|
|
365
|
-
no: readEnvVar("NO_COLOR"),
|
|
366
|
-
force: readEnvVar("FORCE_COLOR"),
|
|
367
|
-
mock: readEnvVar("MOCK_COLOR"),
|
|
368
|
-
term: readEnvVar("TERM"),
|
|
369
|
-
tty: process.stdout.isTTY,
|
|
370
|
-
});
|
|
371
|
-
*/
|
|
372
388
|
if (!process || !process.env || !process.stdout) {
|
|
373
389
|
return TypoSupport.none();
|
|
374
390
|
}
|
|
@@ -379,13 +395,17 @@ export class TypoSupport {
|
|
|
379
395
|
if (envForceColor === "0") {
|
|
380
396
|
return TypoSupport.none();
|
|
381
397
|
}
|
|
382
|
-
if (envForceColor
|
|
383
|
-
|
|
384
|
-
return TypoSupport.tty();
|
|
385
|
-
}
|
|
398
|
+
if (envForceColor === "1") {
|
|
399
|
+
return TypoSupport.tty();
|
|
386
400
|
}
|
|
387
|
-
if (
|
|
388
|
-
return TypoSupport.
|
|
401
|
+
if (envForceColor === "2") {
|
|
402
|
+
return TypoSupport.tty();
|
|
403
|
+
}
|
|
404
|
+
if (envForceColor === "3") {
|
|
405
|
+
return TypoSupport.tty(); // TODO - should there be fancy colors possible?
|
|
406
|
+
}
|
|
407
|
+
if (envForceColor) {
|
|
408
|
+
return TypoSupport.tty();
|
|
389
409
|
}
|
|
390
410
|
if (!process.stdout.isTTY) {
|
|
391
411
|
return TypoSupport.none();
|
|
@@ -402,7 +422,10 @@ export class TypoSupport {
|
|
|
402
422
|
* @param typoStyle - Style to apply.
|
|
403
423
|
* @returns Styled string.
|
|
404
424
|
*/
|
|
405
|
-
computeStyledString(value: string, typoStyle: TypoStyle): string {
|
|
425
|
+
computeStyledString(value: string, typoStyle: TypoStyle | undefined): string {
|
|
426
|
+
if (typoStyle === undefined) {
|
|
427
|
+
return value;
|
|
428
|
+
}
|
|
406
429
|
let styledValue = value;
|
|
407
430
|
if (typoStyle.case === "upper") {
|
|
408
431
|
styledValue = styledValue.toUpperCase();
|
|
@@ -516,3 +539,5 @@ function readEnvVar(name: string) {
|
|
|
516
539
|
}
|
|
517
540
|
return process.env[name];
|
|
518
541
|
}
|
|
542
|
+
|
|
543
|
+
type TypoSupportKind = "none" | "tty" | "mock";
|
package/src/lib/Usage.ts
CHANGED
|
@@ -93,7 +93,7 @@ export type UsageOption = {
|
|
|
93
93
|
/**
|
|
94
94
|
* Extra annotation appended to the option label in help.
|
|
95
95
|
*/
|
|
96
|
-
annotation?: string | undefined;
|
|
96
|
+
annotation?: string | undefined; // TODO - maybe prefix/postfix would be more useful
|
|
97
97
|
/**
|
|
98
98
|
* Help text.
|
|
99
99
|
*/
|
|
@@ -175,7 +175,7 @@ export function usageToStyledLines(params: {
|
|
|
175
175
|
lines.push("");
|
|
176
176
|
const introText = new TypoText();
|
|
177
177
|
introText.push(textUsageText(usage.information.description));
|
|
178
|
-
if (usage.information.hint) {
|
|
178
|
+
if (usage.information.hint !== undefined) {
|
|
179
179
|
introText.push(textDelimiter(" "));
|
|
180
180
|
introText.push(textSubtleInfo(`(${usage.information.hint})`));
|
|
181
181
|
}
|
|
@@ -221,7 +221,7 @@ export function usageToStyledLines(params: {
|
|
|
221
221
|
for (const optionUsage of usage.options) {
|
|
222
222
|
const typoGridRow = new Array<TypoText>();
|
|
223
223
|
typoGridRow.push(new TypoText(textDelimiter(" ")));
|
|
224
|
-
if (optionUsage.short) {
|
|
224
|
+
if (optionUsage.short !== undefined) {
|
|
225
225
|
typoGridRow.push(
|
|
226
226
|
new TypoText(
|
|
227
227
|
textConstants(`-${optionUsage.short}`),
|
|
@@ -234,11 +234,11 @@ export function usageToStyledLines(params: {
|
|
|
234
234
|
const longOptionText = new TypoText(
|
|
235
235
|
textConstants(`--${optionUsage.long}`),
|
|
236
236
|
);
|
|
237
|
-
if (optionUsage.label) {
|
|
237
|
+
if (optionUsage.label !== undefined) {
|
|
238
238
|
longOptionText.push(textDelimiter(" "));
|
|
239
239
|
longOptionText.push(textUserInput(optionUsage.label));
|
|
240
240
|
}
|
|
241
|
-
if (optionUsage.annotation) {
|
|
241
|
+
if (optionUsage.annotation !== undefined) {
|
|
242
242
|
longOptionText.push(textSubtleInfo(optionUsage.annotation));
|
|
243
243
|
}
|
|
244
244
|
typoGridRow.push(longOptionText);
|
|
@@ -248,21 +248,21 @@ export function usageToStyledLines(params: {
|
|
|
248
248
|
lines.push(...typoGrid.computeStyledLines(typoSupport));
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
if (usage.information.examples) {
|
|
251
|
+
if (usage.information.examples !== undefined) {
|
|
252
252
|
lines.push("");
|
|
253
253
|
lines.push(textBlockTitle("Examples:").computeStyledString(typoSupport));
|
|
254
254
|
for (const example of usage.information.examples) {
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
lines.push(
|
|
255
|
+
const explanationText = new TypoText();
|
|
256
|
+
explanationText.push(textDelimiter(" "));
|
|
257
|
+
explanationText.push(textSubtleInfo(`# ${example.explanation}`));
|
|
258
|
+
lines.push(explanationText.computeStyledString(typoSupport));
|
|
259
259
|
const commandLineText = new TypoText();
|
|
260
260
|
commandLineText.push(textDelimiter(" "));
|
|
261
261
|
commandLineText.push(textConstants(cliName));
|
|
262
262
|
for (const commandArg of example.commandArgs) {
|
|
263
263
|
commandLineText.push(textDelimiter(" "));
|
|
264
264
|
if (typeof commandArg === "string") {
|
|
265
|
-
commandLineText.push(commandArg);
|
|
265
|
+
commandLineText.push(new TypoString(commandArg));
|
|
266
266
|
} else if ("positional" in commandArg) {
|
|
267
267
|
commandLineText.push(textUserInput(commandArg.positional));
|
|
268
268
|
} else if ("subcommand" in commandArg) {
|
|
@@ -299,11 +299,11 @@ function createInformationals(usage: {
|
|
|
299
299
|
hint?: string | undefined;
|
|
300
300
|
}): Array<TypoText> {
|
|
301
301
|
const informationals = [];
|
|
302
|
-
if (usage.description) {
|
|
302
|
+
if (usage.description !== undefined) {
|
|
303
303
|
informationals.push(textDelimiter(" "));
|
|
304
304
|
informationals.push(textUsefulInfo(usage.description));
|
|
305
305
|
}
|
|
306
|
-
if (usage.hint) {
|
|
306
|
+
if (usage.hint !== undefined) {
|
|
307
307
|
informationals.push(textDelimiter(" "));
|
|
308
308
|
informationals.push(textSubtleInfo(`(${usage.hint})`));
|
|
309
309
|
}
|