@tmlmt/cooklang-parser 3.0.0-alpha.7 → 3.0.0-alpha.9
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/index.cjs +353 -322
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -15
- package/dist/index.d.ts +87 -15
- package/dist/index.js +351 -321
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
package/dist/index.cjs
CHANGED
|
@@ -39,7 +39,8 @@ __export(index_exports, {
|
|
|
39
39
|
Recipe: () => Recipe,
|
|
40
40
|
Section: () => Section,
|
|
41
41
|
ShoppingCart: () => ShoppingCart,
|
|
42
|
-
ShoppingList: () => ShoppingList
|
|
42
|
+
ShoppingList: () => ShoppingList,
|
|
43
|
+
isAlternativeSelected: () => isAlternativeSelected
|
|
43
44
|
});
|
|
44
45
|
module.exports = __toCommonJS(index_exports);
|
|
45
46
|
|
|
@@ -654,13 +655,13 @@ var InvalidQuantityFormat = class extends Error {
|
|
|
654
655
|
|
|
655
656
|
// src/utils/type_guards.ts
|
|
656
657
|
function isGroup(x) {
|
|
657
|
-
return x
|
|
658
|
+
return "and" in x || "or" in x;
|
|
658
659
|
}
|
|
659
660
|
function isOrGroup(x) {
|
|
660
|
-
return isGroup(x) &&
|
|
661
|
+
return isGroup(x) && "or" in x;
|
|
661
662
|
}
|
|
662
663
|
function isAndGroup(x) {
|
|
663
|
-
return isGroup(x) &&
|
|
664
|
+
return isGroup(x) && "and" in x;
|
|
664
665
|
}
|
|
665
666
|
function isQuantity(x) {
|
|
666
667
|
return x && typeof x === "object" && "quantity" in x;
|
|
@@ -679,8 +680,10 @@ function isValueIntegerLike(q) {
|
|
|
679
680
|
|
|
680
681
|
// src/quantities/mutations.ts
|
|
681
682
|
function extendAllUnits(q) {
|
|
682
|
-
if (
|
|
683
|
-
return {
|
|
683
|
+
if (isAndGroup(q)) {
|
|
684
|
+
return { and: q.and.map(extendAllUnits) };
|
|
685
|
+
} else if (isOrGroup(q)) {
|
|
686
|
+
return { or: q.or.map(extendAllUnits) };
|
|
684
687
|
} else {
|
|
685
688
|
const newQ = {
|
|
686
689
|
quantity: q.quantity
|
|
@@ -692,13 +695,23 @@ function extendAllUnits(q) {
|
|
|
692
695
|
}
|
|
693
696
|
}
|
|
694
697
|
function normalizeAllUnits(q) {
|
|
695
|
-
if (
|
|
696
|
-
return {
|
|
698
|
+
if (isAndGroup(q)) {
|
|
699
|
+
return { and: q.and.map(normalizeAllUnits) };
|
|
700
|
+
} else if (isOrGroup(q)) {
|
|
701
|
+
return { or: q.or.map(normalizeAllUnits) };
|
|
697
702
|
} else {
|
|
698
703
|
const newQ = {
|
|
699
704
|
quantity: q.quantity,
|
|
700
705
|
unit: resolveUnit(q.unit)
|
|
701
706
|
};
|
|
707
|
+
if (q.equivalents && q.equivalents.length > 0) {
|
|
708
|
+
const equivalentsNormalized = q.equivalents.map(
|
|
709
|
+
(eq) => normalizeAllUnits(eq)
|
|
710
|
+
);
|
|
711
|
+
return {
|
|
712
|
+
or: [newQ, ...equivalentsNormalized]
|
|
713
|
+
};
|
|
714
|
+
}
|
|
702
715
|
return newQ;
|
|
703
716
|
}
|
|
704
717
|
}
|
|
@@ -783,23 +796,23 @@ function addQuantities(q1, q2) {
|
|
|
783
796
|
function toPlainUnit(quantity) {
|
|
784
797
|
if (isQuantity(quantity))
|
|
785
798
|
return quantity.unit ? { ...quantity, unit: quantity.unit.name } : quantity;
|
|
786
|
-
else {
|
|
799
|
+
else if (isOrGroup(quantity)) {
|
|
787
800
|
return {
|
|
788
|
-
|
|
789
|
-
|
|
801
|
+
or: quantity.or.map(toPlainUnit)
|
|
802
|
+
};
|
|
803
|
+
} else {
|
|
804
|
+
return {
|
|
805
|
+
and: quantity.and.map(toPlainUnit)
|
|
790
806
|
};
|
|
791
807
|
}
|
|
792
808
|
}
|
|
793
809
|
function toExtendedUnit(q) {
|
|
794
810
|
if (isQuantity(q)) {
|
|
795
811
|
return q.unit ? { ...q, unit: { name: q.unit } } : q;
|
|
812
|
+
} else if (isOrGroup(q)) {
|
|
813
|
+
return { or: q.or.map(toExtendedUnit) };
|
|
796
814
|
} else {
|
|
797
|
-
return {
|
|
798
|
-
...q,
|
|
799
|
-
entries: q.entries.map(
|
|
800
|
-
(entry) => isQuantity(entry) ? toExtendedUnit(entry) : toExtendedUnit(entry)
|
|
801
|
-
)
|
|
802
|
-
};
|
|
815
|
+
return { and: q.and.map(toExtendedUnit) };
|
|
803
816
|
}
|
|
804
817
|
}
|
|
805
818
|
function deNormalizeQuantity(q) {
|
|
@@ -813,26 +826,24 @@ function deNormalizeQuantity(q) {
|
|
|
813
826
|
}
|
|
814
827
|
var flattenPlainUnitGroup = (summed) => {
|
|
815
828
|
if (isOrGroup(summed)) {
|
|
816
|
-
const entries = summed.
|
|
829
|
+
const entries = summed.or;
|
|
817
830
|
const andGroupEntry = entries.find(
|
|
818
|
-
(e2) =>
|
|
831
|
+
(e2) => isAndGroup(e2)
|
|
819
832
|
);
|
|
820
833
|
if (andGroupEntry) {
|
|
821
834
|
const andEntries = [];
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
}
|
|
835
|
+
const addGroupEntryContent = andGroupEntry.and;
|
|
836
|
+
for (const entry of addGroupEntryContent) {
|
|
837
|
+
andEntries.push({
|
|
838
|
+
quantity: entry.quantity,
|
|
839
|
+
...entry.unit && { unit: entry.unit }
|
|
840
|
+
});
|
|
829
841
|
}
|
|
830
842
|
const equivalentsList = entries.filter((e2) => isQuantity(e2)).map((e2) => ({ quantity: e2.quantity, unit: e2.unit }));
|
|
831
843
|
if (equivalentsList.length > 0) {
|
|
832
844
|
return [
|
|
833
845
|
{
|
|
834
|
-
|
|
835
|
-
entries: andEntries,
|
|
846
|
+
and: andEntries,
|
|
836
847
|
equivalents: equivalentsList
|
|
837
848
|
}
|
|
838
849
|
];
|
|
@@ -856,25 +867,21 @@ var flattenPlainUnitGroup = (summed) => {
|
|
|
856
867
|
const first = entries[0];
|
|
857
868
|
return [{ quantity: first.quantity, unit: first.unit }];
|
|
858
869
|
}
|
|
859
|
-
} else if (
|
|
870
|
+
} else if (isAndGroup(summed)) {
|
|
860
871
|
const andEntries = [];
|
|
861
872
|
const equivalentsList = [];
|
|
862
|
-
for (const entry of summed.
|
|
873
|
+
for (const entry of summed.and) {
|
|
863
874
|
if (isOrGroup(entry)) {
|
|
864
|
-
const orEntries = entry.
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
unit: orEntries[0].unit
|
|
871
|
-
});
|
|
872
|
-
equivalentsList.push(...orEntries.slice(1));
|
|
873
|
-
}
|
|
875
|
+
const orEntries = entry.or;
|
|
876
|
+
andEntries.push({
|
|
877
|
+
quantity: orEntries[0].quantity,
|
|
878
|
+
...orEntries[0].unit && { unit: orEntries[0].unit }
|
|
879
|
+
});
|
|
880
|
+
equivalentsList.push(...orEntries.slice(1));
|
|
874
881
|
} else if (isQuantity(entry)) {
|
|
875
882
|
andEntries.push({
|
|
876
883
|
quantity: entry.quantity,
|
|
877
|
-
unit: entry.unit
|
|
884
|
+
...entry.unit && { unit: entry.unit }
|
|
878
885
|
});
|
|
879
886
|
}
|
|
880
887
|
}
|
|
@@ -882,13 +889,14 @@ var flattenPlainUnitGroup = (summed) => {
|
|
|
882
889
|
return andEntries;
|
|
883
890
|
}
|
|
884
891
|
const result = {
|
|
885
|
-
|
|
886
|
-
entries: andEntries,
|
|
892
|
+
and: andEntries,
|
|
887
893
|
equivalents: equivalentsList
|
|
888
894
|
};
|
|
889
895
|
return [result];
|
|
890
896
|
} else {
|
|
891
|
-
return [
|
|
897
|
+
return [
|
|
898
|
+
{ quantity: summed.quantity, ...summed.unit && { unit: summed.unit } }
|
|
899
|
+
];
|
|
892
900
|
}
|
|
893
901
|
};
|
|
894
902
|
|
|
@@ -1371,11 +1379,11 @@ var deepClone = (v) => typeof structuredClone === "function" ? structuredClone(v
|
|
|
1371
1379
|
// src/quantities/alternatives.ts
|
|
1372
1380
|
function getEquivalentUnitsLists(...quantities) {
|
|
1373
1381
|
const quantitiesCopy = deepClone(quantities);
|
|
1374
|
-
const OrGroups = quantitiesCopy.filter(isOrGroup).filter((q) => q.
|
|
1382
|
+
const OrGroups = quantitiesCopy.filter(isOrGroup).filter((q) => q.or.length > 1);
|
|
1375
1383
|
const unitLists = [];
|
|
1376
1384
|
const normalizeOrGroup = (og) => ({
|
|
1377
1385
|
...og,
|
|
1378
|
-
|
|
1386
|
+
or: og.or.map((q) => ({
|
|
1379
1387
|
...q,
|
|
1380
1388
|
unit: resolveUnit(q.unit?.name, q.unit?.integerProtected)
|
|
1381
1389
|
}))
|
|
@@ -1397,7 +1405,7 @@ function getEquivalentUnitsLists(...quantities) {
|
|
|
1397
1405
|
...v,
|
|
1398
1406
|
unit: resolveUnit(v.unit?.name, v.unit?.integerProtected)
|
|
1399
1407
|
};
|
|
1400
|
-
const commonQuantity = og.
|
|
1408
|
+
const commonQuantity = og.or.find(
|
|
1401
1409
|
(q) => isQuantity(q) && areUnitsCompatible(q.unit, normalizedV.unit)
|
|
1402
1410
|
);
|
|
1403
1411
|
if (commonQuantity) {
|
|
@@ -1406,7 +1414,7 @@ function getEquivalentUnitsLists(...quantities) {
|
|
|
1406
1414
|
}
|
|
1407
1415
|
return acc;
|
|
1408
1416
|
}, []);
|
|
1409
|
-
for (const newQ of og.
|
|
1417
|
+
for (const newQ of og.or) {
|
|
1410
1418
|
if (commonUnitList.some((q) => areUnitsCompatible(q.unit, newQ.unit))) {
|
|
1411
1419
|
continue;
|
|
1412
1420
|
} else {
|
|
@@ -1417,10 +1425,10 @@ function getEquivalentUnitsLists(...quantities) {
|
|
|
1417
1425
|
}
|
|
1418
1426
|
for (const orGroup of OrGroups) {
|
|
1419
1427
|
const orGroupModified = normalizeOrGroup(orGroup);
|
|
1420
|
-
const units2 = orGroupModified.
|
|
1428
|
+
const units2 = orGroupModified.or.map((q) => q.unit);
|
|
1421
1429
|
const linkIndex = findLinkIndexForUnits(unitLists, units2);
|
|
1422
1430
|
if (linkIndex === -1) {
|
|
1423
|
-
unitLists.push(orGroupModified.
|
|
1431
|
+
unitLists.push(orGroupModified.or);
|
|
1424
1432
|
} else {
|
|
1425
1433
|
mergeOrGroupIntoList(unitLists, linkIndex, orGroupModified);
|
|
1426
1434
|
}
|
|
@@ -1516,7 +1524,7 @@ function reduceOrsToFirstEquivalent(unitList, quantities) {
|
|
|
1516
1524
|
return quantities.map((q) => {
|
|
1517
1525
|
if (isQuantity(q)) return reduceToQuantity(q);
|
|
1518
1526
|
const qListModified = sortUnitList(
|
|
1519
|
-
q.
|
|
1527
|
+
q.or.map((qq) => ({
|
|
1520
1528
|
...qq,
|
|
1521
1529
|
unit: resolveUnit(qq.unit?.name, qq.unit?.integerProtected)
|
|
1522
1530
|
}))
|
|
@@ -1562,10 +1570,10 @@ function addQuantitiesOrGroups(...quantities) {
|
|
|
1562
1570
|
if (sum.length === 1) {
|
|
1563
1571
|
return { sum: sum[0], unitsLists };
|
|
1564
1572
|
}
|
|
1565
|
-
return { sum: {
|
|
1573
|
+
return { sum: { and: sum }, unitsLists };
|
|
1566
1574
|
}
|
|
1567
1575
|
function regroupQuantitiesAndExpandEquivalents(sum, unitsLists) {
|
|
1568
|
-
const sumQuantities =
|
|
1576
|
+
const sumQuantities = isAndGroup(sum) ? sum.and : [sum];
|
|
1569
1577
|
const result = [];
|
|
1570
1578
|
const processedQuantities = /* @__PURE__ */ new Set();
|
|
1571
1579
|
for (const list of unitsLists) {
|
|
@@ -1608,12 +1616,10 @@ function regroupQuantitiesAndExpandEquivalents(sum, unitsLists) {
|
|
|
1608
1616
|
});
|
|
1609
1617
|
if (main.length + equivalents.length > 1) {
|
|
1610
1618
|
const resultMain = main.length > 1 ? {
|
|
1611
|
-
|
|
1612
|
-
entries: main.map(deNormalizeQuantity)
|
|
1619
|
+
and: main.map(deNormalizeQuantity)
|
|
1613
1620
|
} : deNormalizeQuantity(main[0]);
|
|
1614
1621
|
result.push({
|
|
1615
|
-
|
|
1616
|
-
entries: [resultMain, ...equivalents]
|
|
1622
|
+
or: [resultMain, ...equivalents]
|
|
1617
1623
|
});
|
|
1618
1624
|
} else {
|
|
1619
1625
|
result.push(deNormalizeQuantity(main[0]));
|
|
@@ -1631,7 +1637,7 @@ function addEquivalentsAndSimplify(...quantities) {
|
|
|
1631
1637
|
if (regrouped.length === 1) {
|
|
1632
1638
|
return toPlainUnit(regrouped[0]);
|
|
1633
1639
|
} else {
|
|
1634
|
-
return {
|
|
1640
|
+
return { and: regrouped.map(toPlainUnit) };
|
|
1635
1641
|
}
|
|
1636
1642
|
}
|
|
1637
1643
|
|
|
@@ -1648,8 +1654,7 @@ var _Recipe = class _Recipe {
|
|
|
1648
1654
|
*/
|
|
1649
1655
|
__publicField(this, "metadata", {});
|
|
1650
1656
|
/**
|
|
1651
|
-
* The
|
|
1652
|
-
* Contains the full context including alternatives list and active selection index.
|
|
1657
|
+
* The possible choices of alternative ingredients for this recipe.
|
|
1653
1658
|
*/
|
|
1654
1659
|
__publicField(this, "choices", {
|
|
1655
1660
|
ingredientItems: /* @__PURE__ */ new Map(),
|
|
@@ -1712,7 +1717,7 @@ var _Recipe = class _Recipe {
|
|
|
1712
1717
|
if (!regexMatchGroups || !regexMatchGroups.arbitraryQuantity) return;
|
|
1713
1718
|
const quantityMatch = regexMatchGroups.arbitraryQuantity?.trim().match(quantityAlternativeRegex);
|
|
1714
1719
|
if (quantityMatch?.groups) {
|
|
1715
|
-
const value =
|
|
1720
|
+
const value = parseQuantityInput(quantityMatch.groups.quantity);
|
|
1716
1721
|
const unit = quantityMatch.groups.unit;
|
|
1717
1722
|
const name = regexMatchGroups.arbitraryName || void 0;
|
|
1718
1723
|
if (!value || value.type === "fixed" && value.value.type === "text") {
|
|
@@ -2015,286 +2020,229 @@ var _Recipe = class _Recipe {
|
|
|
2015
2020
|
* @internal
|
|
2016
2021
|
*/
|
|
2017
2022
|
_populate_ingredient_quantities() {
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2023
|
+
for (const ing of this.ingredients) {
|
|
2024
|
+
delete ing.quantities;
|
|
2025
|
+
delete ing.usedAsPrimary;
|
|
2026
|
+
}
|
|
2027
|
+
const ingredientsWithQuantities = this.getIngredientQuantities();
|
|
2028
|
+
const matchedIndices = /* @__PURE__ */ new Set();
|
|
2029
|
+
for (const computed of ingredientsWithQuantities) {
|
|
2030
|
+
const idx = this.ingredients.findIndex(
|
|
2031
|
+
(ing2, i2) => ing2.name === computed.name && !matchedIndices.has(i2)
|
|
2032
|
+
);
|
|
2033
|
+
matchedIndices.add(idx);
|
|
2034
|
+
const ing = this.ingredients[idx];
|
|
2035
|
+
if (computed.quantities) {
|
|
2036
|
+
ing.quantities = computed.quantities;
|
|
2021
2037
|
}
|
|
2022
|
-
if (
|
|
2023
|
-
|
|
2038
|
+
if (computed.usedAsPrimary) {
|
|
2039
|
+
ing.usedAsPrimary = true;
|
|
2024
2040
|
}
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Gets ingredients with their quantities populated, optionally filtered by section/step
|
|
2045
|
+
* and respecting user choices for alternatives.
|
|
2046
|
+
*
|
|
2047
|
+
* When no options are provided, returns all recipe ingredients with quantities
|
|
2048
|
+
* calculated using primary alternatives (same as after parsing).
|
|
2049
|
+
*
|
|
2050
|
+
* @param options - Options for filtering and choice selection:
|
|
2051
|
+
* - `section`: Filter to a specific section (Section object or 0-based index)
|
|
2052
|
+
* - `step`: Filter to a specific step (Step object or 0-based index)
|
|
2053
|
+
* - `choices`: Choices for alternative ingredients (defaults to primary)
|
|
2054
|
+
* @returns Array of Ingredient objects with quantities populated
|
|
2055
|
+
*
|
|
2056
|
+
* @example
|
|
2057
|
+
* ```typescript
|
|
2058
|
+
* // Get all ingredients with primary alternatives
|
|
2059
|
+
* const ingredients = recipe.getIngredientQuantities();
|
|
2060
|
+
*
|
|
2061
|
+
* // Get ingredients for a specific section
|
|
2062
|
+
* const sectionIngredients = recipe.getIngredientQuantities({ section: 0 });
|
|
2063
|
+
*
|
|
2064
|
+
* // Get ingredients with specific choices applied
|
|
2065
|
+
* const withChoices = recipe.getIngredientQuantities({
|
|
2066
|
+
* choices: { ingredientItems: new Map([['ingredient-item-2', 1]]) }
|
|
2067
|
+
* });
|
|
2068
|
+
* ```
|
|
2069
|
+
*/
|
|
2070
|
+
getIngredientQuantities(options) {
|
|
2071
|
+
const { section, step, choices } = options || {};
|
|
2072
|
+
const sectionsToProcess = section !== void 0 ? (() => {
|
|
2073
|
+
const idx = typeof section === "number" ? section : this.sections.indexOf(section);
|
|
2074
|
+
return idx >= 0 && idx < this.sections.length ? [this.sections[idx]] : [];
|
|
2075
|
+
})() : this.sections;
|
|
2028
2076
|
const ingredientGroups = /* @__PURE__ */ new Map();
|
|
2029
|
-
|
|
2030
|
-
|
|
2077
|
+
const selectedIndices = /* @__PURE__ */ new Set();
|
|
2078
|
+
const referencedIndices = /* @__PURE__ */ new Set();
|
|
2079
|
+
for (const currentSection of sectionsToProcess) {
|
|
2080
|
+
const allSteps = currentSection.content.filter(
|
|
2031
2081
|
(item) => item.type === "step"
|
|
2032
|
-
)
|
|
2033
|
-
|
|
2082
|
+
);
|
|
2083
|
+
const stepsToProcess = step === void 0 ? allSteps : typeof step === "number" ? step >= 0 && step < allSteps.length ? [allSteps[step]] : [] : allSteps.includes(step) ? [step] : [];
|
|
2084
|
+
for (const currentStep of stepsToProcess) {
|
|
2085
|
+
for (const item of currentStep.items.filter(
|
|
2034
2086
|
(item2) => item2.type === "ingredient"
|
|
2035
2087
|
)) {
|
|
2036
|
-
const
|
|
2037
|
-
const
|
|
2038
|
-
|
|
2039
|
-
|
|
2088
|
+
const isGrouped = "group" in item && item.group !== void 0;
|
|
2089
|
+
const groupAlternatives = isGrouped ? this.choices.ingredientGroups.get(item.group) : void 0;
|
|
2090
|
+
let selectedAltIndex = 0;
|
|
2091
|
+
let isSelected = false;
|
|
2092
|
+
let hasExplicitChoice = false;
|
|
2093
|
+
if (isGrouped) {
|
|
2094
|
+
const groupChoice = choices?.ingredientGroups?.get(item.group);
|
|
2095
|
+
hasExplicitChoice = groupChoice !== void 0;
|
|
2096
|
+
const targetIndex = groupChoice ?? 0;
|
|
2097
|
+
isSelected = groupAlternatives?.[targetIndex]?.itemId === item.id;
|
|
2098
|
+
} else {
|
|
2099
|
+
const itemChoice = choices?.ingredientItems?.get(item.id);
|
|
2100
|
+
hasExplicitChoice = itemChoice !== void 0;
|
|
2101
|
+
selectedAltIndex = itemChoice ?? 0;
|
|
2102
|
+
isSelected = true;
|
|
2040
2103
|
}
|
|
2041
|
-
const
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
}
|
|
2104
|
+
const alternative = item.alternatives[selectedAltIndex];
|
|
2105
|
+
if (!alternative || !isSelected) continue;
|
|
2106
|
+
selectedIndices.add(alternative.index);
|
|
2107
|
+
const allAlts = isGrouped ? groupAlternatives : item.alternatives;
|
|
2108
|
+
for (const alt of allAlts) {
|
|
2109
|
+
referencedIndices.add(alt.index);
|
|
2048
2110
|
}
|
|
2049
|
-
if (!
|
|
2050
|
-
const
|
|
2051
|
-
|
|
2052
|
-
|
|
2111
|
+
if (!alternative.itemQuantity) continue;
|
|
2112
|
+
const baseQty = {
|
|
2113
|
+
quantity: alternative.itemQuantity.quantity,
|
|
2114
|
+
...alternative.itemQuantity.unit && {
|
|
2053
2115
|
unit: alternative.itemQuantity.unit
|
|
2054
2116
|
}
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
allQuantities.push(...alternative.itemQuantity.equivalents);
|
|
2058
|
-
}
|
|
2059
|
-
const quantityEntry = allQuantities.length === 1 ? allQuantities[0] : { type: "or", entries: allQuantities };
|
|
2060
|
-
const hasInlineAlternatives = item.alternatives.length > 1;
|
|
2061
|
-
const hasGroupedAlternatives = isGroupedItem && this.choices.ingredientGroups.has(item.group);
|
|
2117
|
+
};
|
|
2118
|
+
const quantityEntry = alternative.itemQuantity.equivalents?.length ? { or: [baseQty, ...alternative.itemQuantity.equivalents] } : baseQty;
|
|
2062
2119
|
let alternativeRefs;
|
|
2063
|
-
if (
|
|
2064
|
-
alternativeRefs =
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
const
|
|
2068
|
-
index: otherAlt.index
|
|
2069
|
-
};
|
|
2120
|
+
if (!hasExplicitChoice && allAlts.length > 1) {
|
|
2121
|
+
alternativeRefs = allAlts.filter(
|
|
2122
|
+
(alt) => isGrouped ? alt.itemId !== item.id : alt.index !== alternative.index
|
|
2123
|
+
).map((otherAlt) => {
|
|
2124
|
+
const ref = { index: otherAlt.index };
|
|
2070
2125
|
if (otherAlt.itemQuantity) {
|
|
2071
2126
|
const altQty = {
|
|
2072
|
-
quantity: otherAlt.itemQuantity.quantity
|
|
2127
|
+
quantity: otherAlt.itemQuantity.quantity,
|
|
2128
|
+
...otherAlt.itemQuantity.unit && {
|
|
2129
|
+
unit: otherAlt.itemQuantity.unit.name
|
|
2130
|
+
},
|
|
2131
|
+
...otherAlt.itemQuantity.equivalents && {
|
|
2132
|
+
equivalents: otherAlt.itemQuantity.equivalents.map(
|
|
2133
|
+
(eq) => toPlainUnit(eq)
|
|
2134
|
+
)
|
|
2135
|
+
}
|
|
2073
2136
|
};
|
|
2074
|
-
|
|
2075
|
-
altQty.unit = otherAlt.itemQuantity.unit.name;
|
|
2076
|
-
}
|
|
2077
|
-
if (otherAlt.itemQuantity.equivalents) {
|
|
2078
|
-
altQty.equivalents = otherAlt.itemQuantity.equivalents.map(
|
|
2079
|
-
(eq) => toPlainUnit(eq)
|
|
2080
|
-
);
|
|
2081
|
-
}
|
|
2082
|
-
newRef.quantities = [altQty];
|
|
2137
|
+
ref.quantities = [altQty];
|
|
2083
2138
|
}
|
|
2084
|
-
|
|
2085
|
-
}
|
|
2086
|
-
}
|
|
2087
|
-
|
|
2088
|
-
|
|
2139
|
+
return ref;
|
|
2140
|
+
});
|
|
2141
|
+
}
|
|
2142
|
+
const altIndices = getAlternativeSignature(alternativeRefs) ?? "";
|
|
2143
|
+
let signature;
|
|
2144
|
+
if (isGrouped) {
|
|
2145
|
+
const resolvedUnit = resolveUnit(
|
|
2146
|
+
alternative.itemQuantity.unit?.name
|
|
2089
2147
|
);
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
altQty.unit = otherAlt.itemQuantity.unit.name;
|
|
2099
|
-
}
|
|
2100
|
-
if (otherAlt.itemQuantity.equivalents) {
|
|
2101
|
-
altQty.equivalents = otherAlt.itemQuantity.equivalents.map(
|
|
2102
|
-
(eq) => toPlainUnit(eq)
|
|
2103
|
-
);
|
|
2104
|
-
}
|
|
2105
|
-
alternativeRefs.push({
|
|
2106
|
-
index: otherAlt.index,
|
|
2107
|
-
quantities: [altQty]
|
|
2108
|
-
});
|
|
2109
|
-
}
|
|
2110
|
-
}
|
|
2111
|
-
if (alternativeRefs.length === 0) {
|
|
2112
|
-
alternativeRefs = void 0;
|
|
2113
|
-
}
|
|
2148
|
+
signature = `group:${item.group}|${altIndices}|${resolvedUnit.type}`;
|
|
2149
|
+
} else if (altIndices) {
|
|
2150
|
+
const resolvedUnit = resolveUnit(
|
|
2151
|
+
alternative.itemQuantity.unit?.name
|
|
2152
|
+
);
|
|
2153
|
+
signature = `${altIndices}|${resolvedUnit.type}}`;
|
|
2154
|
+
} else {
|
|
2155
|
+
signature = null;
|
|
2114
2156
|
}
|
|
2115
2157
|
if (!ingredientGroups.has(alternative.index)) {
|
|
2116
2158
|
ingredientGroups.set(alternative.index, /* @__PURE__ */ new Map());
|
|
2117
2159
|
}
|
|
2118
|
-
const
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
alternativeQuantities: /* @__PURE__ */ new Map(),
|
|
2124
|
-
quantities: []
|
|
2160
|
+
const groupsForIng = ingredientGroups.get(alternative.index);
|
|
2161
|
+
if (!groupsForIng.has(signature)) {
|
|
2162
|
+
groupsForIng.set(signature, {
|
|
2163
|
+
quantities: [],
|
|
2164
|
+
alternativeQuantities: /* @__PURE__ */ new Map()
|
|
2125
2165
|
});
|
|
2126
2166
|
}
|
|
2127
|
-
const group =
|
|
2167
|
+
const group = groupsForIng.get(signature);
|
|
2128
2168
|
group.quantities.push(quantityEntry);
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
group.alternativeQuantities.set(ref.index, []);
|
|
2133
|
-
}
|
|
2134
|
-
if (ref.quantities && ref.quantities.length > 0) {
|
|
2135
|
-
for (const altQty of ref.quantities) {
|
|
2136
|
-
if (altQty.equivalents && altQty.equivalents.length > 0) {
|
|
2137
|
-
const entries = [
|
|
2138
|
-
toExtendedUnit({
|
|
2139
|
-
quantity: altQty.quantity,
|
|
2140
|
-
unit: altQty.unit
|
|
2141
|
-
}),
|
|
2142
|
-
...altQty.equivalents.map((eq) => toExtendedUnit(eq))
|
|
2143
|
-
];
|
|
2144
|
-
group.alternativeQuantities.get(ref.index).push({ type: "or", entries });
|
|
2145
|
-
} else {
|
|
2146
|
-
group.alternativeQuantities.get(ref.index).push(
|
|
2147
|
-
toExtendedUnit({
|
|
2148
|
-
quantity: altQty.quantity,
|
|
2149
|
-
unit: altQty.unit
|
|
2150
|
-
})
|
|
2151
|
-
);
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2169
|
+
for (const ref of alternativeRefs ?? []) {
|
|
2170
|
+
if (!group.alternativeQuantities.has(ref.index)) {
|
|
2171
|
+
group.alternativeQuantities.set(ref.index, []);
|
|
2155
2172
|
}
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
for (const [index, groupsForIngredient] of ingredientGroups) {
|
|
2161
|
-
const ingredient = this.ingredients[index];
|
|
2162
|
-
const quantityGroups = [];
|
|
2163
|
-
for (const [, group] of groupsForIngredient) {
|
|
2164
|
-
const summedGroupQuantity = addEquivalentsAndSimplify(
|
|
2165
|
-
...group.quantities
|
|
2166
|
-
);
|
|
2167
|
-
const groupQuantities = flattenPlainUnitGroup(summedGroupQuantity);
|
|
2168
|
-
let alternatives;
|
|
2169
|
-
if (group.alternativeQuantities.size > 0) {
|
|
2170
|
-
alternatives = [];
|
|
2171
|
-
for (const [altIndex, altQuantities] of group.alternativeQuantities) {
|
|
2172
|
-
const ref = { index: altIndex };
|
|
2173
|
-
if (altQuantities.length > 0) {
|
|
2174
|
-
const summedAltQuantity = addEquivalentsAndSimplify(
|
|
2175
|
-
...altQuantities
|
|
2176
|
-
);
|
|
2177
|
-
const flattenedAlt = flattenPlainUnitGroup(summedAltQuantity);
|
|
2178
|
-
ref.quantities = flattenedAlt.flatMap((item) => {
|
|
2179
|
-
if ("quantity" in item) {
|
|
2180
|
-
return [item];
|
|
2181
|
-
} else {
|
|
2182
|
-
return item.entries;
|
|
2183
|
-
}
|
|
2173
|
+
for (const altQty of ref.quantities ?? []) {
|
|
2174
|
+
const extended = toExtendedUnit({
|
|
2175
|
+
quantity: altQty.quantity,
|
|
2176
|
+
unit: altQty.unit
|
|
2184
2177
|
});
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
for (const gq of groupQuantities) {
|
|
2190
|
-
if ("type" in gq && gq.type === "and") {
|
|
2191
|
-
const andGroup = {
|
|
2192
|
-
type: "and",
|
|
2193
|
-
entries: gq.entries
|
|
2194
|
-
};
|
|
2195
|
-
if (gq.equivalents && gq.equivalents.length > 0) {
|
|
2196
|
-
andGroup.equivalents = gq.equivalents;
|
|
2197
|
-
}
|
|
2198
|
-
if (alternatives && alternatives.length > 0) {
|
|
2199
|
-
andGroup.alternatives = alternatives;
|
|
2200
|
-
}
|
|
2201
|
-
quantityGroups.push(andGroup);
|
|
2202
|
-
} else {
|
|
2203
|
-
const quantityGroup = gq;
|
|
2204
|
-
if (alternatives && alternatives.length > 0) {
|
|
2205
|
-
quantityGroup.alternatives = alternatives;
|
|
2206
|
-
}
|
|
2207
|
-
quantityGroups.push(quantityGroup);
|
|
2208
|
-
}
|
|
2209
|
-
}
|
|
2210
|
-
}
|
|
2211
|
-
if (quantityGroups.length > 0) {
|
|
2212
|
-
ingredient.quantities = quantityGroups;
|
|
2213
|
-
}
|
|
2214
|
-
}
|
|
2215
|
-
}
|
|
2216
|
-
/**
|
|
2217
|
-
* Calculates ingredient quantities based on the provided choices.
|
|
2218
|
-
* Returns a list of computed ingredients with their total quantities.
|
|
2219
|
-
*
|
|
2220
|
-
* @param choices - The recipe choices to apply when computing quantities.
|
|
2221
|
-
* If not provided, uses the default choices (first alternative for each item).
|
|
2222
|
-
* @returns An array of ComputedIngredient with quantityTotal calculated based on choices.
|
|
2223
|
-
*/
|
|
2224
|
-
calc_ingredient_quantities(choices) {
|
|
2225
|
-
const effectiveChoices = choices || {
|
|
2226
|
-
ingredientItems: new Map(
|
|
2227
|
-
Array.from(this.choices.ingredientItems.keys()).map((k) => [k, 0])
|
|
2228
|
-
),
|
|
2229
|
-
ingredientGroups: new Map(
|
|
2230
|
-
Array.from(this.choices.ingredientGroups.keys()).map((k) => [k, 0])
|
|
2231
|
-
)
|
|
2232
|
-
};
|
|
2233
|
-
const ingredientQuantities = /* @__PURE__ */ new Map();
|
|
2234
|
-
const selectedIngredientIndices = /* @__PURE__ */ new Set();
|
|
2235
|
-
for (const section of this.sections) {
|
|
2236
|
-
for (const step of section.content.filter(
|
|
2237
|
-
(item) => item.type === "step"
|
|
2238
|
-
)) {
|
|
2239
|
-
for (const item of step.items.filter(
|
|
2240
|
-
(item2) => item2.type === "ingredient"
|
|
2241
|
-
)) {
|
|
2242
|
-
for (let i2 = 0; i2 < item.alternatives.length; i2++) {
|
|
2243
|
-
const alternative = item.alternatives[i2];
|
|
2244
|
-
const isAlternativeChoiceItem = effectiveChoices.ingredientItems?.get(item.id) === i2;
|
|
2245
|
-
const alternativeChoiceGroupIdx = item.group ? effectiveChoices.ingredientGroups?.get(item.group) : void 0;
|
|
2246
|
-
const alternativeChoiceGroup = item.group ? this.choices.ingredientGroups.get(item.group) : void 0;
|
|
2247
|
-
const isAlternativeChoiceGroup = alternativeChoiceGroup && alternativeChoiceGroupIdx !== void 0 ? alternativeChoiceGroup[alternativeChoiceGroupIdx]?.itemId === item.id : false;
|
|
2248
|
-
const isSelected = !("group" in item) && (item.alternatives.length === 1 || isAlternativeChoiceItem) || isAlternativeChoiceGroup;
|
|
2249
|
-
if (isSelected) {
|
|
2250
|
-
selectedIngredientIndices.add(alternative.index);
|
|
2251
|
-
if (alternative.itemQuantity) {
|
|
2252
|
-
const allQuantities = [
|
|
2253
|
-
{
|
|
2254
|
-
quantity: alternative.itemQuantity.quantity,
|
|
2255
|
-
unit: alternative.itemQuantity.unit
|
|
2256
|
-
}
|
|
2178
|
+
if (altQty.equivalents?.length) {
|
|
2179
|
+
const eqEntries = [
|
|
2180
|
+
extended,
|
|
2181
|
+
...altQty.equivalents.map((eq) => toExtendedUnit(eq))
|
|
2257
2182
|
];
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
const equivalents = allQuantities.length === 1 ? allQuantities[0] : {
|
|
2262
|
-
type: "or",
|
|
2263
|
-
entries: allQuantities
|
|
2264
|
-
};
|
|
2265
|
-
ingredientQuantities.set(alternative.index, [
|
|
2266
|
-
...ingredientQuantities.get(alternative.index) || [],
|
|
2267
|
-
equivalents
|
|
2268
|
-
]);
|
|
2183
|
+
group.alternativeQuantities.get(ref.index).push({ or: eqEntries });
|
|
2184
|
+
} else {
|
|
2185
|
+
group.alternativeQuantities.get(ref.index).push(extended);
|
|
2269
2186
|
}
|
|
2270
2187
|
}
|
|
2271
2188
|
}
|
|
2272
2189
|
}
|
|
2273
2190
|
}
|
|
2274
2191
|
}
|
|
2275
|
-
const
|
|
2192
|
+
const result = [];
|
|
2276
2193
|
for (let index = 0; index < this.ingredients.length; index++) {
|
|
2277
|
-
if (!
|
|
2278
|
-
const
|
|
2279
|
-
const
|
|
2280
|
-
name:
|
|
2194
|
+
if (!referencedIndices.has(index)) continue;
|
|
2195
|
+
const orig = this.ingredients[index];
|
|
2196
|
+
const ing = {
|
|
2197
|
+
name: orig.name,
|
|
2198
|
+
...orig.preparation && { preparation: orig.preparation },
|
|
2199
|
+
...orig.flags && { flags: orig.flags },
|
|
2200
|
+
...orig.extras && { extras: orig.extras }
|
|
2281
2201
|
};
|
|
2282
|
-
if (
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2202
|
+
if (selectedIndices.has(index)) {
|
|
2203
|
+
ing.usedAsPrimary = true;
|
|
2204
|
+
const groupsForIng = ingredientGroups.get(index);
|
|
2205
|
+
if (groupsForIng) {
|
|
2206
|
+
const quantityGroups = [];
|
|
2207
|
+
for (const [, group] of groupsForIng) {
|
|
2208
|
+
const summed = addEquivalentsAndSimplify(...group.quantities);
|
|
2209
|
+
const flattened = flattenPlainUnitGroup(summed);
|
|
2210
|
+
const alternatives = group.alternativeQuantities.size > 0 ? [...group.alternativeQuantities].map(([altIdx, altQtys]) => ({
|
|
2211
|
+
index: altIdx,
|
|
2212
|
+
...altQtys.length > 0 && {
|
|
2213
|
+
quantities: flattenPlainUnitGroup(
|
|
2214
|
+
addEquivalentsAndSimplify(...altQtys)
|
|
2215
|
+
).flatMap(
|
|
2216
|
+
/* v8 ignore next -- item.and branch requires complex nested AND-with-equivalents structure */
|
|
2217
|
+
(item) => "quantity" in item ? [item] : item.and
|
|
2218
|
+
)
|
|
2219
|
+
}
|
|
2220
|
+
})) : void 0;
|
|
2221
|
+
for (const gq of flattened) {
|
|
2222
|
+
if ("and" in gq) {
|
|
2223
|
+
quantityGroups.push({
|
|
2224
|
+
and: gq.and,
|
|
2225
|
+
...gq.equivalents?.length && {
|
|
2226
|
+
equivalents: gq.equivalents
|
|
2227
|
+
},
|
|
2228
|
+
...alternatives?.length && { alternatives }
|
|
2229
|
+
});
|
|
2230
|
+
} else {
|
|
2231
|
+
quantityGroups.push({
|
|
2232
|
+
...gq,
|
|
2233
|
+
...alternatives?.length && { alternatives }
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
if (quantityGroups.length > 0) {
|
|
2239
|
+
ing.quantities = quantityGroups;
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2294
2242
|
}
|
|
2295
|
-
|
|
2243
|
+
result.push(ing);
|
|
2296
2244
|
}
|
|
2297
|
-
return
|
|
2245
|
+
return result;
|
|
2298
2246
|
}
|
|
2299
2247
|
/**
|
|
2300
2248
|
* Parses a recipe from a string.
|
|
@@ -2343,10 +2291,6 @@ var _Recipe = class _Recipe {
|
|
|
2343
2291
|
}
|
|
2344
2292
|
if (blankLineBefore && line.startsWith(">")) {
|
|
2345
2293
|
flushPendingItems(section, items);
|
|
2346
|
-
flushPendingNote(
|
|
2347
|
-
section,
|
|
2348
|
-
noteText ? this._parseNoteText(noteText) : []
|
|
2349
|
-
);
|
|
2350
2294
|
noteText = line.substring(1).trim();
|
|
2351
2295
|
inNote = true;
|
|
2352
2296
|
blankLineBefore = false;
|
|
@@ -2361,8 +2305,6 @@ var _Recipe = class _Recipe {
|
|
|
2361
2305
|
blankLineBefore = false;
|
|
2362
2306
|
continue;
|
|
2363
2307
|
}
|
|
2364
|
-
flushPendingNote(section, noteText ? this._parseNoteText(noteText) : []);
|
|
2365
|
-
noteText = "";
|
|
2366
2308
|
let cursor = 0;
|
|
2367
2309
|
for (const match of line.matchAll(tokensRegex)) {
|
|
2368
2310
|
const idx = match.index;
|
|
@@ -2627,7 +2569,7 @@ var ShoppingList = class {
|
|
|
2627
2569
|
this.ingredients = [];
|
|
2628
2570
|
const addIngredientQuantity = (name, quantityTotal) => {
|
|
2629
2571
|
const quantityTotalExtended = extendAllUnits(quantityTotal);
|
|
2630
|
-
const newQuantities = isAndGroup(quantityTotalExtended) ? quantityTotalExtended.
|
|
2572
|
+
const newQuantities = isAndGroup(quantityTotalExtended) ? quantityTotalExtended.and : [quantityTotalExtended];
|
|
2631
2573
|
const existing = this.ingredients.find((i2) => i2.name === name);
|
|
2632
2574
|
if (existing) {
|
|
2633
2575
|
if (!existing.quantityTotal) {
|
|
@@ -2638,7 +2580,7 @@ var ShoppingList = class {
|
|
|
2638
2580
|
const existingQuantityTotalExtended = extendAllUnits(
|
|
2639
2581
|
existing.quantityTotal
|
|
2640
2582
|
);
|
|
2641
|
-
const existingQuantities = isAndGroup(existingQuantityTotalExtended) ? existingQuantityTotalExtended.
|
|
2583
|
+
const existingQuantities = isAndGroup(existingQuantityTotalExtended) ? existingQuantityTotalExtended.and : [existingQuantityTotalExtended];
|
|
2642
2584
|
existing.quantityTotal = addEquivalentsAndSimplify(
|
|
2643
2585
|
...existingQuantities,
|
|
2644
2586
|
...newQuantities
|
|
@@ -2660,15 +2602,43 @@ var ShoppingList = class {
|
|
|
2660
2602
|
} else {
|
|
2661
2603
|
scaledRecipe = addedRecipe.recipe.scaleTo(addedRecipe.servings);
|
|
2662
2604
|
}
|
|
2663
|
-
const
|
|
2664
|
-
addedRecipe.choices
|
|
2665
|
-
);
|
|
2666
|
-
for (const ingredient of
|
|
2605
|
+
const ingredients = scaledRecipe.getIngredientQuantities({
|
|
2606
|
+
choices: addedRecipe.choices
|
|
2607
|
+
});
|
|
2608
|
+
for (const ingredient of ingredients) {
|
|
2667
2609
|
if (ingredient.flags && ingredient.flags.includes("hidden")) {
|
|
2668
2610
|
continue;
|
|
2669
2611
|
}
|
|
2670
|
-
if (ingredient.
|
|
2671
|
-
|
|
2612
|
+
if (!ingredient.usedAsPrimary) {
|
|
2613
|
+
continue;
|
|
2614
|
+
}
|
|
2615
|
+
if (ingredient.quantities && ingredient.quantities.length > 0) {
|
|
2616
|
+
const allQuantities = [];
|
|
2617
|
+
for (const qGroup of ingredient.quantities) {
|
|
2618
|
+
if ("and" in qGroup) {
|
|
2619
|
+
for (const qty of qGroup.and) {
|
|
2620
|
+
allQuantities.push(qty);
|
|
2621
|
+
}
|
|
2622
|
+
} else {
|
|
2623
|
+
const plainQty = {
|
|
2624
|
+
quantity: qGroup.quantity
|
|
2625
|
+
};
|
|
2626
|
+
if (qGroup.unit) plainQty.unit = qGroup.unit;
|
|
2627
|
+
if (qGroup.equivalents) plainQty.equivalents = qGroup.equivalents;
|
|
2628
|
+
allQuantities.push(plainQty);
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
if (allQuantities.length === 1) {
|
|
2632
|
+
addIngredientQuantity(ingredient.name, allQuantities[0]);
|
|
2633
|
+
} else {
|
|
2634
|
+
const extendedQuantities = allQuantities.map(
|
|
2635
|
+
(q) => extendAllUnits(q)
|
|
2636
|
+
);
|
|
2637
|
+
const totalQuantity = addEquivalentsAndSimplify(
|
|
2638
|
+
...extendedQuantities
|
|
2639
|
+
);
|
|
2640
|
+
addIngredientQuantity(ingredient.name, totalQuantity);
|
|
2641
|
+
}
|
|
2672
2642
|
} else if (!this.ingredients.some((i2) => i2.name === ingredient.name)) {
|
|
2673
2643
|
this.ingredients.push({ name: ingredient.name });
|
|
2674
2644
|
}
|
|
@@ -2680,8 +2650,16 @@ var ShoppingList = class {
|
|
|
2680
2650
|
* recalculates the quantities and recategorize the ingredients.
|
|
2681
2651
|
* @param recipe - The recipe to add.
|
|
2682
2652
|
* @param options - Options for adding the recipe.
|
|
2653
|
+
* @throws Error if the recipe has alternatives without corresponding choices.
|
|
2683
2654
|
*/
|
|
2684
2655
|
add_recipe(recipe, options = {}) {
|
|
2656
|
+
const errorMessage = this.getUnresolvedAlternativesError(
|
|
2657
|
+
recipe,
|
|
2658
|
+
options.choices
|
|
2659
|
+
);
|
|
2660
|
+
if (errorMessage) {
|
|
2661
|
+
throw new Error(errorMessage);
|
|
2662
|
+
}
|
|
2685
2663
|
if (!options.scaling) {
|
|
2686
2664
|
this.recipes.push({
|
|
2687
2665
|
recipe,
|
|
@@ -2706,6 +2684,41 @@ var ShoppingList = class {
|
|
|
2706
2684
|
this.calculate_ingredients();
|
|
2707
2685
|
this.categorize();
|
|
2708
2686
|
}
|
|
2687
|
+
/**
|
|
2688
|
+
* Checks if a recipe has unresolved alternatives (alternatives without provided choices).
|
|
2689
|
+
* @param recipe - The recipe to check.
|
|
2690
|
+
* @param choices - The choices provided for the recipe.
|
|
2691
|
+
* @returns An error message if there are unresolved alternatives, undefined otherwise.
|
|
2692
|
+
*/
|
|
2693
|
+
getUnresolvedAlternativesError(recipe, choices) {
|
|
2694
|
+
const missingItems = [];
|
|
2695
|
+
const missingGroups = [];
|
|
2696
|
+
for (const itemId of recipe.choices.ingredientItems.keys()) {
|
|
2697
|
+
if (!choices?.ingredientItems?.has(itemId)) {
|
|
2698
|
+
missingItems.push(itemId);
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
for (const groupId of recipe.choices.ingredientGroups.keys()) {
|
|
2702
|
+
if (!choices?.ingredientGroups?.has(groupId)) {
|
|
2703
|
+
missingGroups.push(groupId);
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
if (missingItems.length === 0 && missingGroups.length === 0) {
|
|
2707
|
+
return void 0;
|
|
2708
|
+
}
|
|
2709
|
+
const parts = [];
|
|
2710
|
+
if (missingItems.length > 0) {
|
|
2711
|
+
parts.push(
|
|
2712
|
+
`ingredientItems: [${missingItems.map((i2) => `'${i2}'`).join(", ")}]`
|
|
2713
|
+
);
|
|
2714
|
+
}
|
|
2715
|
+
if (missingGroups.length > 0) {
|
|
2716
|
+
parts.push(
|
|
2717
|
+
`ingredientGroups: [${missingGroups.map((g) => `'${g}'`).join(", ")}]`
|
|
2718
|
+
);
|
|
2719
|
+
}
|
|
2720
|
+
return `Recipe has unresolved alternatives. Missing choices for: ${parts.join(", ")}`;
|
|
2721
|
+
}
|
|
2709
2722
|
/**
|
|
2710
2723
|
* Removes a recipe from the shopping list, then automatically
|
|
2711
2724
|
* recalculates the quantities and recategorize the ingredients.s
|
|
@@ -2894,7 +2907,7 @@ var ShoppingCart = class {
|
|
|
2894
2907
|
const normalizedQuantityTotal = normalizeAllUnits(ingredient.quantityTotal);
|
|
2895
2908
|
function getOptimumMatchForQuantityParts(normalizedQuantities, normalizedOptions2, selection = []) {
|
|
2896
2909
|
if (isAndGroup(normalizedQuantities)) {
|
|
2897
|
-
for (const q of normalizedQuantities.
|
|
2910
|
+
for (const q of normalizedQuantities.and) {
|
|
2898
2911
|
const result = getOptimumMatchForQuantityParts(
|
|
2899
2912
|
q,
|
|
2900
2913
|
normalizedOptions2,
|
|
@@ -2903,7 +2916,7 @@ var ShoppingCart = class {
|
|
|
2903
2916
|
selection.push(...result);
|
|
2904
2917
|
}
|
|
2905
2918
|
} else {
|
|
2906
|
-
const alternativeUnitsOfQuantity = isOrGroup(normalizedQuantities) ? normalizedQuantities.
|
|
2919
|
+
const alternativeUnitsOfQuantity = isOrGroup(normalizedQuantities) ? normalizedQuantities.or : [normalizedQuantities];
|
|
2907
2920
|
const solutions = [];
|
|
2908
2921
|
const errors = /* @__PURE__ */ new Set();
|
|
2909
2922
|
for (const alternative of alternativeUnitsOfQuantity) {
|
|
@@ -3023,6 +3036,21 @@ var ShoppingCart = class {
|
|
|
3023
3036
|
return this.summary;
|
|
3024
3037
|
}
|
|
3025
3038
|
};
|
|
3039
|
+
|
|
3040
|
+
// src/utils/render_helpers.ts
|
|
3041
|
+
function isAlternativeSelected(recipe, choices, item, alternativeIndex) {
|
|
3042
|
+
if (item.group) {
|
|
3043
|
+
const selectedIndex2 = choices?.ingredientGroups?.get(item.group);
|
|
3044
|
+
const groupAlternatives = recipe.choices.ingredientGroups.get(item.group);
|
|
3045
|
+
if (groupAlternatives && selectedIndex2 && selectedIndex2 < groupAlternatives.length) {
|
|
3046
|
+
const selectedItemId = groupAlternatives[selectedIndex2]?.itemId;
|
|
3047
|
+
return selectedItemId === item.id;
|
|
3048
|
+
}
|
|
3049
|
+
return false;
|
|
3050
|
+
}
|
|
3051
|
+
const selectedIndex = choices?.ingredientItems?.get(item.id);
|
|
3052
|
+
return alternativeIndex === selectedIndex;
|
|
3053
|
+
}
|
|
3026
3054
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3027
3055
|
0 && (module.exports = {
|
|
3028
3056
|
CategoryConfig,
|
|
@@ -3032,9 +3060,12 @@ var ShoppingCart = class {
|
|
|
3032
3060
|
Recipe,
|
|
3033
3061
|
Section,
|
|
3034
3062
|
ShoppingCart,
|
|
3035
|
-
ShoppingList
|
|
3063
|
+
ShoppingList,
|
|
3064
|
+
isAlternativeSelected
|
|
3036
3065
|
});
|
|
3037
3066
|
/* v8 ignore else -- @preserve */
|
|
3067
|
+
// v8 ignore else -- @preserve
|
|
3038
3068
|
/* v8 ignore else -- expliciting error type -- @preserve */
|
|
3069
|
+
// v8 ignore if -- @preserve
|
|
3039
3070
|
/* v8 ignore if -- @preserve */
|
|
3040
3071
|
//# sourceMappingURL=index.cjs.map
|