2025-01-19 19:22:19 +01:00
|
|
|
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)[] {
|
2025-01-19 21:11:38 +01:00
|
|
|
const cleanText = removeMarkdownFormatting(text)
|
|
|
|
.split("\n")
|
|
|
|
.map((line) => line.trim().replace(/^-/, ""))
|
|
|
|
.join("\n");
|
2025-01-19 19:22:19 +01:00
|
|
|
|
|
|
|
const ingredients = parseIngredient(cleanText, {
|
|
|
|
normalizeUOM: true,
|
|
|
|
additionalUOMs: customUnits,
|
2025-01-18 00:46:05 +01:00
|
|
|
});
|
|
|
|
|
2025-01-19 19:22:19 +01:00
|
|
|
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;
|
2025-01-18 00:46:05 +01:00
|
|
|
}
|