@optique/core 1.2.0-dev.2240 → 1.2.0-dev.2242
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/suggestion.cjs +41 -0
- package/dist/suggestion.d.cts +29 -1
- package/dist/suggestion.d.ts +29 -1
- package/dist/suggestion.js +42 -2
- package/dist/valueparser.cjs +24 -4
- package/dist/valueparser.d.cts +34 -3
- package/dist/valueparser.d.ts +34 -3
- package/dist/valueparser.js +25 -5
- package/package.json +10 -2
package/dist/suggestion.cjs
CHANGED
|
@@ -251,6 +251,46 @@ function createErrorWithSuggestions(baseError, invalidInput, usage, type = "both
|
|
|
251
251
|
] : baseError;
|
|
252
252
|
}
|
|
253
253
|
/**
|
|
254
|
+
* Appends a "Did you mean …?" hint to a base error message when the input
|
|
255
|
+
* is close to one of the candidate values.
|
|
256
|
+
*
|
|
257
|
+
* This helper is meant for closed-set value parsers (e.g. `choice()`) that
|
|
258
|
+
* want to surface near-match suggestions without re-implementing distance
|
|
259
|
+
* logic themselves. It wraps {@link findSimilar} and
|
|
260
|
+
* {@link createSuggestionMessage} and returns the `base` message unchanged
|
|
261
|
+
* when no candidates are close enough.
|
|
262
|
+
*
|
|
263
|
+
* @param base The base error message to display.
|
|
264
|
+
* @param input The invalid input the user typed.
|
|
265
|
+
* @param candidates The list of valid candidate strings to suggest from.
|
|
266
|
+
* @param options Optional thresholds; defaults to
|
|
267
|
+
* {@link DEFAULT_FIND_SIMILAR_OPTIONS}.
|
|
268
|
+
* @returns `base` with a "Did you mean …?" line appended when a close
|
|
269
|
+
* candidate is found, or `base` unchanged when none are found.
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* ```typescript
|
|
273
|
+
* const base = message`Invalid color: ${input}.`;
|
|
274
|
+
* appendValueHint(base, input, ["red", "green", "blue"]);
|
|
275
|
+
* // → "Invalid color: redd.\n\nDid you mean `red`?"
|
|
276
|
+
* ```
|
|
277
|
+
*
|
|
278
|
+
* @since 1.2.0
|
|
279
|
+
*/
|
|
280
|
+
function appendValueHint(base, input, candidates, options) {
|
|
281
|
+
const suggestions = findSimilar(input, candidates, options != null ? {
|
|
282
|
+
...DEFAULT_FIND_SIMILAR_OPTIONS,
|
|
283
|
+
...options
|
|
284
|
+
} : void 0);
|
|
285
|
+
const suggestionMsg = createSuggestionMessage(suggestions);
|
|
286
|
+
return suggestionMsg.length > 0 ? [
|
|
287
|
+
...base,
|
|
288
|
+
require_message.lineBreak(),
|
|
289
|
+
require_message.lineBreak(),
|
|
290
|
+
...suggestionMsg
|
|
291
|
+
] : base;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
254
294
|
* Creates a unique key for a suggestion to enable deduplication.
|
|
255
295
|
*
|
|
256
296
|
* For literal suggestions, the text itself is used as the key.
|
|
@@ -313,6 +353,7 @@ function deduplicateSuggestions(suggestions) {
|
|
|
313
353
|
|
|
314
354
|
//#endregion
|
|
315
355
|
exports.DEFAULT_FIND_SIMILAR_OPTIONS = DEFAULT_FIND_SIMILAR_OPTIONS;
|
|
356
|
+
exports.appendValueHint = appendValueHint;
|
|
316
357
|
exports.createErrorWithSuggestions = createErrorWithSuggestions;
|
|
317
358
|
exports.createSuggestionMessage = createSuggestionMessage;
|
|
318
359
|
exports.deduplicateSuggestions = deduplicateSuggestions;
|
package/dist/suggestion.d.cts
CHANGED
|
@@ -154,6 +154,34 @@ declare function expandCommandAliasSuggestions(usage: Usage, suggestions: readon
|
|
|
154
154
|
* @since 0.7.0
|
|
155
155
|
*/
|
|
156
156
|
declare function createErrorWithSuggestions(baseError: Message, invalidInput: string, usage: Usage, type?: "option" | "command" | "both", customFormatter?: (suggestions: readonly string[]) => Message): Message;
|
|
157
|
+
/**
|
|
158
|
+
* Appends a "Did you mean …?" hint to a base error message when the input
|
|
159
|
+
* is close to one of the candidate values.
|
|
160
|
+
*
|
|
161
|
+
* This helper is meant for closed-set value parsers (e.g. `choice()`) that
|
|
162
|
+
* want to surface near-match suggestions without re-implementing distance
|
|
163
|
+
* logic themselves. It wraps {@link findSimilar} and
|
|
164
|
+
* {@link createSuggestionMessage} and returns the `base` message unchanged
|
|
165
|
+
* when no candidates are close enough.
|
|
166
|
+
*
|
|
167
|
+
* @param base The base error message to display.
|
|
168
|
+
* @param input The invalid input the user typed.
|
|
169
|
+
* @param candidates The list of valid candidate strings to suggest from.
|
|
170
|
+
* @param options Optional thresholds; defaults to
|
|
171
|
+
* {@link DEFAULT_FIND_SIMILAR_OPTIONS}.
|
|
172
|
+
* @returns `base` with a "Did you mean …?" line appended when a close
|
|
173
|
+
* candidate is found, or `base` unchanged when none are found.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* const base = message`Invalid color: ${input}.`;
|
|
178
|
+
* appendValueHint(base, input, ["red", "green", "blue"]);
|
|
179
|
+
* // → "Invalid color: redd.\n\nDid you mean `red`?"
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* @since 1.2.0
|
|
183
|
+
*/
|
|
184
|
+
declare function appendValueHint(base: Message, input: string, candidates: readonly string[], options?: Readonly<Pick<FindSimilarOptions, "maxDistance" | "maxSuggestions">>): Message;
|
|
157
185
|
/**
|
|
158
186
|
* Removes duplicate suggestions from an array while preserving order.
|
|
159
187
|
*
|
|
@@ -185,4 +213,4 @@ declare function createErrorWithSuggestions(baseError: Message, invalidInput: st
|
|
|
185
213
|
*/
|
|
186
214
|
declare function deduplicateSuggestions(suggestions: readonly Suggestion[]): Suggestion[];
|
|
187
215
|
//#endregion
|
|
188
|
-
export { DEFAULT_FIND_SIMILAR_OPTIONS, FindSimilarOptions, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, expandCommandAliasSuggestions, findSimilar, levenshteinDistance };
|
|
216
|
+
export { DEFAULT_FIND_SIMILAR_OPTIONS, FindSimilarOptions, appendValueHint, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, expandCommandAliasSuggestions, findSimilar, levenshteinDistance };
|
package/dist/suggestion.d.ts
CHANGED
|
@@ -154,6 +154,34 @@ declare function expandCommandAliasSuggestions(usage: Usage, suggestions: readon
|
|
|
154
154
|
* @since 0.7.0
|
|
155
155
|
*/
|
|
156
156
|
declare function createErrorWithSuggestions(baseError: Message, invalidInput: string, usage: Usage, type?: "option" | "command" | "both", customFormatter?: (suggestions: readonly string[]) => Message): Message;
|
|
157
|
+
/**
|
|
158
|
+
* Appends a "Did you mean …?" hint to a base error message when the input
|
|
159
|
+
* is close to one of the candidate values.
|
|
160
|
+
*
|
|
161
|
+
* This helper is meant for closed-set value parsers (e.g. `choice()`) that
|
|
162
|
+
* want to surface near-match suggestions without re-implementing distance
|
|
163
|
+
* logic themselves. It wraps {@link findSimilar} and
|
|
164
|
+
* {@link createSuggestionMessage} and returns the `base` message unchanged
|
|
165
|
+
* when no candidates are close enough.
|
|
166
|
+
*
|
|
167
|
+
* @param base The base error message to display.
|
|
168
|
+
* @param input The invalid input the user typed.
|
|
169
|
+
* @param candidates The list of valid candidate strings to suggest from.
|
|
170
|
+
* @param options Optional thresholds; defaults to
|
|
171
|
+
* {@link DEFAULT_FIND_SIMILAR_OPTIONS}.
|
|
172
|
+
* @returns `base` with a "Did you mean …?" line appended when a close
|
|
173
|
+
* candidate is found, or `base` unchanged when none are found.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* const base = message`Invalid color: ${input}.`;
|
|
178
|
+
* appendValueHint(base, input, ["red", "green", "blue"]);
|
|
179
|
+
* // → "Invalid color: redd.\n\nDid you mean `red`?"
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* @since 1.2.0
|
|
183
|
+
*/
|
|
184
|
+
declare function appendValueHint(base: Message, input: string, candidates: readonly string[], options?: Readonly<Pick<FindSimilarOptions, "maxDistance" | "maxSuggestions">>): Message;
|
|
157
185
|
/**
|
|
158
186
|
* Removes duplicate suggestions from an array while preserving order.
|
|
159
187
|
*
|
|
@@ -185,4 +213,4 @@ declare function createErrorWithSuggestions(baseError: Message, invalidInput: st
|
|
|
185
213
|
*/
|
|
186
214
|
declare function deduplicateSuggestions(suggestions: readonly Suggestion[]): Suggestion[];
|
|
187
215
|
//#endregion
|
|
188
|
-
export { DEFAULT_FIND_SIMILAR_OPTIONS, FindSimilarOptions, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, expandCommandAliasSuggestions, findSimilar, levenshteinDistance };
|
|
216
|
+
export { DEFAULT_FIND_SIMILAR_OPTIONS, FindSimilarOptions, appendValueHint, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, expandCommandAliasSuggestions, findSimilar, levenshteinDistance };
|
package/dist/suggestion.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { message, optionName, text } from "./message.js";
|
|
1
|
+
import { lineBreak, message, optionName, text } from "./message.js";
|
|
2
2
|
import { extractCommandNames, extractOptionNames, isSuggestionHidden } from "./usage.js";
|
|
3
3
|
|
|
4
4
|
//#region src/suggestion.ts
|
|
@@ -251,6 +251,46 @@ function createErrorWithSuggestions(baseError, invalidInput, usage, type = "both
|
|
|
251
251
|
] : baseError;
|
|
252
252
|
}
|
|
253
253
|
/**
|
|
254
|
+
* Appends a "Did you mean …?" hint to a base error message when the input
|
|
255
|
+
* is close to one of the candidate values.
|
|
256
|
+
*
|
|
257
|
+
* This helper is meant for closed-set value parsers (e.g. `choice()`) that
|
|
258
|
+
* want to surface near-match suggestions without re-implementing distance
|
|
259
|
+
* logic themselves. It wraps {@link findSimilar} and
|
|
260
|
+
* {@link createSuggestionMessage} and returns the `base` message unchanged
|
|
261
|
+
* when no candidates are close enough.
|
|
262
|
+
*
|
|
263
|
+
* @param base The base error message to display.
|
|
264
|
+
* @param input The invalid input the user typed.
|
|
265
|
+
* @param candidates The list of valid candidate strings to suggest from.
|
|
266
|
+
* @param options Optional thresholds; defaults to
|
|
267
|
+
* {@link DEFAULT_FIND_SIMILAR_OPTIONS}.
|
|
268
|
+
* @returns `base` with a "Did you mean …?" line appended when a close
|
|
269
|
+
* candidate is found, or `base` unchanged when none are found.
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* ```typescript
|
|
273
|
+
* const base = message`Invalid color: ${input}.`;
|
|
274
|
+
* appendValueHint(base, input, ["red", "green", "blue"]);
|
|
275
|
+
* // → "Invalid color: redd.\n\nDid you mean `red`?"
|
|
276
|
+
* ```
|
|
277
|
+
*
|
|
278
|
+
* @since 1.2.0
|
|
279
|
+
*/
|
|
280
|
+
function appendValueHint(base, input, candidates, options) {
|
|
281
|
+
const suggestions = findSimilar(input, candidates, options != null ? {
|
|
282
|
+
...DEFAULT_FIND_SIMILAR_OPTIONS,
|
|
283
|
+
...options
|
|
284
|
+
} : void 0);
|
|
285
|
+
const suggestionMsg = createSuggestionMessage(suggestions);
|
|
286
|
+
return suggestionMsg.length > 0 ? [
|
|
287
|
+
...base,
|
|
288
|
+
lineBreak(),
|
|
289
|
+
lineBreak(),
|
|
290
|
+
...suggestionMsg
|
|
291
|
+
] : base;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
254
294
|
* Creates a unique key for a suggestion to enable deduplication.
|
|
255
295
|
*
|
|
256
296
|
* For literal suggestions, the text itself is used as the key.
|
|
@@ -312,4 +352,4 @@ function deduplicateSuggestions(suggestions) {
|
|
|
312
352
|
}
|
|
313
353
|
|
|
314
354
|
//#endregion
|
|
315
|
-
export { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, expandCommandAliasSuggestions, findSimilar, levenshteinDistance };
|
|
355
|
+
export { DEFAULT_FIND_SIMILAR_OPTIONS, appendValueHint, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, expandCommandAliasSuggestions, findSimilar, levenshteinDistance };
|
package/dist/valueparser.cjs
CHANGED
|
@@ -139,6 +139,13 @@ function choice(choices, options = {}) {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
const stringInvalidChoice = stringOptions.errors?.invalidChoice;
|
|
142
|
+
const stringSuggest = stringOptions.suggest;
|
|
143
|
+
if (stringSuggest !== void 0) {
|
|
144
|
+
const isValidString = stringSuggest === "nearest" || stringSuggest === "never";
|
|
145
|
+
const isValidObject = typeof stringSuggest === "object" && stringSuggest !== null;
|
|
146
|
+
const isValidFunction = typeof stringSuggest === "function";
|
|
147
|
+
if (!isValidString && !isValidObject && !isValidFunction) throw new TypeError(`Expected suggest to be "nearest", "never", an object, or a function, but got ${typeof stringSuggest}: ${String(stringSuggest)}.`);
|
|
148
|
+
}
|
|
142
149
|
return {
|
|
143
150
|
mode: "sync",
|
|
144
151
|
metavar: metavar$1,
|
|
@@ -149,7 +156,7 @@ function choice(choices, options = {}) {
|
|
|
149
156
|
const index = normalizedValues.indexOf(normalizedInput);
|
|
150
157
|
if (index < 0) return {
|
|
151
158
|
success: false,
|
|
152
|
-
error: formatStringChoiceError(input, stringChoices, stringInvalidChoice)
|
|
159
|
+
error: formatStringChoiceError(input, stringChoices, stringInvalidChoice, stringSuggest)
|
|
153
160
|
};
|
|
154
161
|
return {
|
|
155
162
|
success: true,
|
|
@@ -256,9 +263,22 @@ function normalizeDecimal(s) {
|
|
|
256
263
|
/**
|
|
257
264
|
* Formats error message for string choice parser.
|
|
258
265
|
*/
|
|
259
|
-
function formatStringChoiceError(input, choices, invalidChoice) {
|
|
260
|
-
|
|
261
|
-
|
|
266
|
+
function formatStringChoiceError(input, choices, invalidChoice, suggest) {
|
|
267
|
+
const base = invalidChoice ? typeof invalidChoice === "function" ? invalidChoice(input, choices) : invalidChoice : formatDefaultChoiceError(input, choices);
|
|
268
|
+
if (suggest == null || suggest === "never") return base;
|
|
269
|
+
if (suggest === "nearest") return require_suggestion.appendValueHint(base, input, choices);
|
|
270
|
+
if (typeof suggest === "function") {
|
|
271
|
+
const hints = suggest(input, choices);
|
|
272
|
+
if (!hints || hints.length === 0) return base;
|
|
273
|
+
const suggestionMsg = require_suggestion.createSuggestionMessage(hints);
|
|
274
|
+
return suggestionMsg.length > 0 ? [
|
|
275
|
+
...base,
|
|
276
|
+
require_message.lineBreak(),
|
|
277
|
+
require_message.lineBreak(),
|
|
278
|
+
...suggestionMsg
|
|
279
|
+
] : base;
|
|
280
|
+
}
|
|
281
|
+
return require_suggestion.appendValueHint(base, input, choices, suggest);
|
|
262
282
|
}
|
|
263
283
|
/**
|
|
264
284
|
* Formats error message for number choice parser.
|
package/dist/valueparser.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NonEmptyString, ensureNonEmptyString, isNonEmptyString } from "./nonempty.cjs";
|
|
2
2
|
import { Message } from "./message.cjs";
|
|
3
|
+
import { FindSimilarOptions } from "./suggestion.cjs";
|
|
3
4
|
import { Mode, ModeIterable, ModeValue, Suggestion } from "./internal/parser.cjs";
|
|
4
5
|
|
|
5
6
|
//#region src/valueparser.d.ts
|
|
@@ -101,9 +102,20 @@ interface ValueParser<M extends Mode = "sync", T = unknown> {
|
|
|
101
102
|
* Provides completion suggestions for values of this type.
|
|
102
103
|
* This is optional and used for shell completion functionality.
|
|
103
104
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
105
|
+
* In `"sync"` mode, returns an `Iterable<Suggestion>`. In `"async"`
|
|
106
|
+
* mode, returns an `AsyncIterable<Suggestion>`; the runtime consumes
|
|
107
|
+
* it with `for await`. Sleeping or yielding in multiple batches is
|
|
108
|
+
* acceptable.
|
|
109
|
+
*
|
|
110
|
+
* For async suggesters, failures must be swallowed and logged rather
|
|
111
|
+
* than propagated as exceptions, because completion is best-effort.
|
|
112
|
+
* An exception that escapes the generator will surface as broken
|
|
113
|
+
* completion in the user's shell.
|
|
114
|
+
*
|
|
115
|
+
* @param prefix The current input prefix to complete. Only yield
|
|
116
|
+
* items whose `text` starts with this prefix.
|
|
117
|
+
* @returns An iterable (or async iterable in async mode) of
|
|
118
|
+
* suggestion objects.
|
|
107
119
|
* @since 0.6.0
|
|
108
120
|
*/
|
|
109
121
|
suggest?(prefix: string): ModeIterable<M, Suggestion>;
|
|
@@ -258,6 +270,25 @@ interface ChoiceOptionsString extends ChoiceOptionsBase {
|
|
|
258
270
|
* @default `false`
|
|
259
271
|
*/
|
|
260
272
|
readonly caseInsensitive?: boolean;
|
|
273
|
+
/**
|
|
274
|
+
* Controls whether the error message for an invalid value includes a
|
|
275
|
+
* "Did you mean …?" hint based on Levenshtein distance.
|
|
276
|
+
*
|
|
277
|
+
* - `"nearest"`: append a hint with the closest valid choice(s) using
|
|
278
|
+
* default distance thresholds. Recommended for small-to-medium
|
|
279
|
+
* enumeration sets.
|
|
280
|
+
* - `{ maxDistance?, maxSuggestions? }`: same as `"nearest"` but with
|
|
281
|
+
* custom thresholds.
|
|
282
|
+
* - `"never"`: disable hints; emit the plain "not one of" error message
|
|
283
|
+
* unchanged.
|
|
284
|
+
* - A function `(input, choices) => readonly string[] | undefined`:
|
|
285
|
+
* custom suggestion logic. Return `undefined` to suppress the hint,
|
|
286
|
+
* or a non-empty array of strings to display as suggestions.
|
|
287
|
+
*
|
|
288
|
+
* @default `"never"` (backward-compatible: no hint is appended)
|
|
289
|
+
* @since 1.2.0
|
|
290
|
+
*/
|
|
291
|
+
readonly suggest?: "nearest" | "never" | Readonly<Pick<FindSimilarOptions, "maxDistance" | "maxSuggestions">> | ((input: string, choices: readonly string[]) => readonly string[] | undefined);
|
|
261
292
|
/**
|
|
262
293
|
* Custom error messages for choice parsing failures.
|
|
263
294
|
* @since 0.5.0
|
package/dist/valueparser.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NonEmptyString, ensureNonEmptyString, isNonEmptyString } from "./nonempty.js";
|
|
2
2
|
import { Message } from "./message.js";
|
|
3
|
+
import { FindSimilarOptions } from "./suggestion.js";
|
|
3
4
|
import { Mode, ModeIterable, ModeValue, Suggestion } from "./internal/parser.js";
|
|
4
5
|
|
|
5
6
|
//#region src/valueparser.d.ts
|
|
@@ -101,9 +102,20 @@ interface ValueParser<M extends Mode = "sync", T = unknown> {
|
|
|
101
102
|
* Provides completion suggestions for values of this type.
|
|
102
103
|
* This is optional and used for shell completion functionality.
|
|
103
104
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
105
|
+
* In `"sync"` mode, returns an `Iterable<Suggestion>`. In `"async"`
|
|
106
|
+
* mode, returns an `AsyncIterable<Suggestion>`; the runtime consumes
|
|
107
|
+
* it with `for await`. Sleeping or yielding in multiple batches is
|
|
108
|
+
* acceptable.
|
|
109
|
+
*
|
|
110
|
+
* For async suggesters, failures must be swallowed and logged rather
|
|
111
|
+
* than propagated as exceptions, because completion is best-effort.
|
|
112
|
+
* An exception that escapes the generator will surface as broken
|
|
113
|
+
* completion in the user's shell.
|
|
114
|
+
*
|
|
115
|
+
* @param prefix The current input prefix to complete. Only yield
|
|
116
|
+
* items whose `text` starts with this prefix.
|
|
117
|
+
* @returns An iterable (or async iterable in async mode) of
|
|
118
|
+
* suggestion objects.
|
|
107
119
|
* @since 0.6.0
|
|
108
120
|
*/
|
|
109
121
|
suggest?(prefix: string): ModeIterable<M, Suggestion>;
|
|
@@ -258,6 +270,25 @@ interface ChoiceOptionsString extends ChoiceOptionsBase {
|
|
|
258
270
|
* @default `false`
|
|
259
271
|
*/
|
|
260
272
|
readonly caseInsensitive?: boolean;
|
|
273
|
+
/**
|
|
274
|
+
* Controls whether the error message for an invalid value includes a
|
|
275
|
+
* "Did you mean …?" hint based on Levenshtein distance.
|
|
276
|
+
*
|
|
277
|
+
* - `"nearest"`: append a hint with the closest valid choice(s) using
|
|
278
|
+
* default distance thresholds. Recommended for small-to-medium
|
|
279
|
+
* enumeration sets.
|
|
280
|
+
* - `{ maxDistance?, maxSuggestions? }`: same as `"nearest"` but with
|
|
281
|
+
* custom thresholds.
|
|
282
|
+
* - `"never"`: disable hints; emit the plain "not one of" error message
|
|
283
|
+
* unchanged.
|
|
284
|
+
* - A function `(input, choices) => readonly string[] | undefined`:
|
|
285
|
+
* custom suggestion logic. Return `undefined` to suppress the hint,
|
|
286
|
+
* or a non-empty array of strings to display as suggestions.
|
|
287
|
+
*
|
|
288
|
+
* @default `"never"` (backward-compatible: no hint is appended)
|
|
289
|
+
* @since 1.2.0
|
|
290
|
+
*/
|
|
291
|
+
readonly suggest?: "nearest" | "never" | Readonly<Pick<FindSimilarOptions, "maxDistance" | "maxSuggestions">> | ((input: string, choices: readonly string[]) => readonly string[] | undefined);
|
|
261
292
|
/**
|
|
262
293
|
* Custom error messages for choice parsing failures.
|
|
263
294
|
* @since 0.5.0
|
package/dist/valueparser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cloneMessage, lineBreak, message, metavar, text, valueSet } from "./message.js";
|
|
2
2
|
import { isDerivedValueParser } from "./internal/dependency.js";
|
|
3
|
-
import { deduplicateSuggestions } from "./suggestion.js";
|
|
3
|
+
import { appendValueHint, createSuggestionMessage, deduplicateSuggestions } from "./suggestion.js";
|
|
4
4
|
import { ensureNonEmptyString, isNonEmptyString } from "./nonempty.js";
|
|
5
5
|
|
|
6
6
|
//#region src/valueparser.ts
|
|
@@ -139,6 +139,13 @@ function choice(choices, options = {}) {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
const stringInvalidChoice = stringOptions.errors?.invalidChoice;
|
|
142
|
+
const stringSuggest = stringOptions.suggest;
|
|
143
|
+
if (stringSuggest !== void 0) {
|
|
144
|
+
const isValidString = stringSuggest === "nearest" || stringSuggest === "never";
|
|
145
|
+
const isValidObject = typeof stringSuggest === "object" && stringSuggest !== null;
|
|
146
|
+
const isValidFunction = typeof stringSuggest === "function";
|
|
147
|
+
if (!isValidString && !isValidObject && !isValidFunction) throw new TypeError(`Expected suggest to be "nearest", "never", an object, or a function, but got ${typeof stringSuggest}: ${String(stringSuggest)}.`);
|
|
148
|
+
}
|
|
142
149
|
return {
|
|
143
150
|
mode: "sync",
|
|
144
151
|
metavar: metavar$1,
|
|
@@ -149,7 +156,7 @@ function choice(choices, options = {}) {
|
|
|
149
156
|
const index = normalizedValues.indexOf(normalizedInput);
|
|
150
157
|
if (index < 0) return {
|
|
151
158
|
success: false,
|
|
152
|
-
error: formatStringChoiceError(input, stringChoices, stringInvalidChoice)
|
|
159
|
+
error: formatStringChoiceError(input, stringChoices, stringInvalidChoice, stringSuggest)
|
|
153
160
|
};
|
|
154
161
|
return {
|
|
155
162
|
success: true,
|
|
@@ -256,9 +263,22 @@ function normalizeDecimal(s) {
|
|
|
256
263
|
/**
|
|
257
264
|
* Formats error message for string choice parser.
|
|
258
265
|
*/
|
|
259
|
-
function formatStringChoiceError(input, choices, invalidChoice) {
|
|
260
|
-
|
|
261
|
-
|
|
266
|
+
function formatStringChoiceError(input, choices, invalidChoice, suggest) {
|
|
267
|
+
const base = invalidChoice ? typeof invalidChoice === "function" ? invalidChoice(input, choices) : invalidChoice : formatDefaultChoiceError(input, choices);
|
|
268
|
+
if (suggest == null || suggest === "never") return base;
|
|
269
|
+
if (suggest === "nearest") return appendValueHint(base, input, choices);
|
|
270
|
+
if (typeof suggest === "function") {
|
|
271
|
+
const hints = suggest(input, choices);
|
|
272
|
+
if (!hints || hints.length === 0) return base;
|
|
273
|
+
const suggestionMsg = createSuggestionMessage(hints);
|
|
274
|
+
return suggestionMsg.length > 0 ? [
|
|
275
|
+
...base,
|
|
276
|
+
lineBreak(),
|
|
277
|
+
lineBreak(),
|
|
278
|
+
...suggestionMsg
|
|
279
|
+
] : base;
|
|
280
|
+
}
|
|
281
|
+
return appendValueHint(base, input, choices, suggest);
|
|
262
282
|
}
|
|
263
283
|
/**
|
|
264
284
|
* Formats error message for number choice parser.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@optique/core",
|
|
3
|
-
"version": "1.2.0-dev.
|
|
3
|
+
"version": "1.2.0-dev.2242",
|
|
4
4
|
"description": "Type-safe combinatorial command-line interface parser",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"CLI",
|
|
@@ -179,6 +179,14 @@
|
|
|
179
179
|
"import": "./dist/program.js",
|
|
180
180
|
"require": "./dist/program.cjs"
|
|
181
181
|
},
|
|
182
|
+
"./suggestion": {
|
|
183
|
+
"types": {
|
|
184
|
+
"import": "./dist/suggestion.d.ts",
|
|
185
|
+
"require": "./dist/suggestion.d.cts"
|
|
186
|
+
},
|
|
187
|
+
"import": "./dist/suggestion.js",
|
|
188
|
+
"require": "./dist/suggestion.cjs"
|
|
189
|
+
},
|
|
182
190
|
"./usage": {
|
|
183
191
|
"types": {
|
|
184
192
|
"import": "./dist/usage.d.ts",
|
|
@@ -208,7 +216,7 @@
|
|
|
208
216
|
"fast-check": "^4.7.0",
|
|
209
217
|
"tsdown": "^0.13.0",
|
|
210
218
|
"typescript": "^5.8.3",
|
|
211
|
-
"@optique/env": "1.2.0-dev.
|
|
219
|
+
"@optique/env": "1.2.0-dev.2242+4f7a5e89"
|
|
212
220
|
},
|
|
213
221
|
"scripts": {
|
|
214
222
|
"build": "tsdown",
|