@optique/core 0.7.0-dev.152 → 0.7.0-dev.155

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.
@@ -4,6 +4,65 @@ const require_suggestion = require('./suggestion.cjs');
4
4
 
5
5
  //#region src/constructs.ts
6
6
  /**
7
+ * Extracts required (non-optional) usage terms from a usage array.
8
+ * @param usage The usage to extract required terms from
9
+ * @returns Usage containing only required (non-optional) terms
10
+ */
11
+ function extractRequiredUsage(usage) {
12
+ const required = [];
13
+ for (const term of usage) if (term.type === "optional") continue;
14
+ else if (term.type === "exclusive") {
15
+ const requiredBranches = term.terms.map((branch) => extractRequiredUsage(branch)).filter((branch) => branch.length > 0);
16
+ if (requiredBranches.length > 0) required.push({
17
+ type: "exclusive",
18
+ terms: requiredBranches
19
+ });
20
+ } else if (term.type === "multiple") {
21
+ if (term.min > 0) {
22
+ const requiredTerms = extractRequiredUsage(term.terms);
23
+ if (requiredTerms.length > 0) required.push({
24
+ type: "multiple",
25
+ terms: requiredTerms,
26
+ min: term.min
27
+ });
28
+ }
29
+ } else required.push(term);
30
+ return required;
31
+ }
32
+ /**
33
+ * Analyzes parsers to determine what types of inputs are expected.
34
+ * @param parsers The parsers being combined
35
+ * @returns Context about what types of inputs are expected
36
+ */
37
+ function analyzeNoMatchContext(parsers) {
38
+ const combinedUsage = [{
39
+ type: "exclusive",
40
+ terms: parsers.map((p) => p.usage)
41
+ }];
42
+ const requiredUsage = extractRequiredUsage(combinedUsage);
43
+ return {
44
+ hasOptions: require_usage.extractOptionNames(requiredUsage).size > 0,
45
+ hasCommands: require_usage.extractCommandNames(requiredUsage).size > 0,
46
+ hasArguments: require_usage.extractArgumentMetavars(requiredUsage).size > 0
47
+ };
48
+ }
49
+ /**
50
+ * Generates a contextual error message based on what types of inputs
51
+ * the parsers expect (options, commands, or arguments).
52
+ * @param context Context about what types of inputs are expected
53
+ * @returns An appropriate error message
54
+ */
55
+ function generateNoMatchError(context) {
56
+ const { hasOptions, hasCommands, hasArguments } = context;
57
+ if (hasArguments && !hasOptions && !hasCommands) return require_message.message`Missing required argument.`;
58
+ else if (hasCommands && !hasOptions && !hasArguments) return require_message.message`No matching command found.`;
59
+ else if (hasOptions && !hasCommands && !hasArguments) return require_message.message`No matching option found.`;
60
+ else if (hasCommands && hasOptions && !hasArguments) return require_message.message`No matching option or command found.`;
61
+ else if (hasArguments && hasOptions && !hasCommands) return require_message.message`No matching option or argument found.`;
62
+ else if (hasArguments && hasCommands && !hasOptions) return require_message.message`No matching command or argument found.`;
63
+ else return require_message.message`No matching option, command, or argument found.`;
64
+ }
65
+ /**
7
66
  * @since 0.5.0
8
67
  */
9
68
  function or(...args) {
@@ -16,6 +75,7 @@ function or(...args) {
16
75
  parsers = args;
17
76
  options = void 0;
18
77
  }
78
+ const noMatchContext = analyzeNoMatchContext(parsers);
19
79
  return {
20
80
  $valueType: [],
21
81
  $stateType: [],
@@ -26,10 +86,14 @@ function or(...args) {
26
86
  }],
27
87
  initialState: void 0,
28
88
  complete(state) {
29
- if (state == null) return {
30
- success: false,
31
- error: options?.errors?.noMatch ?? require_message.message`No matching option or command found.`
32
- };
89
+ if (state == null) {
90
+ const customNoMatch = options?.errors?.noMatch;
91
+ const error = customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
92
+ return {
93
+ success: false,
94
+ error
95
+ };
96
+ }
33
97
  const [i, result] = state;
34
98
  if (result.success) return parsers[i].complete(result.next.state);
35
99
  return {
@@ -40,7 +104,10 @@ function or(...args) {
40
104
  parse(context) {
41
105
  let error = {
42
106
  consumed: 0,
43
- error: context.buffer.length < 1 ? options?.errors?.noMatch ?? require_message.message`No matching option or command found.` : (() => {
107
+ error: context.buffer.length < 1 ? (() => {
108
+ const customNoMatch = options?.errors?.noMatch;
109
+ return customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
110
+ })() : (() => {
44
111
  const token = context.buffer[0];
45
112
  const defaultMsg = require_message.message`Unexpected option or subcommand: ${require_message.optionName(token)}.`;
46
113
  if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
@@ -151,6 +218,7 @@ function longestMatch(...args) {
151
218
  parsers = args;
152
219
  options = void 0;
153
220
  }
221
+ const noMatchContext = analyzeNoMatchContext(parsers);
154
222
  return {
155
223
  $valueType: [],
156
224
  $stateType: [],
@@ -161,10 +229,14 @@ function longestMatch(...args) {
161
229
  }],
162
230
  initialState: void 0,
163
231
  complete(state) {
164
- if (state == null) return {
165
- success: false,
166
- error: options?.errors?.noMatch ?? require_message.message`No matching option or command found.`
167
- };
232
+ if (state == null) {
233
+ const customNoMatch = options?.errors?.noMatch;
234
+ const error = customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
235
+ return {
236
+ success: false,
237
+ error
238
+ };
239
+ }
168
240
  const [i, result] = state;
169
241
  if (result.success) return parsers[i].complete(result.next.state);
170
242
  return {
@@ -176,7 +248,10 @@ function longestMatch(...args) {
176
248
  let bestMatch = null;
177
249
  let error = {
178
250
  consumed: 0,
179
- error: context.buffer.length < 1 ? options?.errors?.noMatch ?? require_message.message`No matching option or command found.` : (() => {
251
+ error: context.buffer.length < 1 ? (() => {
252
+ const customNoMatch = options?.errors?.noMatch;
253
+ return customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
254
+ })() : (() => {
180
255
  const token = context.buffer[0];
181
256
  const defaultMsg = require_message.message`Unexpected option or subcommand: ${require_message.optionName(token)}.`;
182
257
  if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
@@ -278,6 +353,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
278
353
  }
279
354
  const parserPairs = Object.entries(parsers);
280
355
  parserPairs.sort(([_, parserA], [__, parserB]) => parserB.priority - parserA.priority);
356
+ const noMatchContext = analyzeNoMatchContext(Object.values(parsers));
281
357
  return {
282
358
  $valueType: [],
283
359
  $stateType: [],
@@ -308,7 +384,10 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
308
384
  if (customMessage) return typeof customMessage === "function" ? customMessage(token) : customMessage;
309
385
  const baseError = require_message.message`Unexpected option or argument: ${token}.`;
310
386
  return require_suggestion.createErrorWithSuggestions(baseError, token, context.usage, "both", options.errors?.suggestions);
311
- })() : options.errors?.endOfInput ?? require_message.message`Expected an option or argument, but got end of input.`
387
+ })() : (() => {
388
+ const customEndOfInput = options.errors?.endOfInput;
389
+ return customEndOfInput ? typeof customEndOfInput === "function" ? customEndOfInput(noMatchContext) : customEndOfInput : generateNoMatchError(noMatchContext);
390
+ })()
312
391
  };
313
392
  let currentContext = context;
314
393
  let anySuccess = false;
@@ -13,6 +13,25 @@ interface OrOptions {
13
13
  */
14
14
  errors?: OrErrorOptions;
15
15
  }
16
+ /**
17
+ * Context information about what types of inputs are expected,
18
+ * used for generating contextual error messages.
19
+ * @since 0.9.0
20
+ */
21
+ interface NoMatchContext {
22
+ /**
23
+ * Whether any of the parsers expect options.
24
+ */
25
+ readonly hasOptions: boolean;
26
+ /**
27
+ * Whether any of the parsers expect commands.
28
+ */
29
+ readonly hasCommands: boolean;
30
+ /**
31
+ * Whether any of the parsers expect arguments.
32
+ */
33
+ readonly hasArguments: boolean;
34
+ }
16
35
  /**
17
36
  * Options for customizing error messages in the {@link or} parser.
18
37
  * @since 0.5.0
@@ -20,8 +39,27 @@ interface OrOptions {
20
39
  interface OrErrorOptions {
21
40
  /**
22
41
  * Custom error message when no parser matches.
42
+ * Can be a static message or a function that receives context about what
43
+ * types of inputs are expected, allowing for more precise error messages.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * // Static message (overrides all cases)
48
+ * { noMatch: message`Invalid input.` }
49
+ *
50
+ * // Dynamic message based on context (for i18n, etc.)
51
+ * {
52
+ * noMatch: ({ hasOptions, hasCommands, hasArguments }) => {
53
+ * if (hasArguments && !hasOptions && !hasCommands) {
54
+ * return message`인수가 필요합니다.`; // Korean: "Argument required"
55
+ * }
56
+ * // ... other cases
57
+ * }
58
+ * }
59
+ * ```
60
+ * @since 0.9.0 - Function form added
23
61
  */
24
- noMatch?: Message;
62
+ noMatch?: Message | ((context: NoMatchContext) => Message);
25
63
  /**
26
64
  * Custom error message for unexpected input.
27
65
  * Can be a static message or a function that receives the unexpected token.
@@ -306,8 +344,27 @@ interface LongestMatchOptions {
306
344
  interface LongestMatchErrorOptions {
307
345
  /**
308
346
  * Custom error message when no parser matches.
347
+ * Can be a static message or a function that receives context about what
348
+ * types of inputs are expected, allowing for more precise error messages.
349
+ *
350
+ * @example
351
+ * ```typescript
352
+ * // Static message (overrides all cases)
353
+ * { noMatch: message`Invalid input.` }
354
+ *
355
+ * // Dynamic message based on context (for i18n, etc.)
356
+ * {
357
+ * noMatch: ({ hasOptions, hasCommands, hasArguments }) => {
358
+ * if (hasArguments && !hasOptions && !hasCommands) {
359
+ * return message`引数が必要です。`; // Japanese: "Argument required"
360
+ * }
361
+ * // ... other cases
362
+ * }
363
+ * }
364
+ * ```
365
+ * @since 0.9.0 - Function form added
309
366
  */
310
- noMatch?: Message;
367
+ noMatch?: Message | ((context: NoMatchContext) => Message);
311
368
  /**
312
369
  * Custom error message for unexpected input.
313
370
  * Can be a static message or a function that receives the unexpected token.
@@ -447,8 +504,27 @@ interface ObjectErrorOptions {
447
504
  readonly unexpectedInput?: Message | ((token: string) => Message);
448
505
  /**
449
506
  * Error message when end of input is reached unexpectedly.
507
+ * Can be a static message or a function that receives context about what
508
+ * types of inputs are expected, allowing for more precise error messages.
509
+ *
510
+ * @example
511
+ * ```typescript
512
+ * // Static message (overrides all cases)
513
+ * { endOfInput: message`Invalid input.` }
514
+ *
515
+ * // Dynamic message based on context (for i18n, etc.)
516
+ * {
517
+ * endOfInput: ({ hasOptions, hasCommands, hasArguments }) => {
518
+ * if (hasArguments && !hasOptions && !hasCommands) {
519
+ * return message`Argument manquant.`; // French: "Missing argument"
520
+ * }
521
+ * // ... other cases
522
+ * }
523
+ * }
524
+ * ```
525
+ * @since 0.9.0 - Function form added
450
526
  */
451
- readonly endOfInput?: Message;
527
+ readonly endOfInput?: Message | ((context: NoMatchContext) => Message);
452
528
  /**
453
529
  * Custom function to format suggestion messages.
454
530
  * If provided, this will be used instead of the default "Did you mean?"
@@ -1183,4 +1259,4 @@ declare function concat<TA extends readonly unknown[], TB extends readonly unkno
1183
1259
  */
1184
1260
  declare function group<TValue, TState>(label: string, parser: Parser<TValue, TState>): Parser<TValue, TState>;
1185
1261
  //#endregion
1186
- export { LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, group, longestMatch, merge, object, or, tuple };
1262
+ export { LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, group, longestMatch, merge, object, or, tuple };
@@ -13,6 +13,25 @@ interface OrOptions {
13
13
  */
14
14
  errors?: OrErrorOptions;
15
15
  }
16
+ /**
17
+ * Context information about what types of inputs are expected,
18
+ * used for generating contextual error messages.
19
+ * @since 0.9.0
20
+ */
21
+ interface NoMatchContext {
22
+ /**
23
+ * Whether any of the parsers expect options.
24
+ */
25
+ readonly hasOptions: boolean;
26
+ /**
27
+ * Whether any of the parsers expect commands.
28
+ */
29
+ readonly hasCommands: boolean;
30
+ /**
31
+ * Whether any of the parsers expect arguments.
32
+ */
33
+ readonly hasArguments: boolean;
34
+ }
16
35
  /**
17
36
  * Options for customizing error messages in the {@link or} parser.
18
37
  * @since 0.5.0
@@ -20,8 +39,27 @@ interface OrOptions {
20
39
  interface OrErrorOptions {
21
40
  /**
22
41
  * Custom error message when no parser matches.
42
+ * Can be a static message or a function that receives context about what
43
+ * types of inputs are expected, allowing for more precise error messages.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * // Static message (overrides all cases)
48
+ * { noMatch: message`Invalid input.` }
49
+ *
50
+ * // Dynamic message based on context (for i18n, etc.)
51
+ * {
52
+ * noMatch: ({ hasOptions, hasCommands, hasArguments }) => {
53
+ * if (hasArguments && !hasOptions && !hasCommands) {
54
+ * return message`인수가 필요합니다.`; // Korean: "Argument required"
55
+ * }
56
+ * // ... other cases
57
+ * }
58
+ * }
59
+ * ```
60
+ * @since 0.9.0 - Function form added
23
61
  */
24
- noMatch?: Message;
62
+ noMatch?: Message | ((context: NoMatchContext) => Message);
25
63
  /**
26
64
  * Custom error message for unexpected input.
27
65
  * Can be a static message or a function that receives the unexpected token.
@@ -306,8 +344,27 @@ interface LongestMatchOptions {
306
344
  interface LongestMatchErrorOptions {
307
345
  /**
308
346
  * Custom error message when no parser matches.
347
+ * Can be a static message or a function that receives context about what
348
+ * types of inputs are expected, allowing for more precise error messages.
349
+ *
350
+ * @example
351
+ * ```typescript
352
+ * // Static message (overrides all cases)
353
+ * { noMatch: message`Invalid input.` }
354
+ *
355
+ * // Dynamic message based on context (for i18n, etc.)
356
+ * {
357
+ * noMatch: ({ hasOptions, hasCommands, hasArguments }) => {
358
+ * if (hasArguments && !hasOptions && !hasCommands) {
359
+ * return message`引数が必要です。`; // Japanese: "Argument required"
360
+ * }
361
+ * // ... other cases
362
+ * }
363
+ * }
364
+ * ```
365
+ * @since 0.9.0 - Function form added
309
366
  */
310
- noMatch?: Message;
367
+ noMatch?: Message | ((context: NoMatchContext) => Message);
311
368
  /**
312
369
  * Custom error message for unexpected input.
313
370
  * Can be a static message or a function that receives the unexpected token.
@@ -447,8 +504,27 @@ interface ObjectErrorOptions {
447
504
  readonly unexpectedInput?: Message | ((token: string) => Message);
448
505
  /**
449
506
  * Error message when end of input is reached unexpectedly.
507
+ * Can be a static message or a function that receives context about what
508
+ * types of inputs are expected, allowing for more precise error messages.
509
+ *
510
+ * @example
511
+ * ```typescript
512
+ * // Static message (overrides all cases)
513
+ * { endOfInput: message`Invalid input.` }
514
+ *
515
+ * // Dynamic message based on context (for i18n, etc.)
516
+ * {
517
+ * endOfInput: ({ hasOptions, hasCommands, hasArguments }) => {
518
+ * if (hasArguments && !hasOptions && !hasCommands) {
519
+ * return message`Argument manquant.`; // French: "Missing argument"
520
+ * }
521
+ * // ... other cases
522
+ * }
523
+ * }
524
+ * ```
525
+ * @since 0.9.0 - Function form added
450
526
  */
451
- readonly endOfInput?: Message;
527
+ readonly endOfInput?: Message | ((context: NoMatchContext) => Message);
452
528
  /**
453
529
  * Custom function to format suggestion messages.
454
530
  * If provided, this will be used instead of the default "Did you mean?"
@@ -1183,4 +1259,4 @@ declare function concat<TA extends readonly unknown[], TB extends readonly unkno
1183
1259
  */
1184
1260
  declare function group<TValue, TState>(label: string, parser: Parser<TValue, TState>): Parser<TValue, TState>;
1185
1261
  //#endregion
1186
- export { LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, group, longestMatch, merge, object, or, tuple };
1262
+ export { LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, group, longestMatch, merge, object, or, tuple };
@@ -1,9 +1,68 @@
1
1
  import { message, optionName, values } from "./message.js";
2
- import { extractOptionNames } from "./usage.js";
2
+ import { extractArgumentMetavars, extractCommandNames, extractOptionNames } from "./usage.js";
3
3
  import { createErrorWithSuggestions } from "./suggestion.js";
4
4
 
5
5
  //#region src/constructs.ts
6
6
  /**
7
+ * Extracts required (non-optional) usage terms from a usage array.
8
+ * @param usage The usage to extract required terms from
9
+ * @returns Usage containing only required (non-optional) terms
10
+ */
11
+ function extractRequiredUsage(usage) {
12
+ const required = [];
13
+ for (const term of usage) if (term.type === "optional") continue;
14
+ else if (term.type === "exclusive") {
15
+ const requiredBranches = term.terms.map((branch) => extractRequiredUsage(branch)).filter((branch) => branch.length > 0);
16
+ if (requiredBranches.length > 0) required.push({
17
+ type: "exclusive",
18
+ terms: requiredBranches
19
+ });
20
+ } else if (term.type === "multiple") {
21
+ if (term.min > 0) {
22
+ const requiredTerms = extractRequiredUsage(term.terms);
23
+ if (requiredTerms.length > 0) required.push({
24
+ type: "multiple",
25
+ terms: requiredTerms,
26
+ min: term.min
27
+ });
28
+ }
29
+ } else required.push(term);
30
+ return required;
31
+ }
32
+ /**
33
+ * Analyzes parsers to determine what types of inputs are expected.
34
+ * @param parsers The parsers being combined
35
+ * @returns Context about what types of inputs are expected
36
+ */
37
+ function analyzeNoMatchContext(parsers) {
38
+ const combinedUsage = [{
39
+ type: "exclusive",
40
+ terms: parsers.map((p) => p.usage)
41
+ }];
42
+ const requiredUsage = extractRequiredUsage(combinedUsage);
43
+ return {
44
+ hasOptions: extractOptionNames(requiredUsage).size > 0,
45
+ hasCommands: extractCommandNames(requiredUsage).size > 0,
46
+ hasArguments: extractArgumentMetavars(requiredUsage).size > 0
47
+ };
48
+ }
49
+ /**
50
+ * Generates a contextual error message based on what types of inputs
51
+ * the parsers expect (options, commands, or arguments).
52
+ * @param context Context about what types of inputs are expected
53
+ * @returns An appropriate error message
54
+ */
55
+ function generateNoMatchError(context) {
56
+ const { hasOptions, hasCommands, hasArguments } = context;
57
+ if (hasArguments && !hasOptions && !hasCommands) return message`Missing required argument.`;
58
+ else if (hasCommands && !hasOptions && !hasArguments) return message`No matching command found.`;
59
+ else if (hasOptions && !hasCommands && !hasArguments) return message`No matching option found.`;
60
+ else if (hasCommands && hasOptions && !hasArguments) return message`No matching option or command found.`;
61
+ else if (hasArguments && hasOptions && !hasCommands) return message`No matching option or argument found.`;
62
+ else if (hasArguments && hasCommands && !hasOptions) return message`No matching command or argument found.`;
63
+ else return message`No matching option, command, or argument found.`;
64
+ }
65
+ /**
7
66
  * @since 0.5.0
8
67
  */
9
68
  function or(...args) {
@@ -16,6 +75,7 @@ function or(...args) {
16
75
  parsers = args;
17
76
  options = void 0;
18
77
  }
78
+ const noMatchContext = analyzeNoMatchContext(parsers);
19
79
  return {
20
80
  $valueType: [],
21
81
  $stateType: [],
@@ -26,10 +86,14 @@ function or(...args) {
26
86
  }],
27
87
  initialState: void 0,
28
88
  complete(state) {
29
- if (state == null) return {
30
- success: false,
31
- error: options?.errors?.noMatch ?? message`No matching option or command found.`
32
- };
89
+ if (state == null) {
90
+ const customNoMatch = options?.errors?.noMatch;
91
+ const error = customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
92
+ return {
93
+ success: false,
94
+ error
95
+ };
96
+ }
33
97
  const [i, result] = state;
34
98
  if (result.success) return parsers[i].complete(result.next.state);
35
99
  return {
@@ -40,7 +104,10 @@ function or(...args) {
40
104
  parse(context) {
41
105
  let error = {
42
106
  consumed: 0,
43
- error: context.buffer.length < 1 ? options?.errors?.noMatch ?? message`No matching option or command found.` : (() => {
107
+ error: context.buffer.length < 1 ? (() => {
108
+ const customNoMatch = options?.errors?.noMatch;
109
+ return customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
110
+ })() : (() => {
44
111
  const token = context.buffer[0];
45
112
  const defaultMsg = message`Unexpected option or subcommand: ${optionName(token)}.`;
46
113
  if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
@@ -151,6 +218,7 @@ function longestMatch(...args) {
151
218
  parsers = args;
152
219
  options = void 0;
153
220
  }
221
+ const noMatchContext = analyzeNoMatchContext(parsers);
154
222
  return {
155
223
  $valueType: [],
156
224
  $stateType: [],
@@ -161,10 +229,14 @@ function longestMatch(...args) {
161
229
  }],
162
230
  initialState: void 0,
163
231
  complete(state) {
164
- if (state == null) return {
165
- success: false,
166
- error: options?.errors?.noMatch ?? message`No matching option or command found.`
167
- };
232
+ if (state == null) {
233
+ const customNoMatch = options?.errors?.noMatch;
234
+ const error = customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
235
+ return {
236
+ success: false,
237
+ error
238
+ };
239
+ }
168
240
  const [i, result] = state;
169
241
  if (result.success) return parsers[i].complete(result.next.state);
170
242
  return {
@@ -176,7 +248,10 @@ function longestMatch(...args) {
176
248
  let bestMatch = null;
177
249
  let error = {
178
250
  consumed: 0,
179
- error: context.buffer.length < 1 ? options?.errors?.noMatch ?? message`No matching option or command found.` : (() => {
251
+ error: context.buffer.length < 1 ? (() => {
252
+ const customNoMatch = options?.errors?.noMatch;
253
+ return customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
254
+ })() : (() => {
180
255
  const token = context.buffer[0];
181
256
  const defaultMsg = message`Unexpected option or subcommand: ${optionName(token)}.`;
182
257
  if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
@@ -278,6 +353,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
278
353
  }
279
354
  const parserPairs = Object.entries(parsers);
280
355
  parserPairs.sort(([_, parserA], [__, parserB]) => parserB.priority - parserA.priority);
356
+ const noMatchContext = analyzeNoMatchContext(Object.values(parsers));
281
357
  return {
282
358
  $valueType: [],
283
359
  $stateType: [],
@@ -308,7 +384,10 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
308
384
  if (customMessage) return typeof customMessage === "function" ? customMessage(token) : customMessage;
309
385
  const baseError = message`Unexpected option or argument: ${token}.`;
310
386
  return createErrorWithSuggestions(baseError, token, context.usage, "both", options.errors?.suggestions);
311
- })() : options.errors?.endOfInput ?? message`Expected an option or argument, but got end of input.`
387
+ })() : (() => {
388
+ const customEndOfInput = options.errors?.endOfInput;
389
+ return customEndOfInput ? typeof customEndOfInput === "function" ? customEndOfInput(noMatchContext) : customEndOfInput : generateNoMatchError(noMatchContext);
390
+ })()
312
391
  };
313
392
  let currentContext = context;
314
393
  let anySuccess = false;
package/dist/facade.cjs CHANGED
@@ -343,7 +343,7 @@ function classifyResult(result, args) {
343
343
  * Handles shell completion requests.
344
344
  * @since 0.6.0
345
345
  */
346
- function handleCompletion(completionArgs, programName, parser, completionParser, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth) {
346
+ function handleCompletion(completionArgs, programName, parser, completionParser, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, completionMode) {
347
347
  const shellName = completionArgs[0] || "";
348
348
  const args = completionArgs.slice(1);
349
349
  if (!shellName) {
@@ -375,7 +375,8 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
375
375
  return onError(1);
376
376
  }
377
377
  if (args.length === 0) {
378
- const script = shell.generateScript(programName, ["completion", shellName]);
378
+ const completionArg = completionMode === "option" ? "--completion" : "completion";
379
+ const script = shell.generateScript(programName, [completionArg, shellName]);
379
380
  stdout(script);
380
381
  } else {
381
382
  const suggestions = require_parser.suggest(parser, args);
@@ -456,7 +457,7 @@ function run(parser, programName, args, options = {}) {
456
457
  } : createCompletionParser(completion, programName, availableShells, completionName);
457
458
  if (options.completion) {
458
459
  const hasHelpOption = args.includes("--help");
459
- if ((completionMode === "command" || completionMode === "both") && args.length >= 1 && ((completionName === "singular" || completionName === "both" ? args[0] === "completion" : false) || (completionName === "plural" || completionName === "both" ? args[0] === "completions" : false)) && !hasHelpOption) return handleCompletion(args.slice(1), programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth);
460
+ if ((completionMode === "command" || completionMode === "both") && args.length >= 1 && ((completionName === "singular" || completionName === "both" ? args[0] === "completion" : false) || (completionName === "plural" || completionName === "both" ? args[0] === "completions" : false)) && !hasHelpOption) return handleCompletion(args.slice(1), programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, completionMode);
460
461
  if (completionMode === "option" || completionMode === "both") for (let i = 0; i < args.length; i++) {
461
462
  const arg = args[i];
462
463
  const singularMatch = completionName === "singular" || completionName === "both" ? arg.startsWith("--completion=") : false;
@@ -464,14 +465,14 @@ function run(parser, programName, args, options = {}) {
464
465
  if (singularMatch || pluralMatch) {
465
466
  const shell = arg.slice(arg.indexOf("=") + 1);
466
467
  const completionArgs = args.slice(i + 1);
467
- return handleCompletion([shell, ...completionArgs], programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth);
468
+ return handleCompletion([shell, ...completionArgs], programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, completionMode);
468
469
  } else {
469
470
  const singularMatchExact = completionName === "singular" || completionName === "both" ? arg === "--completion" : false;
470
471
  const pluralMatchExact = completionName === "plural" || completionName === "both" ? arg === "--completions" : false;
471
472
  if ((singularMatchExact || pluralMatchExact) && i + 1 < args.length) {
472
473
  const shell = args[i + 1];
473
474
  const completionArgs = args.slice(i + 2);
474
- return handleCompletion([shell, ...completionArgs], programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth);
475
+ return handleCompletion([shell, ...completionArgs], programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, completionMode);
475
476
  }
476
477
  }
477
478
  }
package/dist/facade.js CHANGED
@@ -343,7 +343,7 @@ function classifyResult(result, args) {
343
343
  * Handles shell completion requests.
344
344
  * @since 0.6.0
345
345
  */
346
- function handleCompletion(completionArgs, programName, parser, completionParser, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth) {
346
+ function handleCompletion(completionArgs, programName, parser, completionParser, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, completionMode) {
347
347
  const shellName = completionArgs[0] || "";
348
348
  const args = completionArgs.slice(1);
349
349
  if (!shellName) {
@@ -375,7 +375,8 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
375
375
  return onError(1);
376
376
  }
377
377
  if (args.length === 0) {
378
- const script = shell.generateScript(programName, ["completion", shellName]);
378
+ const completionArg = completionMode === "option" ? "--completion" : "completion";
379
+ const script = shell.generateScript(programName, [completionArg, shellName]);
379
380
  stdout(script);
380
381
  } else {
381
382
  const suggestions = suggest(parser, args);
@@ -456,7 +457,7 @@ function run(parser, programName, args, options = {}) {
456
457
  } : createCompletionParser(completion, programName, availableShells, completionName);
457
458
  if (options.completion) {
458
459
  const hasHelpOption = args.includes("--help");
459
- if ((completionMode === "command" || completionMode === "both") && args.length >= 1 && ((completionName === "singular" || completionName === "both" ? args[0] === "completion" : false) || (completionName === "plural" || completionName === "both" ? args[0] === "completions" : false)) && !hasHelpOption) return handleCompletion(args.slice(1), programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth);
460
+ if ((completionMode === "command" || completionMode === "both") && args.length >= 1 && ((completionName === "singular" || completionName === "both" ? args[0] === "completion" : false) || (completionName === "plural" || completionName === "both" ? args[0] === "completions" : false)) && !hasHelpOption) return handleCompletion(args.slice(1), programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, completionMode);
460
461
  if (completionMode === "option" || completionMode === "both") for (let i = 0; i < args.length; i++) {
461
462
  const arg = args[i];
462
463
  const singularMatch = completionName === "singular" || completionName === "both" ? arg.startsWith("--completion=") : false;
@@ -464,14 +465,14 @@ function run(parser, programName, args, options = {}) {
464
465
  if (singularMatch || pluralMatch) {
465
466
  const shell = arg.slice(arg.indexOf("=") + 1);
466
467
  const completionArgs = args.slice(i + 1);
467
- return handleCompletion([shell, ...completionArgs], programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth);
468
+ return handleCompletion([shell, ...completionArgs], programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, completionMode);
468
469
  } else {
469
470
  const singularMatchExact = completionName === "singular" || completionName === "both" ? arg === "--completion" : false;
470
471
  const pluralMatchExact = completionName === "plural" || completionName === "both" ? arg === "--completions" : false;
471
472
  if ((singularMatchExact || pluralMatchExact) && i + 1 < args.length) {
472
473
  const shell = args[i + 1];
473
474
  const completionArgs = args.slice(i + 2);
474
- return handleCompletion([shell, ...completionArgs], programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth);
475
+ return handleCompletion([shell, ...completionArgs], programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, completionMode);
475
476
  }
476
477
  }
477
478
  }
package/dist/index.cjs CHANGED
@@ -19,6 +19,7 @@ exports.commandLine = require_message.commandLine;
19
19
  exports.concat = require_constructs.concat;
20
20
  exports.constant = require_primitives.constant;
21
21
  exports.envVar = require_message.envVar;
22
+ exports.extractArgumentMetavars = require_usage.extractArgumentMetavars;
22
23
  exports.extractCommandNames = require_usage.extractCommandNames;
23
24
  exports.extractOptionNames = require_usage.extractOptionNames;
24
25
  exports.fish = require_completion.fish;
package/dist/index.d.cts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { Message, MessageFormatOptions, MessageTerm, commandLine, envVar, formatMessage, message, metavar, optionName, optionNames, text, value, values } from "./message.cjs";
2
- import { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.cjs";
2
+ import { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.cjs";
3
3
  import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, ShowDefaultOptions, formatDocPage } from "./doc.cjs";
4
4
  import { ChoiceOptions, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.cjs";
5
5
  import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOptions, map, multiple, optional, withDefault } from "./modifiers.cjs";
6
6
  import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, OptionErrorOptions, OptionOptions, argument, command, constant, flag, option } from "./primitives.cjs";
7
7
  import { DocState, InferValue, Parser, ParserContext, ParserResult, Result, Suggestion, getDocPage, parse, suggest } from "./parser.cjs";
8
- import { LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, group, longestMatch, merge, object, or, tuple } from "./constructs.cjs";
8
+ import { LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, group, longestMatch, merge, object, or, tuple } from "./constructs.cjs";
9
9
  import { ShellCompletion, bash, fish, nu, pwsh, zsh } from "./completion.cjs";
10
10
  import { RunError, RunOptions, run } from "./facade.cjs";
11
- export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, commandLine, concat, constant, envVar, extractCommandNames, extractOptionNames, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
11
+ export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, commandLine, concat, constant, envVar, extractArgumentMetavars, extractCommandNames, extractOptionNames, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { Message, MessageFormatOptions, MessageTerm, commandLine, envVar, formatMessage, message, metavar, optionName, optionNames, text, value, values } from "./message.js";
2
- import { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
2
+ import { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
3
3
  import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, ShowDefaultOptions, formatDocPage } from "./doc.js";
4
4
  import { ChoiceOptions, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.js";
5
5
  import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOptions, map, multiple, optional, withDefault } from "./modifiers.js";
6
6
  import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, OptionErrorOptions, OptionOptions, argument, command, constant, flag, option } from "./primitives.js";
7
7
  import { DocState, InferValue, Parser, ParserContext, ParserResult, Result, Suggestion, getDocPage, parse, suggest } from "./parser.js";
8
- import { LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
8
+ import { LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
9
9
  import { ShellCompletion, bash, fish, nu, pwsh, zsh } from "./completion.js";
10
10
  import { RunError, RunOptions, run } from "./facade.js";
11
- export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, commandLine, concat, constant, envVar, extractCommandNames, extractOptionNames, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
11
+ export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, commandLine, concat, constant, envVar, extractArgumentMetavars, extractCommandNames, extractOptionNames, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { commandLine, envVar, formatMessage, message, metavar, optionName, optionNames, text, value, values } from "./message.js";
2
- import { extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
2
+ import { extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
3
3
  import { concat, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
4
4
  import { formatDocPage } from "./doc.js";
5
5
  import { bash, fish, nu, pwsh, zsh } from "./completion.js";
@@ -9,4 +9,4 @@ import { argument, command, constant, flag, option } from "./primitives.js";
9
9
  import { getDocPage, parse, suggest } from "./parser.js";
10
10
  import { RunError, run } from "./facade.js";
11
11
 
12
- export { RunError, WithDefaultError, argument, bash, choice, command, commandLine, concat, constant, envVar, extractCommandNames, extractOptionNames, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
12
+ export { RunError, WithDefaultError, argument, bash, choice, command, commandLine, concat, constant, envVar, extractArgumentMetavars, extractCommandNames, extractOptionNames, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
package/dist/parser.d.cts CHANGED
@@ -4,7 +4,7 @@ import { DocFragments, DocPage } from "./doc.cjs";
4
4
  import { ValueParserResult } from "./valueparser.cjs";
5
5
  import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOptions, map, multiple, optional, withDefault } from "./modifiers.cjs";
6
6
  import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, OptionErrorOptions, OptionOptions, argument, command, constant, flag, option } from "./primitives.cjs";
7
- import { LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, group, longestMatch, merge, object, or, tuple } from "./constructs.cjs";
7
+ import { LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, group, longestMatch, merge, object, or, tuple } from "./constructs.cjs";
8
8
 
9
9
  //#region src/parser.d.ts
10
10
 
@@ -323,4 +323,4 @@ declare function suggest<T>(parser: Parser<T, unknown>, args: readonly [string,
323
323
  */
324
324
  declare function getDocPage(parser: Parser<unknown, unknown>, args?: readonly string[]): DocPage | undefined;
325
325
  //#endregion
326
- export { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, DocState, FlagErrorOptions, FlagOptions, InferValue, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, Suggestion, TupleOptions, WithDefaultError, WithDefaultOptions, argument, command, concat, constant, flag, getDocPage, group, longestMatch, map, merge, multiple, object, option, optional, or, parse, suggest, tuple, withDefault };
326
+ export { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, DocState, FlagErrorOptions, FlagOptions, InferValue, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, MultipleErrorOptions, MultipleOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, Suggestion, TupleOptions, WithDefaultError, WithDefaultOptions, argument, command, concat, constant, flag, getDocPage, group, longestMatch, map, merge, multiple, object, option, optional, or, parse, suggest, tuple, withDefault };
package/dist/parser.d.ts CHANGED
@@ -4,7 +4,7 @@ import { DocFragments, DocPage } from "./doc.js";
4
4
  import { ValueParserResult } from "./valueparser.js";
5
5
  import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOptions, map, multiple, optional, withDefault } from "./modifiers.js";
6
6
  import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, OptionErrorOptions, OptionOptions, argument, command, constant, flag, option } from "./primitives.js";
7
- import { LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
7
+ import { LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
8
8
 
9
9
  //#region src/parser.d.ts
10
10
 
@@ -323,4 +323,4 @@ declare function suggest<T>(parser: Parser<T, unknown>, args: readonly [string,
323
323
  */
324
324
  declare function getDocPage(parser: Parser<unknown, unknown>, args?: readonly string[]): DocPage | undefined;
325
325
  //#endregion
326
- export { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, DocState, FlagErrorOptions, FlagOptions, InferValue, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, Suggestion, TupleOptions, WithDefaultError, WithDefaultOptions, argument, command, concat, constant, flag, getDocPage, group, longestMatch, map, merge, multiple, object, option, optional, or, parse, suggest, tuple, withDefault };
326
+ export { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, DocState, FlagErrorOptions, FlagOptions, InferValue, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, MultipleErrorOptions, MultipleOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, Suggestion, TupleOptions, WithDefaultError, WithDefaultOptions, argument, command, concat, constant, flag, getDocPage, group, longestMatch, map, merge, multiple, object, option, optional, or, parse, suggest, tuple, withDefault };
package/dist/usage.cjs CHANGED
@@ -63,6 +63,38 @@ function extractCommandNames(usage) {
63
63
  return names;
64
64
  }
65
65
  /**
66
+ * Extracts all argument metavars from a Usage array.
67
+ *
68
+ * This function recursively traverses the usage structure and collects
69
+ * all argument metavariable names, similar to {@link extractOptionNames}
70
+ * and {@link extractCommandNames}.
71
+ *
72
+ * @param usage The usage structure to extract argument metavars from.
73
+ * @returns A Set of all argument metavars found in the usage structure.
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * const usage: Usage = [
78
+ * { type: "argument", metavar: "FILE" },
79
+ * { type: "argument", metavar: "OUTPUT" },
80
+ * ];
81
+ * const metavars = extractArgumentMetavars(usage);
82
+ * // metavars = Set(["FILE", "OUTPUT"])
83
+ * ```
84
+ * @since 0.9.0
85
+ */
86
+ function extractArgumentMetavars(usage) {
87
+ const metavars = /* @__PURE__ */ new Set();
88
+ function traverseUsage(terms) {
89
+ if (!terms || !Array.isArray(terms)) return;
90
+ for (const term of terms) if (term.type === "argument") metavars.add(term.metavar);
91
+ else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
92
+ else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
93
+ }
94
+ traverseUsage(usage);
95
+ return metavars;
96
+ }
97
+ /**
66
98
  * Formats a usage description into a human-readable string representation
67
99
  * suitable for command-line help text.
68
100
  *
@@ -299,6 +331,7 @@ function* formatUsageTermInternal(term, options) {
299
331
  }
300
332
 
301
333
  //#endregion
334
+ exports.extractArgumentMetavars = extractArgumentMetavars;
302
335
  exports.extractCommandNames = extractCommandNames;
303
336
  exports.extractOptionNames = extractOptionNames;
304
337
  exports.formatUsage = formatUsage;
package/dist/usage.d.cts CHANGED
@@ -158,6 +158,28 @@ declare function extractOptionNames(usage: Usage): Set<string>;
158
158
  * @since 0.7.0
159
159
  */
160
160
  declare function extractCommandNames(usage: Usage): Set<string>;
161
+ /**
162
+ * Extracts all argument metavars from a Usage array.
163
+ *
164
+ * This function recursively traverses the usage structure and collects
165
+ * all argument metavariable names, similar to {@link extractOptionNames}
166
+ * and {@link extractCommandNames}.
167
+ *
168
+ * @param usage The usage structure to extract argument metavars from.
169
+ * @returns A Set of all argument metavars found in the usage structure.
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * const usage: Usage = [
174
+ * { type: "argument", metavar: "FILE" },
175
+ * { type: "argument", metavar: "OUTPUT" },
176
+ * ];
177
+ * const metavars = extractArgumentMetavars(usage);
178
+ * // metavars = Set(["FILE", "OUTPUT"])
179
+ * ```
180
+ * @since 0.9.0
181
+ */
182
+ declare function extractArgumentMetavars(usage: Usage): Set<string>;
161
183
  /**
162
184
  * Options for formatting usage descriptions.
163
185
  */
@@ -256,4 +278,4 @@ interface UsageTermFormatOptions extends UsageFormatOptions {
256
278
  */
257
279
  declare function formatUsageTerm(term: UsageTerm, options?: UsageTermFormatOptions): string;
258
280
  //#endregion
259
- export { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage };
281
+ export { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage };
package/dist/usage.d.ts CHANGED
@@ -158,6 +158,28 @@ declare function extractOptionNames(usage: Usage): Set<string>;
158
158
  * @since 0.7.0
159
159
  */
160
160
  declare function extractCommandNames(usage: Usage): Set<string>;
161
+ /**
162
+ * Extracts all argument metavars from a Usage array.
163
+ *
164
+ * This function recursively traverses the usage structure and collects
165
+ * all argument metavariable names, similar to {@link extractOptionNames}
166
+ * and {@link extractCommandNames}.
167
+ *
168
+ * @param usage The usage structure to extract argument metavars from.
169
+ * @returns A Set of all argument metavars found in the usage structure.
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * const usage: Usage = [
174
+ * { type: "argument", metavar: "FILE" },
175
+ * { type: "argument", metavar: "OUTPUT" },
176
+ * ];
177
+ * const metavars = extractArgumentMetavars(usage);
178
+ * // metavars = Set(["FILE", "OUTPUT"])
179
+ * ```
180
+ * @since 0.9.0
181
+ */
182
+ declare function extractArgumentMetavars(usage: Usage): Set<string>;
161
183
  /**
162
184
  * Options for formatting usage descriptions.
163
185
  */
@@ -256,4 +278,4 @@ interface UsageTermFormatOptions extends UsageFormatOptions {
256
278
  */
257
279
  declare function formatUsageTerm(term: UsageTerm, options?: UsageTermFormatOptions): string;
258
280
  //#endregion
259
- export { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage };
281
+ export { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage };
package/dist/usage.js CHANGED
@@ -62,6 +62,38 @@ function extractCommandNames(usage) {
62
62
  return names;
63
63
  }
64
64
  /**
65
+ * Extracts all argument metavars from a Usage array.
66
+ *
67
+ * This function recursively traverses the usage structure and collects
68
+ * all argument metavariable names, similar to {@link extractOptionNames}
69
+ * and {@link extractCommandNames}.
70
+ *
71
+ * @param usage The usage structure to extract argument metavars from.
72
+ * @returns A Set of all argument metavars found in the usage structure.
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const usage: Usage = [
77
+ * { type: "argument", metavar: "FILE" },
78
+ * { type: "argument", metavar: "OUTPUT" },
79
+ * ];
80
+ * const metavars = extractArgumentMetavars(usage);
81
+ * // metavars = Set(["FILE", "OUTPUT"])
82
+ * ```
83
+ * @since 0.9.0
84
+ */
85
+ function extractArgumentMetavars(usage) {
86
+ const metavars = /* @__PURE__ */ new Set();
87
+ function traverseUsage(terms) {
88
+ if (!terms || !Array.isArray(terms)) return;
89
+ for (const term of terms) if (term.type === "argument") metavars.add(term.metavar);
90
+ else if (term.type === "optional" || term.type === "multiple") traverseUsage(term.terms);
91
+ else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
92
+ }
93
+ traverseUsage(usage);
94
+ return metavars;
95
+ }
96
+ /**
65
97
  * Formats a usage description into a human-readable string representation
66
98
  * suitable for command-line help text.
67
99
  *
@@ -298,4 +330,4 @@ function* formatUsageTermInternal(term, options) {
298
330
  }
299
331
 
300
332
  //#endregion
301
- export { extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage };
333
+ export { extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "0.7.0-dev.152+14108c27",
3
+ "version": "0.7.0-dev.155+ff7366f7",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",