@optique/core 1.2.0-dev.2241 → 1.2.0-dev.2244
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 +42 -0
- package/dist/suggestion.d.cts +29 -1
- package/dist/suggestion.d.ts +29 -1
- package/dist/suggestion.js +43 -2
- package/dist/valueparser.cjs +33 -4
- package/dist/valueparser.d.cts +20 -0
- package/dist/valueparser.d.ts +20 -0
- package/dist/valueparser.js +34 -5
- package/package.json +10 -2
package/dist/suggestion.cjs
CHANGED
|
@@ -251,6 +251,47 @@ 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
|
+
if (suggestionMsg.length === 0) return base;
|
|
287
|
+
return base.length > 0 ? [
|
|
288
|
+
...base,
|
|
289
|
+
require_message.lineBreak(),
|
|
290
|
+
require_message.lineBreak(),
|
|
291
|
+
...suggestionMsg
|
|
292
|
+
] : suggestionMsg;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
254
295
|
* Creates a unique key for a suggestion to enable deduplication.
|
|
255
296
|
*
|
|
256
297
|
* For literal suggestions, the text itself is used as the key.
|
|
@@ -313,6 +354,7 @@ function deduplicateSuggestions(suggestions) {
|
|
|
313
354
|
|
|
314
355
|
//#endregion
|
|
315
356
|
exports.DEFAULT_FIND_SIMILAR_OPTIONS = DEFAULT_FIND_SIMILAR_OPTIONS;
|
|
357
|
+
exports.appendValueHint = appendValueHint;
|
|
316
358
|
exports.createErrorWithSuggestions = createErrorWithSuggestions;
|
|
317
359
|
exports.createSuggestionMessage = createSuggestionMessage;
|
|
318
360
|
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,47 @@ 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
|
+
if (suggestionMsg.length === 0) return base;
|
|
287
|
+
return base.length > 0 ? [
|
|
288
|
+
...base,
|
|
289
|
+
lineBreak(),
|
|
290
|
+
lineBreak(),
|
|
291
|
+
...suggestionMsg
|
|
292
|
+
] : suggestionMsg;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
254
295
|
* Creates a unique key for a suggestion to enable deduplication.
|
|
255
296
|
*
|
|
256
297
|
* For literal suggestions, the text itself is used as the key.
|
|
@@ -312,4 +353,4 @@ function deduplicateSuggestions(suggestions) {
|
|
|
312
353
|
}
|
|
313
354
|
|
|
314
355
|
//#endregion
|
|
315
|
-
export { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, expandCommandAliasSuggestions, findSimilar, levenshteinDistance };
|
|
356
|
+
export { DEFAULT_FIND_SIMILAR_OPTIONS, appendValueHint, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, expandCommandAliasSuggestions, findSimilar, levenshteinDistance };
|
package/dist/valueparser.cjs
CHANGED
|
@@ -139,6 +139,22 @@ 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 isArray = Array.isArray(stringSuggest);
|
|
146
|
+
const isValidObject = typeof stringSuggest === "object" && stringSuggest !== null && !isArray;
|
|
147
|
+
const isValidFunction = typeof stringSuggest === "function";
|
|
148
|
+
if (!isValidString && !isValidObject && !isValidFunction) {
|
|
149
|
+
const actualType = isArray ? "array" : stringSuggest === null ? "null" : typeof stringSuggest;
|
|
150
|
+
throw new TypeError(`Expected suggest to be "nearest", "never", an object, or a function, but got ${actualType}: ${String(stringSuggest)}.`);
|
|
151
|
+
}
|
|
152
|
+
if (isValidObject) {
|
|
153
|
+
const obj = stringSuggest;
|
|
154
|
+
if (obj.maxDistance !== void 0 && (typeof obj.maxDistance !== "number" || !isFinite(obj.maxDistance))) throw new TypeError(`Expected suggest.maxDistance to be a finite number, but got ${typeof obj.maxDistance}: ${String(obj.maxDistance)}.`);
|
|
155
|
+
if (obj.maxSuggestions !== void 0 && (typeof obj.maxSuggestions !== "number" || !isFinite(obj.maxSuggestions))) throw new TypeError(`Expected suggest.maxSuggestions to be a finite number, but got ${typeof obj.maxSuggestions}: ${String(obj.maxSuggestions)}.`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
142
158
|
return {
|
|
143
159
|
mode: "sync",
|
|
144
160
|
metavar: metavar$1,
|
|
@@ -149,7 +165,7 @@ function choice(choices, options = {}) {
|
|
|
149
165
|
const index = normalizedValues.indexOf(normalizedInput);
|
|
150
166
|
if (index < 0) return {
|
|
151
167
|
success: false,
|
|
152
|
-
error: formatStringChoiceError(input, stringChoices, stringInvalidChoice)
|
|
168
|
+
error: formatStringChoiceError(input, stringChoices, stringInvalidChoice, stringSuggest)
|
|
153
169
|
};
|
|
154
170
|
return {
|
|
155
171
|
success: true,
|
|
@@ -256,9 +272,22 @@ function normalizeDecimal(s) {
|
|
|
256
272
|
/**
|
|
257
273
|
* Formats error message for string choice parser.
|
|
258
274
|
*/
|
|
259
|
-
function formatStringChoiceError(input, choices, invalidChoice) {
|
|
260
|
-
|
|
261
|
-
|
|
275
|
+
function formatStringChoiceError(input, choices, invalidChoice, suggest) {
|
|
276
|
+
const base = invalidChoice ? typeof invalidChoice === "function" ? invalidChoice(input, choices) : invalidChoice : formatDefaultChoiceError(input, choices);
|
|
277
|
+
if (suggest == null || suggest === "never") return base;
|
|
278
|
+
if (suggest === "nearest") return require_suggestion.appendValueHint(base, input, choices);
|
|
279
|
+
if (typeof suggest === "function") {
|
|
280
|
+
const hints = suggest(input, choices);
|
|
281
|
+
if (!Array.isArray(hints) || hints.length === 0) return base;
|
|
282
|
+
const suggestionMsg = require_suggestion.createSuggestionMessage(hints);
|
|
283
|
+
return suggestionMsg.length > 0 ? [
|
|
284
|
+
...base,
|
|
285
|
+
require_message.lineBreak(),
|
|
286
|
+
require_message.lineBreak(),
|
|
287
|
+
...suggestionMsg
|
|
288
|
+
] : base;
|
|
289
|
+
}
|
|
290
|
+
return require_suggestion.appendValueHint(base, input, choices, suggest);
|
|
262
291
|
}
|
|
263
292
|
/**
|
|
264
293
|
* 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
|
|
@@ -269,6 +270,25 @@ interface ChoiceOptionsString extends ChoiceOptionsBase {
|
|
|
269
270
|
* @default `false`
|
|
270
271
|
*/
|
|
271
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);
|
|
272
292
|
/**
|
|
273
293
|
* Custom error messages for choice parsing failures.
|
|
274
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
|
|
@@ -269,6 +270,25 @@ interface ChoiceOptionsString extends ChoiceOptionsBase {
|
|
|
269
270
|
* @default `false`
|
|
270
271
|
*/
|
|
271
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);
|
|
272
292
|
/**
|
|
273
293
|
* Custom error messages for choice parsing failures.
|
|
274
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,22 @@ 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 isArray = Array.isArray(stringSuggest);
|
|
146
|
+
const isValidObject = typeof stringSuggest === "object" && stringSuggest !== null && !isArray;
|
|
147
|
+
const isValidFunction = typeof stringSuggest === "function";
|
|
148
|
+
if (!isValidString && !isValidObject && !isValidFunction) {
|
|
149
|
+
const actualType = isArray ? "array" : stringSuggest === null ? "null" : typeof stringSuggest;
|
|
150
|
+
throw new TypeError(`Expected suggest to be "nearest", "never", an object, or a function, but got ${actualType}: ${String(stringSuggest)}.`);
|
|
151
|
+
}
|
|
152
|
+
if (isValidObject) {
|
|
153
|
+
const obj = stringSuggest;
|
|
154
|
+
if (obj.maxDistance !== void 0 && (typeof obj.maxDistance !== "number" || !isFinite(obj.maxDistance))) throw new TypeError(`Expected suggest.maxDistance to be a finite number, but got ${typeof obj.maxDistance}: ${String(obj.maxDistance)}.`);
|
|
155
|
+
if (obj.maxSuggestions !== void 0 && (typeof obj.maxSuggestions !== "number" || !isFinite(obj.maxSuggestions))) throw new TypeError(`Expected suggest.maxSuggestions to be a finite number, but got ${typeof obj.maxSuggestions}: ${String(obj.maxSuggestions)}.`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
142
158
|
return {
|
|
143
159
|
mode: "sync",
|
|
144
160
|
metavar: metavar$1,
|
|
@@ -149,7 +165,7 @@ function choice(choices, options = {}) {
|
|
|
149
165
|
const index = normalizedValues.indexOf(normalizedInput);
|
|
150
166
|
if (index < 0) return {
|
|
151
167
|
success: false,
|
|
152
|
-
error: formatStringChoiceError(input, stringChoices, stringInvalidChoice)
|
|
168
|
+
error: formatStringChoiceError(input, stringChoices, stringInvalidChoice, stringSuggest)
|
|
153
169
|
};
|
|
154
170
|
return {
|
|
155
171
|
success: true,
|
|
@@ -256,9 +272,22 @@ function normalizeDecimal(s) {
|
|
|
256
272
|
/**
|
|
257
273
|
* Formats error message for string choice parser.
|
|
258
274
|
*/
|
|
259
|
-
function formatStringChoiceError(input, choices, invalidChoice) {
|
|
260
|
-
|
|
261
|
-
|
|
275
|
+
function formatStringChoiceError(input, choices, invalidChoice, suggest) {
|
|
276
|
+
const base = invalidChoice ? typeof invalidChoice === "function" ? invalidChoice(input, choices) : invalidChoice : formatDefaultChoiceError(input, choices);
|
|
277
|
+
if (suggest == null || suggest === "never") return base;
|
|
278
|
+
if (suggest === "nearest") return appendValueHint(base, input, choices);
|
|
279
|
+
if (typeof suggest === "function") {
|
|
280
|
+
const hints = suggest(input, choices);
|
|
281
|
+
if (!Array.isArray(hints) || hints.length === 0) return base;
|
|
282
|
+
const suggestionMsg = createSuggestionMessage(hints);
|
|
283
|
+
return suggestionMsg.length > 0 ? [
|
|
284
|
+
...base,
|
|
285
|
+
lineBreak(),
|
|
286
|
+
lineBreak(),
|
|
287
|
+
...suggestionMsg
|
|
288
|
+
] : base;
|
|
289
|
+
}
|
|
290
|
+
return appendValueHint(base, input, choices, suggest);
|
|
262
291
|
}
|
|
263
292
|
/**
|
|
264
293
|
* 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.2244",
|
|
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.2244+b3c70b3c"
|
|
212
220
|
},
|
|
213
221
|
"scripts": {
|
|
214
222
|
"build": "tsdown",
|