1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-07-20 13:49:40 +02:00
mealie/frontend/composables/recipe-page/use-extract-ingredient-references.ts
Hoa (Kyle) Trinh c24d532608
feat: Migrate to Nuxt 3 framework (#5184)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2025-06-19 17:09:12 +00:00

58 lines
2.4 KiB
TypeScript

import type { RecipeIngredient } from "~/lib/api/types/recipe";
import { parseIngredientText } from "~/composables/recipes";
function normalize(word: string): string {
let normalizing = word;
normalizing = removeTrailingPunctuation(normalizing);
normalizing = removeStartingPunctuation(normalizing);
return normalizing;
}
function removeTrailingPunctuation(word: string): string {
const punctuationAtEnding = /\p{P}+$/u;
return word.replace(punctuationAtEnding, "");
}
function removeStartingPunctuation(word: string): string {
const punctuationAtBeginning = /^\p{P}+/u;
return word.replace(punctuationAtBeginning, "");
}
function ingredientMatchesWord(ingredient: RecipeIngredient, word: string, recipeIngredientAmountsDisabled: boolean) {
const searchText = parseIngredientText(ingredient, recipeIngredientAmountsDisabled);
return searchText.toLowerCase().includes(word.toLowerCase());
}
function isBlackListedWord(word: string) {
// Ignore matching blacklisted words when auto-linking - This is kind of a cludgey implementation. We're blacklisting common words but
// other common phrases trigger false positives and I'm not sure how else to approach this. In the future I maybe look at looking directly
// at the food variable and seeing if the food is in the instructions, but I still need to support those who don't want to provide the value
// and only use the "notes" feature.
const blackListedText: string[] = [
"and",
"the",
"for",
"with",
"without",
];
const blackListedRegexMatch = /\d/gm; // Match Any Number
return blackListedText.includes(word) || word.match(blackListedRegexMatch);
}
export function useExtractIngredientReferences(recipeIngredients: RecipeIngredient[], activeRefs: string[], text: string, recipeIngredientAmountsDisabled: boolean): Set<string> {
const availableIngredients = recipeIngredients
.filter(ingredient => ingredient.referenceId !== undefined)
.filter(ingredient => !activeRefs.includes(ingredient.referenceId as string));
const allMatchedIngredientIds: string[] = text
.toLowerCase()
.split(/\s/)
.map(normalize)
.filter(word => word.length > 2)
.filter(word => !isBlackListedWord(word))
.flatMap(word => availableIngredients.filter(ingredient => ingredientMatchesWord(ingredient, word, recipeIngredientAmountsDisabled)))
.map(ingredient => ingredient.referenceId as string);
// deduplicate
return new Set<string>(allMatchedIngredientIds);
}