@optique/core 0.7.12 → 0.7.13-dev.361

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.
@@ -24,6 +24,52 @@ function isOptionRequiringValue(usage, token) {
24
24
  }
25
25
  return traverse(usage);
26
26
  }
27
+ function collectLeadingCandidates(terms, optionNames, commandNames) {
28
+ if (!terms || !Array.isArray(terms)) return true;
29
+ for (const term of terms) {
30
+ if (term.type === "option") {
31
+ for (const name of term.names) optionNames.add(name);
32
+ return false;
33
+ }
34
+ if (term.type === "command") {
35
+ commandNames.add(term.name);
36
+ return false;
37
+ }
38
+ if (term.type === "argument") return false;
39
+ if (term.type === "optional") {
40
+ collectLeadingCandidates(term.terms, optionNames, commandNames);
41
+ continue;
42
+ }
43
+ if (term.type === "multiple") {
44
+ collectLeadingCandidates(term.terms, optionNames, commandNames);
45
+ if (term.min === 0) continue;
46
+ return false;
47
+ }
48
+ if (term.type === "exclusive") {
49
+ let allAlternativesSkippable = true;
50
+ for (const exclusiveUsage of term.terms) {
51
+ const alternativeSkippable = collectLeadingCandidates(exclusiveUsage, optionNames, commandNames);
52
+ allAlternativesSkippable = allAlternativesSkippable && alternativeSkippable;
53
+ }
54
+ if (allAlternativesSkippable) continue;
55
+ return false;
56
+ }
57
+ }
58
+ return true;
59
+ }
60
+ function createUnexpectedInputErrorWithScopedSuggestions(baseError, invalidInput, parsers, customFormatter) {
61
+ const options = /* @__PURE__ */ new Set();
62
+ const commands = /* @__PURE__ */ new Set();
63
+ for (const parser of parsers) collectLeadingCandidates(parser.usage, options, commands);
64
+ const candidates = new Set([...options, ...commands]);
65
+ const suggestions = require_suggestion.findSimilar(invalidInput, candidates, require_suggestion.DEFAULT_FIND_SIMILAR_OPTIONS);
66
+ const suggestionMsg = customFormatter ? customFormatter(suggestions) : require_suggestion.createSuggestionMessage(suggestions);
67
+ return suggestionMsg.length > 0 ? [
68
+ ...baseError,
69
+ require_message.text("\n\n"),
70
+ ...suggestionMsg
71
+ ] : baseError;
72
+ }
27
73
  /**
28
74
  * Extracts required (non-optional) usage terms from a usage array.
29
75
  * @param usage The usage to extract required terms from
@@ -132,7 +178,7 @@ function or(...args) {
132
178
  const token = context.buffer[0];
133
179
  const defaultMsg = require_message.message`Unexpected option or subcommand: ${require_message.optionName(token)}.`;
134
180
  if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
135
- return require_suggestion.createErrorWithSuggestions(defaultMsg, token, context.usage, "both", options?.errors?.suggestions);
181
+ return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
136
182
  })()
137
183
  };
138
184
  const orderedParsers = parsers.map((p, i) => [p, i]);
@@ -282,7 +328,7 @@ function longestMatch(...args) {
282
328
  const token = context.buffer[0];
283
329
  const defaultMsg = require_message.message`Unexpected option or subcommand: ${require_message.optionName(token)}.`;
284
330
  if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
285
- return require_suggestion.createErrorWithSuggestions(defaultMsg, token, context.usage, "both", options?.errors?.suggestions);
331
+ return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
286
332
  })()
287
333
  };
288
334
  for (let i = 0; i < parsers.length; i++) {
@@ -1,6 +1,6 @@
1
- import { message, optionName, values } from "./message.js";
1
+ import { message, optionName, text, values } from "./message.js";
2
2
  import { extractArgumentMetavars, extractCommandNames, extractOptionNames } from "./usage.js";
3
- import { createErrorWithSuggestions } from "./suggestion.js";
3
+ import { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, findSimilar } from "./suggestion.js";
4
4
 
5
5
  //#region src/constructs.ts
6
6
  /**
@@ -24,6 +24,52 @@ function isOptionRequiringValue(usage, token) {
24
24
  }
25
25
  return traverse(usage);
26
26
  }
27
+ function collectLeadingCandidates(terms, optionNames, commandNames) {
28
+ if (!terms || !Array.isArray(terms)) return true;
29
+ for (const term of terms) {
30
+ if (term.type === "option") {
31
+ for (const name of term.names) optionNames.add(name);
32
+ return false;
33
+ }
34
+ if (term.type === "command") {
35
+ commandNames.add(term.name);
36
+ return false;
37
+ }
38
+ if (term.type === "argument") return false;
39
+ if (term.type === "optional") {
40
+ collectLeadingCandidates(term.terms, optionNames, commandNames);
41
+ continue;
42
+ }
43
+ if (term.type === "multiple") {
44
+ collectLeadingCandidates(term.terms, optionNames, commandNames);
45
+ if (term.min === 0) continue;
46
+ return false;
47
+ }
48
+ if (term.type === "exclusive") {
49
+ let allAlternativesSkippable = true;
50
+ for (const exclusiveUsage of term.terms) {
51
+ const alternativeSkippable = collectLeadingCandidates(exclusiveUsage, optionNames, commandNames);
52
+ allAlternativesSkippable = allAlternativesSkippable && alternativeSkippable;
53
+ }
54
+ if (allAlternativesSkippable) continue;
55
+ return false;
56
+ }
57
+ }
58
+ return true;
59
+ }
60
+ function createUnexpectedInputErrorWithScopedSuggestions(baseError, invalidInput, parsers, customFormatter) {
61
+ const options = /* @__PURE__ */ new Set();
62
+ const commands = /* @__PURE__ */ new Set();
63
+ for (const parser of parsers) collectLeadingCandidates(parser.usage, options, commands);
64
+ const candidates = new Set([...options, ...commands]);
65
+ const suggestions = findSimilar(invalidInput, candidates, DEFAULT_FIND_SIMILAR_OPTIONS);
66
+ const suggestionMsg = customFormatter ? customFormatter(suggestions) : createSuggestionMessage(suggestions);
67
+ return suggestionMsg.length > 0 ? [
68
+ ...baseError,
69
+ text("\n\n"),
70
+ ...suggestionMsg
71
+ ] : baseError;
72
+ }
27
73
  /**
28
74
  * Extracts required (non-optional) usage terms from a usage array.
29
75
  * @param usage The usage to extract required terms from
@@ -132,7 +178,7 @@ function or(...args) {
132
178
  const token = context.buffer[0];
133
179
  const defaultMsg = message`Unexpected option or subcommand: ${optionName(token)}.`;
134
180
  if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
135
- return createErrorWithSuggestions(defaultMsg, token, context.usage, "both", options?.errors?.suggestions);
181
+ return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
136
182
  })()
137
183
  };
138
184
  const orderedParsers = parsers.map((p, i) => [p, i]);
@@ -282,7 +328,7 @@ function longestMatch(...args) {
282
328
  const token = context.buffer[0];
283
329
  const defaultMsg = message`Unexpected option or subcommand: ${optionName(token)}.`;
284
330
  if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
285
- return createErrorWithSuggestions(defaultMsg, token, context.usage, "both", options?.errors?.suggestions);
331
+ return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
286
332
  })()
287
333
  };
288
334
  for (let i = 0; i < parsers.length; i++) {
@@ -183,4 +183,5 @@ function createErrorWithSuggestions(baseError, invalidInput, usage, type = "both
183
183
  //#endregion
184
184
  exports.DEFAULT_FIND_SIMILAR_OPTIONS = DEFAULT_FIND_SIMILAR_OPTIONS;
185
185
  exports.createErrorWithSuggestions = createErrorWithSuggestions;
186
+ exports.createSuggestionMessage = createSuggestionMessage;
186
187
  exports.findSimilar = findSimilar;
@@ -181,4 +181,4 @@ function createErrorWithSuggestions(baseError, invalidInput, usage, type = "both
181
181
  }
182
182
 
183
183
  //#endregion
184
- export { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, findSimilar };
184
+ export { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, findSimilar };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "0.7.12",
3
+ "version": "0.7.13-dev.361+10f5601c",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",