UNPKG

@apollo/federation-internals

Version:
53 lines (44 loc) 1.7 kB
import levenshtein from 'js-levenshtein'; import { mapKeys } from './utils'; /** * Given an invalid input string and a list of valid options, returns a filtered * list of valid options sorted based on their similarity with the input. */ export function suggestionList(input: string, options: readonly string[]): string[] { const optionsByDistance = new Map<string, number>(); const threshold = Math.floor(input.length * 0.4) + 1; const inputLowerCase = input.toLowerCase(); for (const option of options) { // Special casing so that if the only mismatch is in uppper/lower-case, then the // option is always shown. const distance = inputLowerCase === option.toLowerCase() ? 1 : levenshtein(input, option); if (distance <= threshold) { optionsByDistance.set(option, distance); } } return mapKeys(optionsByDistance).sort((a, b) => { const distanceDiff = optionsByDistance.get(a)! - optionsByDistance.get(b)!; return distanceDiff !== 0 ? distanceDiff : a.localeCompare(b); }); } const MAX_SUGGESTIONS = 5; /** * Given [ A, B, C ] return ' Did you mean A, B, or C?'. */ export function didYouMean(suggestions: readonly string[]): string { const message = ' Did you mean '; const quotedSuggestions = suggestions.map((x) => `"${x}"`); switch (suggestions.length) { case 0: return ''; case 1: return message + quotedSuggestions[0] + '?'; case 2: return message + quotedSuggestions[0] + ' or ' + quotedSuggestions[1] + '?'; } const selected = quotedSuggestions.slice(0, MAX_SUGGESTIONS); const lastItem = selected.pop(); return message + selected.join(', ') + ', or ' + lastItem + '?'; }