98 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			98 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import {
 | |
|   parseIngredient,
 | |
|   unitsOfMeasure as _unitsOfMeasure,
 | |
| } from "https://esm.sh/parse-ingredient@1.2.1";
 | |
| import { Ingredient, IngredientGroup } from "@lib/recipeSchema.ts";
 | |
| import { removeMarkdownFormatting } from "@lib/string.ts";
 | |
| 
 | |
| const customUnits = {
 | |
|   tableSpoon: {
 | |
|     short: "EL",
 | |
|     plural: "Table Spoons",
 | |
|     alternates: ["el", "EL", "Tbsp", "tbsp"],
 | |
|   },
 | |
|   dose: {
 | |
|     short: "Dose",
 | |
|     plural: "Dosen",
 | |
|     alternates: ["Dose", "dose", "Dose(n)"],
 | |
|   },
 | |
|   pound: {
 | |
|     short: "lb",
 | |
|     plural: "pounds",
 | |
|     alternates: ["lb", "lbs", "pound", "pounds"],
 | |
|   },
 | |
|   teaSpoon: {
 | |
|     short: "TL",
 | |
|     plural: "Tea Spoon",
 | |
|     alternates: ["tl", "TL", "Tsp", "tsp", "teaspoon"],
 | |
|   },
 | |
|   litre: {
 | |
|     short: "L",
 | |
|     plural: "liters",
 | |
|     alternates: ["L", "l"],
 | |
|   },
 | |
|   paket: {
 | |
|     short: "Paket",
 | |
|     plural: "Pakets",
 | |
|     alternates: ["Paket", "paket"],
 | |
|   },
 | |
| };
 | |
| 
 | |
| export const unitsOfMeasure = {
 | |
|   ..._unitsOfMeasure,
 | |
|   ...customUnits,
 | |
| } as const;
 | |
| 
 | |
| export function parseIngredients(
 | |
|   text: string,
 | |
| ): (Ingredient | IngredientGroup)[] {
 | |
|   const cleanText = removeMarkdownFormatting(text)
 | |
|     .split("\n")
 | |
|     .map((line) => line.trim().replace(/^-/, ""))
 | |
|     .join("\n");
 | |
| 
 | |
|   const ingredients = parseIngredient(cleanText, {
 | |
|     normalizeUOM: true,
 | |
|     additionalUOMs: customUnits,
 | |
|   });
 | |
| 
 | |
|   const results: (Ingredient | IngredientGroup)[] = [];
 | |
|   let currentGroup: IngredientGroup | undefined;
 | |
| 
 | |
|   for (const ing of ingredients) {
 | |
|     if (ing.isGroupHeader) {
 | |
|       if (currentGroup) {
 | |
|         results.push(currentGroup);
 | |
|       }
 | |
|       currentGroup = {
 | |
|         name: ing.description.replace(/:$/, ""),
 | |
|         items: [],
 | |
|       };
 | |
|     } else {
 | |
|       const ingredient = {
 | |
|         name: ing.description.replace(/^\s?-/, "").trim(),
 | |
|         unit: ing.unitOfMeasure || "",
 | |
|         quantity: ing.quantity?.toString() || ing.quantity2?.toString() || "",
 | |
|         note: "",
 | |
|       };
 | |
| 
 | |
|       const unit = ingredient.unit.toLowerCase() as keyof typeof unitsOfMeasure;
 | |
|       if (unit in unitsOfMeasure && unit !== "cup") {
 | |
|         ingredient.unit = unitsOfMeasure[unit].short;
 | |
|       }
 | |
| 
 | |
|       if (!currentGroup) {
 | |
|         results.push(ingredient);
 | |
|       } else {
 | |
|         currentGroup.items.push(ingredient);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (currentGroup) {
 | |
|     results.push(currentGroup);
 | |
|   }
 | |
| 
 | |
|   return results;
 | |
| }
 |