@optique/core 0.10.7 → 1.0.0-dev.1116

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 (56) hide show
  1. package/README.md +4 -6
  2. package/dist/annotations.cjs +209 -1
  3. package/dist/annotations.d.cts +78 -1
  4. package/dist/annotations.d.ts +78 -1
  5. package/dist/annotations.js +201 -1
  6. package/dist/completion.cjs +194 -52
  7. package/dist/completion.js +194 -52
  8. package/dist/constructs.cjs +310 -78
  9. package/dist/constructs.d.cts +525 -644
  10. package/dist/constructs.d.ts +525 -644
  11. package/dist/constructs.js +311 -79
  12. package/dist/context.cjs +43 -3
  13. package/dist/context.d.cts +113 -5
  14. package/dist/context.d.ts +113 -5
  15. package/dist/context.js +41 -3
  16. package/dist/dependency.cjs +172 -66
  17. package/dist/dependency.d.cts +22 -2
  18. package/dist/dependency.d.ts +22 -2
  19. package/dist/dependency.js +172 -66
  20. package/dist/doc.cjs +46 -1
  21. package/dist/doc.d.cts +24 -0
  22. package/dist/doc.d.ts +24 -0
  23. package/dist/doc.js +46 -1
  24. package/dist/facade.cjs +702 -322
  25. package/dist/facade.d.cts +124 -190
  26. package/dist/facade.d.ts +124 -190
  27. package/dist/facade.js +703 -323
  28. package/dist/index.cjs +5 -0
  29. package/dist/index.d.cts +5 -5
  30. package/dist/index.d.ts +5 -5
  31. package/dist/index.js +3 -3
  32. package/dist/message.cjs +7 -4
  33. package/dist/message.js +7 -4
  34. package/dist/mode-dispatch.cjs +23 -1
  35. package/dist/mode-dispatch.d.cts +55 -0
  36. package/dist/mode-dispatch.d.ts +55 -0
  37. package/dist/mode-dispatch.js +21 -1
  38. package/dist/modifiers.cjs +210 -55
  39. package/dist/modifiers.js +211 -56
  40. package/dist/parser.cjs +80 -47
  41. package/dist/parser.d.cts +18 -3
  42. package/dist/parser.d.ts +18 -3
  43. package/dist/parser.js +82 -50
  44. package/dist/primitives.cjs +102 -37
  45. package/dist/primitives.d.cts +81 -24
  46. package/dist/primitives.d.ts +81 -24
  47. package/dist/primitives.js +103 -39
  48. package/dist/usage.cjs +88 -6
  49. package/dist/usage.d.cts +51 -13
  50. package/dist/usage.d.ts +51 -13
  51. package/dist/usage.js +85 -7
  52. package/dist/valueparser.cjs +391 -106
  53. package/dist/valueparser.d.cts +62 -10
  54. package/dist/valueparser.d.ts +62 -10
  55. package/dist/valueparser.js +391 -106
  56. package/package.json +10 -1
package/dist/usage.cjs CHANGED
@@ -1,6 +1,38 @@
1
1
 
2
2
  //#region src/usage.ts
3
3
  /**
4
+ * Returns whether the term should be hidden from usage output.
5
+ */
6
+ function isUsageHidden(hidden) {
7
+ return hidden === true || hidden === "usage" || hidden === "help";
8
+ }
9
+ /**
10
+ * Returns whether the term should be hidden from documentation output.
11
+ */
12
+ function isDocHidden(hidden) {
13
+ return hidden === true || hidden === "doc" || hidden === "help";
14
+ }
15
+ /**
16
+ * Returns whether the term should be hidden from suggestion/error candidates.
17
+ */
18
+ function isSuggestionHidden(hidden) {
19
+ return hidden === true;
20
+ }
21
+ /**
22
+ * Merges two hidden visibility settings by taking the union of restrictions.
23
+ */
24
+ function mergeHidden(a, b) {
25
+ if (a == null) return b;
26
+ if (b == null) return a;
27
+ if (a === true || b === true) return true;
28
+ if (a === false) return b;
29
+ if (b === false) return a;
30
+ if (a === b) return a;
31
+ if (a === "help" || b === "help") return "help";
32
+ if ((a === "usage" || a === "doc") && (b === "usage" || b === "doc")) return "help";
33
+ return a;
34
+ }
35
+ /**
4
36
  * Extracts all option names from a usage description.
5
37
  *
6
38
  * This function recursively traverses a {@link Usage} tree and collects all
@@ -25,7 +57,7 @@ function extractOptionNames(usage) {
25
57
  function traverseUsage(terms) {
26
58
  if (!terms || !Array.isArray(terms)) return;
27
59
  for (const term of terms) if (term.type === "option") {
28
- if (term.hidden) continue;
60
+ if (isSuggestionHidden(term.hidden)) continue;
29
61
  for (const name of term.names) names.add(name);
30
62
  } else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
31
63
  else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
@@ -58,7 +90,7 @@ function extractCommandNames(usage) {
58
90
  function traverseUsage(terms) {
59
91
  if (!terms || !Array.isArray(terms)) return;
60
92
  for (const term of terms) if (term.type === "command") {
61
- if (term.hidden) continue;
93
+ if (isSuggestionHidden(term.hidden)) continue;
62
94
  names.add(term.name);
63
95
  } else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
64
96
  else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
@@ -92,7 +124,7 @@ function extractArgumentMetavars(usage) {
92
124
  function traverseUsage(terms) {
93
125
  if (!terms || !Array.isArray(terms)) return;
94
126
  for (const term of terms) if (term.type === "argument") {
95
- if (term.hidden) continue;
127
+ if (isSuggestionHidden(term.hidden)) continue;
96
128
  metavars.add(term.metavar);
97
129
  } else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
98
130
  else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
@@ -117,14 +149,14 @@ function extractArgumentMetavars(usage) {
117
149
  * @returns A formatted string representation of the usage description.
118
150
  */
119
151
  function formatUsage(programName, usage, options = {}) {
120
- usage = normalizeUsage(usage);
152
+ usage = normalizeUsage(filterUsageForDisplay(usage));
121
153
  if (options.expandCommands) {
122
154
  const lastTerm = usage.at(-1);
123
155
  if (usage.length > 0 && usage.slice(0, -1).every((t) => t.type === "command") && lastTerm.type === "exclusive" && lastTerm.terms.every((t) => t.length > 0 && (t[0].type === "command" || t[0].type === "option" || t[0].type === "argument" || t[0].type === "optional" && t[0].terms.length === 1 && (t[0].terms[0].type === "command" || t[0].terms[0].type === "option" || t[0].terms[0].type === "argument")))) {
124
156
  const lines = [];
125
157
  for (let command of lastTerm.terms) {
126
158
  const firstTerm = command[0];
127
- if (firstTerm?.type === "command" && firstTerm.hidden) continue;
159
+ if (firstTerm?.type === "command" && isUsageHidden(firstTerm.hidden)) continue;
128
160
  if (usage.length > 1) command = [...usage.slice(0, -1), ...command];
129
161
  lines.push(formatUsage(programName, command, options));
130
162
  }
@@ -205,9 +237,47 @@ function normalizeUsageTerm(term) {
205
237
  };
206
238
  } else return term;
207
239
  }
240
+ function filterUsageForDisplay(usage) {
241
+ const terms = [];
242
+ for (const term of usage) {
243
+ if ((term.type === "argument" || term.type === "option" || term.type === "command" || term.type === "passthrough") && isUsageHidden(term.hidden)) continue;
244
+ if (term.type === "optional") {
245
+ const filtered = filterUsageForDisplay(term.terms);
246
+ if (filtered.length > 0) terms.push({
247
+ type: "optional",
248
+ terms: filtered
249
+ });
250
+ continue;
251
+ }
252
+ if (term.type === "multiple") {
253
+ const filtered = filterUsageForDisplay(term.terms);
254
+ if (filtered.length > 0) terms.push({
255
+ type: "multiple",
256
+ terms: filtered,
257
+ min: term.min
258
+ });
259
+ continue;
260
+ }
261
+ if (term.type === "exclusive") {
262
+ const filteredBranches = term.terms.map((branch) => {
263
+ const first = branch[0];
264
+ if (first?.type === "command" && isUsageHidden(first.hidden)) return [];
265
+ return filterUsageForDisplay(branch);
266
+ }).filter((branch) => branch.length > 0);
267
+ if (filteredBranches.length > 0) terms.push({
268
+ type: "exclusive",
269
+ terms: filteredBranches
270
+ });
271
+ continue;
272
+ }
273
+ terms.push(term);
274
+ }
275
+ return terms;
276
+ }
208
277
  function* formatUsageTerms(terms, options) {
209
278
  let i = 0;
210
279
  for (const t of terms) {
280
+ if ("hidden" in t && (t.type === "argument" || t.type === "option" || t.type === "command" || t.type === "passthrough") && isUsageHidden(t.hidden)) continue;
211
281
  if (i > 0) yield {
212
282
  text: " ",
213
283
  width: 1
@@ -226,9 +296,11 @@ function* formatUsageTerms(terms, options) {
226
296
  * @returns A formatted string representation of the usage term.
227
297
  */
228
298
  function formatUsageTerm(term, options = {}) {
299
+ const visibleTerms = filterUsageForDisplay([term]);
300
+ if (visibleTerms.length < 1) return "";
229
301
  let lineWidth = 0;
230
302
  let output = "";
231
- for (const { text, width } of formatUsageTermInternal(term, options)) {
303
+ for (const { text, width } of formatUsageTermInternal(visibleTerms[0], options)) {
232
304
  if (options.maxWidth != null && lineWidth + width > options.maxWidth) {
233
305
  output += "\n";
234
306
  lineWidth = 0;
@@ -347,6 +419,12 @@ function* formatUsageTermInternal(term, options) {
347
419
  text: options?.colors ? `\x1b[2m${text}\x1b[0m` : text,
348
420
  width: 5
349
421
  };
422
+ } else if (term.type === "ellipsis") {
423
+ const text = "...";
424
+ yield {
425
+ text: options?.colors ? `\x1b[2m${text}\x1b[0m` : text,
426
+ width: 3
427
+ };
350
428
  } else throw new TypeError(`Unknown usage term type: ${term["type"]}.`);
351
429
  }
352
430
 
@@ -356,4 +434,8 @@ exports.extractCommandNames = extractCommandNames;
356
434
  exports.extractOptionNames = extractOptionNames;
357
435
  exports.formatUsage = formatUsage;
358
436
  exports.formatUsageTerm = formatUsageTerm;
437
+ exports.isDocHidden = isDocHidden;
438
+ exports.isSuggestionHidden = isSuggestionHidden;
439
+ exports.isUsageHidden = isUsageHidden;
440
+ exports.mergeHidden = mergeHidden;
359
441
  exports.normalizeUsage = normalizeUsage;
package/dist/usage.d.cts CHANGED
@@ -12,6 +12,31 @@ import { NonEmptyString } from "./nonempty.cjs";
12
12
  * - Plus-prefixed options (`+o`)
13
13
  */
14
14
  type OptionName = `--${string}` | `-${string}` | `/${string}` | `+${string}`;
15
+ /**
16
+ * Visibility control for parser terms.
17
+ *
18
+ * - `true`: hidden from usage, documentation, and suggestions
19
+ * - `"usage"`: hidden from usage only
20
+ * - `"doc"`: hidden from documentation only
21
+ * - `"help"`: hidden from usage and documentation, but shown in suggestions
22
+ */
23
+ type HiddenVisibility = boolean | "usage" | "doc" | "help";
24
+ /**
25
+ * Returns whether the term should be hidden from usage output.
26
+ */
27
+ declare function isUsageHidden(hidden?: HiddenVisibility): boolean;
28
+ /**
29
+ * Returns whether the term should be hidden from documentation output.
30
+ */
31
+ declare function isDocHidden(hidden?: HiddenVisibility): boolean;
32
+ /**
33
+ * Returns whether the term should be hidden from suggestion/error candidates.
34
+ */
35
+ declare function isSuggestionHidden(hidden?: HiddenVisibility): boolean;
36
+ /**
37
+ * Merges two hidden visibility settings by taking the union of restrictions.
38
+ */
39
+ declare function mergeHidden(a?: HiddenVisibility, b?: HiddenVisibility): HiddenVisibility | undefined;
15
40
  /**
16
41
  * Represents a single term in a command-line usage description.
17
42
  */
@@ -31,11 +56,10 @@ type UsageTerm =
31
56
  */
32
57
  readonly metavar: NonEmptyString;
33
58
  /**
34
- * When `true`, hides the argument from help text, shell completion
35
- * suggestions, and error suggestions.
59
+ * Visibility controls for this term.
36
60
  * @since 0.9.0
37
61
  */
38
- readonly hidden?: boolean;
62
+ readonly hidden?: HiddenVisibility;
39
63
  }
40
64
  /**
41
65
  * An option term, which represents a command-line option that can
@@ -56,11 +80,10 @@ type UsageTerm =
56
80
  */
57
81
  readonly metavar?: NonEmptyString;
58
82
  /**
59
- * When `true`, hides the option from help text, shell completion
60
- * suggestions, and "Did you mean?" error suggestions.
83
+ * Visibility controls for this term.
61
84
  * @since 0.9.0
62
85
  */
63
- readonly hidden?: boolean;
86
+ readonly hidden?: HiddenVisibility;
64
87
  }
65
88
  /**
66
89
  * A command term, which represents a subcommand in the command-line
@@ -76,11 +99,16 @@ type UsageTerm =
76
99
  */
77
100
  readonly name: string;
78
101
  /**
79
- * When `true`, hides the command from help text, shell completion
80
- * suggestions, and "Did you mean?" error suggestions.
102
+ * Optional usage line override for this command's own help page.
103
+ * This affects help/documentation rendering only.
104
+ * @since 1.0.0
105
+ */
106
+ readonly usageLine?: Usage | ((defaultUsageLine: Usage) => Usage);
107
+ /**
108
+ * Visibility controls for this term.
81
109
  * @since 0.9.0
82
110
  */
83
- readonly hidden?: boolean;
111
+ readonly hidden?: HiddenVisibility;
84
112
  }
85
113
  /**
86
114
  * An optional term, which represents an optional component
@@ -155,11 +183,21 @@ type UsageTerm =
155
183
  */
156
184
  readonly type: "passthrough";
157
185
  /**
158
- * When `true`, hides the pass-through from help text and shell
159
- * completion suggestions.
186
+ * Visibility controls for this term.
160
187
  * @since 0.9.0
161
188
  */
162
- readonly hidden?: boolean;
189
+ readonly hidden?: HiddenVisibility;
190
+ }
191
+ /**
192
+ * An ellipsis term, which represents a summary placeholder in usage output.
193
+ * Unlike {@link passthrough}, this term has no parsing semantics and is used
194
+ * only for display.
195
+ * @since 1.0.0
196
+ */ | {
197
+ /**
198
+ * The type of the term, which is always `"ellipsis"` for this term.
199
+ */
200
+ readonly type: "ellipsis";
163
201
  };
164
202
  /**
165
203
  * Represents a command-line usage description, which is a sequence of
@@ -330,4 +368,4 @@ interface UsageTermFormatOptions extends UsageFormatOptions {
330
368
  */
331
369
  declare function formatUsageTerm(term: UsageTerm, options?: UsageTermFormatOptions): string;
332
370
  //#endregion
333
- export { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage };
371
+ export { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };
package/dist/usage.d.ts CHANGED
@@ -12,6 +12,31 @@ import { NonEmptyString } from "./nonempty.js";
12
12
  * - Plus-prefixed options (`+o`)
13
13
  */
14
14
  type OptionName = `--${string}` | `-${string}` | `/${string}` | `+${string}`;
15
+ /**
16
+ * Visibility control for parser terms.
17
+ *
18
+ * - `true`: hidden from usage, documentation, and suggestions
19
+ * - `"usage"`: hidden from usage only
20
+ * - `"doc"`: hidden from documentation only
21
+ * - `"help"`: hidden from usage and documentation, but shown in suggestions
22
+ */
23
+ type HiddenVisibility = boolean | "usage" | "doc" | "help";
24
+ /**
25
+ * Returns whether the term should be hidden from usage output.
26
+ */
27
+ declare function isUsageHidden(hidden?: HiddenVisibility): boolean;
28
+ /**
29
+ * Returns whether the term should be hidden from documentation output.
30
+ */
31
+ declare function isDocHidden(hidden?: HiddenVisibility): boolean;
32
+ /**
33
+ * Returns whether the term should be hidden from suggestion/error candidates.
34
+ */
35
+ declare function isSuggestionHidden(hidden?: HiddenVisibility): boolean;
36
+ /**
37
+ * Merges two hidden visibility settings by taking the union of restrictions.
38
+ */
39
+ declare function mergeHidden(a?: HiddenVisibility, b?: HiddenVisibility): HiddenVisibility | undefined;
15
40
  /**
16
41
  * Represents a single term in a command-line usage description.
17
42
  */
@@ -31,11 +56,10 @@ type UsageTerm =
31
56
  */
32
57
  readonly metavar: NonEmptyString;
33
58
  /**
34
- * When `true`, hides the argument from help text, shell completion
35
- * suggestions, and error suggestions.
59
+ * Visibility controls for this term.
36
60
  * @since 0.9.0
37
61
  */
38
- readonly hidden?: boolean;
62
+ readonly hidden?: HiddenVisibility;
39
63
  }
40
64
  /**
41
65
  * An option term, which represents a command-line option that can
@@ -56,11 +80,10 @@ type UsageTerm =
56
80
  */
57
81
  readonly metavar?: NonEmptyString;
58
82
  /**
59
- * When `true`, hides the option from help text, shell completion
60
- * suggestions, and "Did you mean?" error suggestions.
83
+ * Visibility controls for this term.
61
84
  * @since 0.9.0
62
85
  */
63
- readonly hidden?: boolean;
86
+ readonly hidden?: HiddenVisibility;
64
87
  }
65
88
  /**
66
89
  * A command term, which represents a subcommand in the command-line
@@ -76,11 +99,16 @@ type UsageTerm =
76
99
  */
77
100
  readonly name: string;
78
101
  /**
79
- * When `true`, hides the command from help text, shell completion
80
- * suggestions, and "Did you mean?" error suggestions.
102
+ * Optional usage line override for this command's own help page.
103
+ * This affects help/documentation rendering only.
104
+ * @since 1.0.0
105
+ */
106
+ readonly usageLine?: Usage | ((defaultUsageLine: Usage) => Usage);
107
+ /**
108
+ * Visibility controls for this term.
81
109
  * @since 0.9.0
82
110
  */
83
- readonly hidden?: boolean;
111
+ readonly hidden?: HiddenVisibility;
84
112
  }
85
113
  /**
86
114
  * An optional term, which represents an optional component
@@ -155,11 +183,21 @@ type UsageTerm =
155
183
  */
156
184
  readonly type: "passthrough";
157
185
  /**
158
- * When `true`, hides the pass-through from help text and shell
159
- * completion suggestions.
186
+ * Visibility controls for this term.
160
187
  * @since 0.9.0
161
188
  */
162
- readonly hidden?: boolean;
189
+ readonly hidden?: HiddenVisibility;
190
+ }
191
+ /**
192
+ * An ellipsis term, which represents a summary placeholder in usage output.
193
+ * Unlike {@link passthrough}, this term has no parsing semantics and is used
194
+ * only for display.
195
+ * @since 1.0.0
196
+ */ | {
197
+ /**
198
+ * The type of the term, which is always `"ellipsis"` for this term.
199
+ */
200
+ readonly type: "ellipsis";
163
201
  };
164
202
  /**
165
203
  * Represents a command-line usage description, which is a sequence of
@@ -330,4 +368,4 @@ interface UsageTermFormatOptions extends UsageFormatOptions {
330
368
  */
331
369
  declare function formatUsageTerm(term: UsageTerm, options?: UsageTermFormatOptions): string;
332
370
  //#endregion
333
- export { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage };
371
+ export { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };
package/dist/usage.js CHANGED
@@ -1,5 +1,37 @@
1
1
  //#region src/usage.ts
2
2
  /**
3
+ * Returns whether the term should be hidden from usage output.
4
+ */
5
+ function isUsageHidden(hidden) {
6
+ return hidden === true || hidden === "usage" || hidden === "help";
7
+ }
8
+ /**
9
+ * Returns whether the term should be hidden from documentation output.
10
+ */
11
+ function isDocHidden(hidden) {
12
+ return hidden === true || hidden === "doc" || hidden === "help";
13
+ }
14
+ /**
15
+ * Returns whether the term should be hidden from suggestion/error candidates.
16
+ */
17
+ function isSuggestionHidden(hidden) {
18
+ return hidden === true;
19
+ }
20
+ /**
21
+ * Merges two hidden visibility settings by taking the union of restrictions.
22
+ */
23
+ function mergeHidden(a, b) {
24
+ if (a == null) return b;
25
+ if (b == null) return a;
26
+ if (a === true || b === true) return true;
27
+ if (a === false) return b;
28
+ if (b === false) return a;
29
+ if (a === b) return a;
30
+ if (a === "help" || b === "help") return "help";
31
+ if ((a === "usage" || a === "doc") && (b === "usage" || b === "doc")) return "help";
32
+ return a;
33
+ }
34
+ /**
3
35
  * Extracts all option names from a usage description.
4
36
  *
5
37
  * This function recursively traverses a {@link Usage} tree and collects all
@@ -24,7 +56,7 @@ function extractOptionNames(usage) {
24
56
  function traverseUsage(terms) {
25
57
  if (!terms || !Array.isArray(terms)) return;
26
58
  for (const term of terms) if (term.type === "option") {
27
- if (term.hidden) continue;
59
+ if (isSuggestionHidden(term.hidden)) continue;
28
60
  for (const name of term.names) names.add(name);
29
61
  } else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
30
62
  else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
@@ -57,7 +89,7 @@ function extractCommandNames(usage) {
57
89
  function traverseUsage(terms) {
58
90
  if (!terms || !Array.isArray(terms)) return;
59
91
  for (const term of terms) if (term.type === "command") {
60
- if (term.hidden) continue;
92
+ if (isSuggestionHidden(term.hidden)) continue;
61
93
  names.add(term.name);
62
94
  } else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
63
95
  else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
@@ -91,7 +123,7 @@ function extractArgumentMetavars(usage) {
91
123
  function traverseUsage(terms) {
92
124
  if (!terms || !Array.isArray(terms)) return;
93
125
  for (const term of terms) if (term.type === "argument") {
94
- if (term.hidden) continue;
126
+ if (isSuggestionHidden(term.hidden)) continue;
95
127
  metavars.add(term.metavar);
96
128
  } else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
97
129
  else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
@@ -116,14 +148,14 @@ function extractArgumentMetavars(usage) {
116
148
  * @returns A formatted string representation of the usage description.
117
149
  */
118
150
  function formatUsage(programName, usage, options = {}) {
119
- usage = normalizeUsage(usage);
151
+ usage = normalizeUsage(filterUsageForDisplay(usage));
120
152
  if (options.expandCommands) {
121
153
  const lastTerm = usage.at(-1);
122
154
  if (usage.length > 0 && usage.slice(0, -1).every((t) => t.type === "command") && lastTerm.type === "exclusive" && lastTerm.terms.every((t) => t.length > 0 && (t[0].type === "command" || t[0].type === "option" || t[0].type === "argument" || t[0].type === "optional" && t[0].terms.length === 1 && (t[0].terms[0].type === "command" || t[0].terms[0].type === "option" || t[0].terms[0].type === "argument")))) {
123
155
  const lines = [];
124
156
  for (let command of lastTerm.terms) {
125
157
  const firstTerm = command[0];
126
- if (firstTerm?.type === "command" && firstTerm.hidden) continue;
158
+ if (firstTerm?.type === "command" && isUsageHidden(firstTerm.hidden)) continue;
127
159
  if (usage.length > 1) command = [...usage.slice(0, -1), ...command];
128
160
  lines.push(formatUsage(programName, command, options));
129
161
  }
@@ -204,9 +236,47 @@ function normalizeUsageTerm(term) {
204
236
  };
205
237
  } else return term;
206
238
  }
239
+ function filterUsageForDisplay(usage) {
240
+ const terms = [];
241
+ for (const term of usage) {
242
+ if ((term.type === "argument" || term.type === "option" || term.type === "command" || term.type === "passthrough") && isUsageHidden(term.hidden)) continue;
243
+ if (term.type === "optional") {
244
+ const filtered = filterUsageForDisplay(term.terms);
245
+ if (filtered.length > 0) terms.push({
246
+ type: "optional",
247
+ terms: filtered
248
+ });
249
+ continue;
250
+ }
251
+ if (term.type === "multiple") {
252
+ const filtered = filterUsageForDisplay(term.terms);
253
+ if (filtered.length > 0) terms.push({
254
+ type: "multiple",
255
+ terms: filtered,
256
+ min: term.min
257
+ });
258
+ continue;
259
+ }
260
+ if (term.type === "exclusive") {
261
+ const filteredBranches = term.terms.map((branch) => {
262
+ const first = branch[0];
263
+ if (first?.type === "command" && isUsageHidden(first.hidden)) return [];
264
+ return filterUsageForDisplay(branch);
265
+ }).filter((branch) => branch.length > 0);
266
+ if (filteredBranches.length > 0) terms.push({
267
+ type: "exclusive",
268
+ terms: filteredBranches
269
+ });
270
+ continue;
271
+ }
272
+ terms.push(term);
273
+ }
274
+ return terms;
275
+ }
207
276
  function* formatUsageTerms(terms, options) {
208
277
  let i = 0;
209
278
  for (const t of terms) {
279
+ if ("hidden" in t && (t.type === "argument" || t.type === "option" || t.type === "command" || t.type === "passthrough") && isUsageHidden(t.hidden)) continue;
210
280
  if (i > 0) yield {
211
281
  text: " ",
212
282
  width: 1
@@ -225,9 +295,11 @@ function* formatUsageTerms(terms, options) {
225
295
  * @returns A formatted string representation of the usage term.
226
296
  */
227
297
  function formatUsageTerm(term, options = {}) {
298
+ const visibleTerms = filterUsageForDisplay([term]);
299
+ if (visibleTerms.length < 1) return "";
228
300
  let lineWidth = 0;
229
301
  let output = "";
230
- for (const { text, width } of formatUsageTermInternal(term, options)) {
302
+ for (const { text, width } of formatUsageTermInternal(visibleTerms[0], options)) {
231
303
  if (options.maxWidth != null && lineWidth + width > options.maxWidth) {
232
304
  output += "\n";
233
305
  lineWidth = 0;
@@ -346,8 +418,14 @@ function* formatUsageTermInternal(term, options) {
346
418
  text: options?.colors ? `\x1b[2m${text}\x1b[0m` : text,
347
419
  width: 5
348
420
  };
421
+ } else if (term.type === "ellipsis") {
422
+ const text = "...";
423
+ yield {
424
+ text: options?.colors ? `\x1b[2m${text}\x1b[0m` : text,
425
+ width: 3
426
+ };
349
427
  } else throw new TypeError(`Unknown usage term type: ${term["type"]}.`);
350
428
  }
351
429
 
352
430
  //#endregion
353
- export { extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage };
431
+ export { extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };