@tmlmt/cooklang-parser 3.0.0-alpha.17 → 3.0.0-alpha.20
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 +453 -181
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +185 -66
- package/dist/index.d.ts +185 -66
- package/dist/index.js +450 -179
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -276,8 +276,8 @@ var nonWordChar = "\\s@#~\\[\\]{(,;:!?";
|
|
|
276
276
|
var nonWordCharStrict = "\\s@#~\\[\\]{(,;:!?|";
|
|
277
277
|
var ingredientWithAlternativeRegex = d().literal("@").startNamedGroup("ingredientModifiers").anyOf("@\\-&?").zeroOrMore().endGroup().optional().startNamedGroup("ingredientRecipeAnchor").literal("./").endGroup().optional().startGroup().startGroup().startNamedGroup("mIngredientName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\}|\\([^)]*\\))").endGroup().or().startNamedGroup("sIngredientName").notAnyOf(nonWordChar).zeroOrMore().notAnyOf("\\." + nonWordChar).endGroup().endGroup().startGroup().literal("{").startNamedGroup("ingredientQuantityModifier").literal("=").exactly(1).endGroup().optional().startNamedGroup("ingredientQuantity").startGroup().notAnyOf("}|%").oneOrMore().endGroup().optional().startGroup().literal("%").notAnyOf("|}").oneOrMore().lazy().endGroup().optional().startGroup().literal("|").notAnyOf("}").oneOrMore().lazy().endGroup().zeroOrMore().endGroup().literal("}").endGroup().optional().startGroup().literal("(").startNamedGroup("ingredientPreparation").notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().startGroup().literal("[").startNamedGroup("ingredientNote").notAnyOf("\\]").oneOrMore().lazy().endGroup().literal("]").endGroup().optional().startNamedGroup("ingredientAlternative").startGroup().literal("|").startGroup().anyOf("@\\-&?").zeroOrMore().endGroup().optional().startGroup().literal("./").endGroup().optional().startGroup().startGroup().startGroup().notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\}|\\([^)]*\\))").endGroup().or().startGroup().notAnyOf(nonWordChar).oneOrMore().endGroup().endGroup().startGroup().literal("{").startGroup().literal("=").exactly(1).endGroup().optional().startGroup().notAnyOf("}%").oneOrMore().endGroup().optional().startGroup().literal("%").startGroup().notAnyOf("}").oneOrMore().lazy().endGroup().endGroup().optional().literal("}").endGroup().optional().startGroup().literal("(").startGroup().notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().startGroup().literal("[").startGroup().notAnyOf("\\]").oneOrMore().lazy().endGroup().literal("]").endGroup().optional().endGroup().zeroOrMore().endGroup().toRegExp();
|
|
278
278
|
var inlineIngredientAlternativesRegex = new RegExp("\\|" + ingredientWithAlternativeRegex.source.slice(1));
|
|
279
|
-
var quantityAlternativeRegex = d().startNamedGroup("quantity").notAnyOf("}|%").oneOrMore().endGroup().optional().startGroup().literal("%").startNamedGroup("unit").notAnyOf("|}").oneOrMore().endGroup().endGroup().optional().startGroup().literal("|").startNamedGroup("alternative").startGroup().notAnyOf("}").oneOrMore().endGroup().zeroOrMore().endGroup().endGroup().optional().toRegExp();
|
|
280
|
-
var ingredientWithGroupKeyRegex = d().literal("@|").startNamedGroup("gIngredientGroupKey").notAnyOf(nonWordCharStrict + "/").oneOrMore().endGroup().startGroup().literal("/").startNamedGroup("gIngredientSubgroupKey").notAnyOf(nonWordCharStrict).oneOrMore().endGroup().endGroup().optional().literal("|").startNamedGroup("gIngredientModifiers").anyOf("@\\-&?").zeroOrMore().endGroup().optional().startNamedGroup("gIngredientRecipeAnchor").literal("./").endGroup().optional().startGroup().startGroup().startNamedGroup("gmIngredientName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\}|\\([^)]*\\))").endGroup().or().startNamedGroup("gsIngredientName").notAnyOf(nonWordChar).zeroOrMore().notAnyOf("\\." + nonWordChar).endGroup().endGroup().startGroup().literal("{").startNamedGroup("gIngredientQuantityModifier").literal("=").exactly(1).endGroup().optional().startNamedGroup("gIngredientQuantity").startGroup().notAnyOf("}|%").oneOrMore().endGroup().optional().startGroup().literal("%").notAnyOf("|}").oneOrMore().lazy().endGroup().optional().startGroup().literal("|").notAnyOf("}").oneOrMore().lazy().endGroup().zeroOrMore().endGroup().literal("}").endGroup().optional().startGroup().literal("(").startNamedGroup("gIngredientPreparation").notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().toRegExp();
|
|
279
|
+
var quantityAlternativeRegex = d().startNamedGroup("quantity").notAnyOf("{}|%").oneOrMore().endGroup().optional().startGroup().literal("%").startNamedGroup("unit").notAnyOf("|}").oneOrMore().endGroup().endGroup().optional().startGroup().literal("|").startNamedGroup("alternative").startGroup().notAnyOf("}").oneOrMore().endGroup().zeroOrMore().endGroup().endGroup().optional().toRegExp();
|
|
280
|
+
var ingredientWithGroupKeyRegex = d().literal("@|").startNamedGroup("gIngredientGroupKey").notAnyOf(nonWordCharStrict + "/").oneOrMore().endGroup().startGroup().literal("/").startNamedGroup("gIngredientSubgroupKey").notAnyOf(nonWordCharStrict).oneOrMore().endGroup().endGroup().optional().literal("|").startNamedGroup("gIngredientModifiers").anyOf("@\\-&?").zeroOrMore().endGroup().optional().startNamedGroup("gIngredientRecipeAnchor").literal("./").endGroup().optional().startGroup().startGroup().startNamedGroup("gmIngredientName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\}|\\([^)]*\\))").endGroup().or().startNamedGroup("gsIngredientName").notAnyOf(nonWordChar).zeroOrMore().notAnyOf("\\." + nonWordChar).endGroup().endGroup().startGroup().literal("{").startNamedGroup("gIngredientQuantityModifier").literal("=").exactly(1).endGroup().optional().startNamedGroup("gIngredientQuantity").startGroup().notAnyOf("}|%").oneOrMore().endGroup().optional().startGroup().literal("%").notAnyOf("|}").oneOrMore().lazy().endGroup().optional().startGroup().literal("|").notAnyOf("}").oneOrMore().lazy().endGroup().zeroOrMore().endGroup().literal("}").endGroup().optional().startGroup().literal("(").startNamedGroup("gIngredientPreparation").notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().startGroup().literal("[").startNamedGroup("gIngredientNote").notAnyOf("\\]").oneOrMore().lazy().endGroup().literal("]").endGroup().optional().toRegExp();
|
|
281
281
|
var ingredientAliasRegex = d().startAnchor().startNamedGroup("ingredientListName").notAnyOf("|").oneOrMore().endGroup().literal("|").startNamedGroup("ingredientDisplayName").notAnyOf("|").oneOrMore().endGroup().endAnchor().toRegExp();
|
|
282
282
|
var cookwareRegex = d().literal("#").startNamedGroup("cookwareModifiers").anyOf("\\-&?").zeroOrMore().endGroup().startGroup().startGroup().startNamedGroup("mCookwareName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\})").endGroup().or().startNamedGroup("sCookwareName").notAnyOf(nonWordChar).zeroOrMore().notAnyOf("\\." + nonWordChar).endGroup().endGroup().startGroup().literal("{").startNamedGroup("cookwareQuantity").anyCharacter().zeroOrMore().lazy().endGroup().literal("}").endGroup().optional().toRegExp();
|
|
283
283
|
var timerRegex = d().literal("~").startNamedGroup("timerName").anyCharacter().zeroOrMore().lazy().endGroup().literal("{").startNamedGroup("timerQuantity").anyCharacter().oneOrMore().lazy().endGroup().startGroup().literal("%").startNamedGroup("timerUnit").anyCharacter().oneOrMore().lazy().endGroup().endGroup().optional().literal("}").toRegExp();
|
|
@@ -292,11 +292,18 @@ var tokensRegex = new RegExp(
|
|
|
292
292
|
].map((r2) => r2.source).join("|"),
|
|
293
293
|
"gu"
|
|
294
294
|
);
|
|
295
|
-
var
|
|
296
|
-
var
|
|
297
|
-
var
|
|
298
|
-
|
|
299
|
-
|
|
295
|
+
var yieldPrefixPart = d().startAnchor().literal("yield").literal(":").anyOf(" ").zeroOrMore().startNamedGroup("servingsPrefix").nonWhitespace().startGroup().anyCharacter().zeroOrMore().lazy().nonWhitespace().endGroup().optional().endGroup().optional().anyOf(" ").zeroOrMore().toRegExp();
|
|
296
|
+
var yieldSuffixPart = d().anyOf(" ").zeroOrMore().startNamedGroup("servingsSuffix").nonWhitespace().startGroup().anyCharacter().zeroOrMore().nonWhitespace().endGroup().optional().endGroup().optional().anyOf(" ").zeroOrMore().endAnchor().toRegExp();
|
|
297
|
+
var yieldMetaValueWithUnitRegex = new RegExp(
|
|
298
|
+
yieldPrefixPart.source + arbitraryScalableRegex.source + yieldSuffixPart.source,
|
|
299
|
+
"m"
|
|
300
|
+
);
|
|
301
|
+
var yieldMetaValueAsQuantityRegex = d().startAnchor().literal("yield:").anyOf(" ").zeroOrMore().startNamedGroup("quantity").notAnyOf("{}|%\\n\\r").oneOrMore().endGroup().optional().startGroup().literal("%").startNamedGroup("unit").notAnyOf("\\n\\r|}").oneOrMore().endGroup().endGroup().optional().anyOf(" ").zeroOrMore().endAnchor().toRegExp();
|
|
302
|
+
var yieldMetaValueRegex = new RegExp(
|
|
303
|
+
[
|
|
304
|
+
yieldMetaValueWithUnitRegex.source,
|
|
305
|
+
yieldMetaValueAsQuantityRegex.source
|
|
306
|
+
].join("|"),
|
|
300
307
|
"m"
|
|
301
308
|
);
|
|
302
309
|
var commentRegex = d().literal("--").anyCharacter().zeroOrMore().global().toRegExp();
|
|
@@ -305,6 +312,7 @@ var shoppingListRegex = d().literal("[").startNamedGroup("name").anyCharacter().
|
|
|
305
312
|
var rangeRegex = d().startAnchor().digit().oneOrMore().startGroup().anyOf(".,/").exactly(1).digit().oneOrMore().endGroup().optional().literal("-").digit().oneOrMore().startGroup().anyOf(".,/").exactly(1).digit().oneOrMore().endGroup().optional().endAnchor().toRegExp();
|
|
306
313
|
var numberLikeRegex = d().startAnchor().digit().oneOrMore().startGroup().anyOf(".,/").exactly(1).digit().oneOrMore().endGroup().optional().endAnchor().toRegExp();
|
|
307
314
|
var floatRegex = d().startAnchor().digit().oneOrMore().startGroup().anyOf(".").exactly(1).digit().oneOrMore().endGroup().optional().endAnchor().toRegExp();
|
|
315
|
+
var variantTagRegex = d().startAnchor().literal("[").startNamedGroup("variantOptionalPrefix").literal("?").endGroup().optional().startNamedGroup("variantNames").notAnyOf("\\]").oneOrMore().endGroup().optional().literal("]").whitespace().zeroOrMore().toRegExp();
|
|
308
316
|
var mdEscaped = d().literal("\\").startCaptureGroup().anyOf("*_`").endGroup();
|
|
309
317
|
var mdInlineCode = d().literal("`").startCaptureGroup().notAnyOf("`").oneOrMore().lazy().endGroup().literal("`");
|
|
310
318
|
var mdLink = d().literal("[").startCaptureGroup().notAnyOf("\\]").oneOrMore().lazy().endGroup().literal("](").startCaptureGroup().notAnyOf(")").oneOrMore().lazy().endGroup().literal(")");
|
|
@@ -880,7 +888,7 @@ var NoProductMatchError = class extends Error {
|
|
|
880
888
|
constructor(item_name, code) {
|
|
881
889
|
const messageMap = {
|
|
882
890
|
incompatibleUnits: `The units of the products in the catalogue are incompatible with ingredient ${item_name} in the shopping list.`,
|
|
883
|
-
noProduct:
|
|
891
|
+
noProduct: `No product was found linked to ingredient name ${item_name} in the shopping list`,
|
|
884
892
|
textValue: `Ingredient ${item_name} has a text value as quantity and can therefore not be matched with any product in the catalogue.`,
|
|
885
893
|
noQuantity: `Ingredient ${item_name} has no quantity and can therefore not be matched with any product in the catalogue.`,
|
|
886
894
|
textValue_incompatibleUnits: `Multiple alternative quantities were provided for ingredient ${item_name} in the shopping list but they were either text values or no product in catalog were found to have compatible units`
|
|
@@ -1359,9 +1367,12 @@ function flushPendingNote(section, noteItems) {
|
|
|
1359
1367
|
}
|
|
1360
1368
|
return noteItems;
|
|
1361
1369
|
}
|
|
1362
|
-
function flushPendingItems(section, items) {
|
|
1370
|
+
function flushPendingItems(section, items, stepVariants, stepOptional) {
|
|
1363
1371
|
if (items.length > 0) {
|
|
1364
|
-
|
|
1372
|
+
const step = { type: "step", items: [...items] };
|
|
1373
|
+
if (stepVariants) step.variants = stepVariants;
|
|
1374
|
+
if (stepOptional) step.optional = true;
|
|
1375
|
+
section.content.push(step);
|
|
1365
1376
|
items.length = 0;
|
|
1366
1377
|
return true;
|
|
1367
1378
|
}
|
|
@@ -1480,7 +1491,7 @@ function stringifyFixedValue(quantity) {
|
|
|
1480
1491
|
return String(quantity.value.decimal);
|
|
1481
1492
|
else return quantity.value.text;
|
|
1482
1493
|
}
|
|
1483
|
-
function
|
|
1494
|
+
function parseQuantityValue(input_str) {
|
|
1484
1495
|
const clean_str = String(input_str).trim();
|
|
1485
1496
|
if (rangeRegex.test(clean_str)) {
|
|
1486
1497
|
const range_parts = clean_str.split("-");
|
|
@@ -1494,12 +1505,12 @@ function parseQuantityWithUnit(input) {
|
|
|
1494
1505
|
const trimmed = input.trim();
|
|
1495
1506
|
const separatorIndex = trimmed.indexOf("%");
|
|
1496
1507
|
if (separatorIndex === -1) {
|
|
1497
|
-
return { value:
|
|
1508
|
+
return { value: parseQuantityValue(trimmed) };
|
|
1498
1509
|
}
|
|
1499
1510
|
const valuePart = trimmed.slice(0, separatorIndex).trim();
|
|
1500
1511
|
const unitPart = trimmed.slice(separatorIndex + 1).trim();
|
|
1501
1512
|
return {
|
|
1502
|
-
value:
|
|
1513
|
+
value: parseQuantityValue(valuePart),
|
|
1503
1514
|
unit: unitPart || void 0
|
|
1504
1515
|
};
|
|
1505
1516
|
}
|
|
@@ -1689,7 +1700,7 @@ function parseArbitraryQuantity(raw) {
|
|
|
1689
1700
|
"Arbitrary quantities must have a numerical value"
|
|
1690
1701
|
);
|
|
1691
1702
|
}
|
|
1692
|
-
const value =
|
|
1703
|
+
const value = parseQuantityValue(quantityMatch.groups.quantity);
|
|
1693
1704
|
const unit = quantityMatch.groups.unit;
|
|
1694
1705
|
if (!value || value.type === "fixed" && value.value.type === "text") {
|
|
1695
1706
|
throw new InvalidQuantityFormat(
|
|
@@ -1703,40 +1714,40 @@ function parseArbitraryQuantity(raw) {
|
|
|
1703
1714
|
if (unit) arbitrary.unit = unit;
|
|
1704
1715
|
return arbitrary;
|
|
1705
1716
|
}
|
|
1706
|
-
function
|
|
1707
|
-
const
|
|
1708
|
-
if (
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1717
|
+
function parseServingsMetaVar(content, varName) {
|
|
1718
|
+
const raw = parseSimpleMetaVar(content, varName);
|
|
1719
|
+
if (raw === void 0) return void 0;
|
|
1720
|
+
const num = Number(raw);
|
|
1721
|
+
if (isNaN(num)) {
|
|
1722
|
+
return { numericValue: 1, rawValue: raw };
|
|
1723
|
+
}
|
|
1724
|
+
return { numericValue: num, rawValue: num };
|
|
1725
|
+
}
|
|
1726
|
+
function parseYieldMetaVar(content) {
|
|
1727
|
+
const match = content.match(yieldMetaValueRegex);
|
|
1728
|
+
if (!match) return void 0;
|
|
1729
|
+
if (match.groups?.arbitraryQuantity) {
|
|
1730
|
+
const parsed = parseArbitraryQuantity(match.groups.arbitraryQuantity);
|
|
1731
|
+
const result = {
|
|
1713
1732
|
quantity: parsed.quantity
|
|
1714
1733
|
};
|
|
1715
|
-
if (parsed.unit)
|
|
1716
|
-
if (
|
|
1717
|
-
|
|
1734
|
+
if (parsed.unit) result.unit = parsed.unit;
|
|
1735
|
+
if (match.groups.servingsPrefix) {
|
|
1736
|
+
result.textBefore = match.groups.servingsPrefix;
|
|
1718
1737
|
}
|
|
1719
|
-
if (
|
|
1720
|
-
|
|
1738
|
+
if (match.groups.servingsSuffix) {
|
|
1739
|
+
result.textAfter = match.groups.servingsSuffix;
|
|
1721
1740
|
}
|
|
1722
|
-
return
|
|
1723
|
-
}
|
|
1724
|
-
const varMatch = content.match(scalingSimpleMetaValueRegex(varName));
|
|
1725
|
-
if (!varMatch) return void 0;
|
|
1726
|
-
if (isNaN(Number(varMatch[2]?.trim()))) {
|
|
1727
|
-
throw new Error("Scaling variables should be numbers");
|
|
1741
|
+
return result;
|
|
1728
1742
|
}
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
};
|
|
1736
|
-
if (varMatch[3]) {
|
|
1737
|
-
result.text = `${varMatch[3].trim()}`;
|
|
1743
|
+
if (match.groups?.quantity) {
|
|
1744
|
+
const result = {
|
|
1745
|
+
quantity: parseQuantityValue(match.groups.quantity)
|
|
1746
|
+
};
|
|
1747
|
+
if (match.groups.unit) result.unit = match.groups.unit;
|
|
1748
|
+
return result;
|
|
1738
1749
|
}
|
|
1739
|
-
return
|
|
1750
|
+
return void 0;
|
|
1740
1751
|
}
|
|
1741
1752
|
function parseListMetaVar(content, varName) {
|
|
1742
1753
|
const listMatch = content.match(
|
|
@@ -1854,12 +1865,12 @@ function parseAnyMetaVar(content, varName) {
|
|
|
1854
1865
|
if (simple) return parseMetadataValue(simple);
|
|
1855
1866
|
return void 0;
|
|
1856
1867
|
}
|
|
1857
|
-
function
|
|
1868
|
+
function getNumericValueFromYield(v) {
|
|
1858
1869
|
if (v.quantity.type === "fixed" && v.quantity.value.type !== "text") {
|
|
1859
1870
|
return getNumericValue(v.quantity.value);
|
|
1860
1871
|
}
|
|
1861
1872
|
if (v.quantity.type === "range") return getNumericValue(v.quantity.min);
|
|
1862
|
-
return
|
|
1873
|
+
return 1;
|
|
1863
1874
|
}
|
|
1864
1875
|
function extractMetadata(content) {
|
|
1865
1876
|
const metadata = {};
|
|
@@ -1905,7 +1916,8 @@ function extractMetadata(content) {
|
|
|
1905
1916
|
"yield",
|
|
1906
1917
|
"serves",
|
|
1907
1918
|
// List fields
|
|
1908
|
-
"tags"
|
|
1919
|
+
"tags",
|
|
1920
|
+
"variants"
|
|
1909
1921
|
]);
|
|
1910
1922
|
for (const metaVar of [
|
|
1911
1923
|
"title",
|
|
@@ -1983,15 +1995,22 @@ function extractMetadata(content) {
|
|
|
1983
1995
|
};
|
|
1984
1996
|
unitSystem = unitSystemMap[unitSystemRaw.toLowerCase()];
|
|
1985
1997
|
}
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1998
|
+
const yieldValue = parseYieldMetaVar(metadataContent);
|
|
1999
|
+
if (yieldValue) {
|
|
2000
|
+
metadata.yield = yieldValue;
|
|
2001
|
+
servings = getNumericValueFromYield(yieldValue);
|
|
2002
|
+
}
|
|
2003
|
+
for (const metaVar of ["serves", "servings"]) {
|
|
2004
|
+
const result = parseServingsMetaVar(metadataContent, metaVar);
|
|
2005
|
+
if (result !== void 0) {
|
|
2006
|
+
metadata[metaVar] = result.rawValue;
|
|
2007
|
+
servings = result.numericValue;
|
|
1991
2008
|
}
|
|
1992
2009
|
}
|
|
1993
2010
|
const tags = parseListMetaVar(metadataContent, "tags");
|
|
1994
2011
|
if (tags) metadata.tags = tags;
|
|
2012
|
+
const variants = parseListMetaVar(metadataContent, "variants");
|
|
2013
|
+
if (variants) metadata.variants = variants;
|
|
1995
2014
|
const allKeys = extractAllMetadataKeys(metadataContent);
|
|
1996
2015
|
for (const key of allKeys) {
|
|
1997
2016
|
if (handledKeys.has(key)) continue;
|
|
@@ -2266,7 +2285,7 @@ var ProductCatalog = class {
|
|
|
2266
2285
|
const sizeStrings = Array.isArray(size) ? size : [size];
|
|
2267
2286
|
const sizes = sizeStrings.map((sizeStr) => {
|
|
2268
2287
|
const sizeAndUnitRaw = sizeStr.split("%");
|
|
2269
|
-
const sizeParsed =
|
|
2288
|
+
const sizeParsed = parseQuantityValue(
|
|
2270
2289
|
sizeAndUnitRaw[0]
|
|
2271
2290
|
);
|
|
2272
2291
|
const productSize = { size: sizeParsed };
|
|
@@ -2379,8 +2398,10 @@ var Section = class {
|
|
|
2379
2398
|
/**
|
|
2380
2399
|
* Creates an instance of Section.
|
|
2381
2400
|
* @param name - The name of the section. Defaults to an empty string.
|
|
2401
|
+
* @param variants - Optional variant names for this section.
|
|
2402
|
+
* @param optional - Whether the section is optional.
|
|
2382
2403
|
*/
|
|
2383
|
-
constructor(name = "") {
|
|
2404
|
+
constructor(name = "", variants, optional) {
|
|
2384
2405
|
/**
|
|
2385
2406
|
* The name of the section. Can be an empty string for the default (first) section.
|
|
2386
2407
|
* @defaultValue `""`
|
|
@@ -2388,7 +2409,13 @@ var Section = class {
|
|
|
2388
2409
|
__publicField(this, "name");
|
|
2389
2410
|
/** An array of steps and notes that make up the content of the section. */
|
|
2390
2411
|
__publicField(this, "content", []);
|
|
2412
|
+
/** Optional list of variant names this section belongs to. */
|
|
2413
|
+
__publicField(this, "variants");
|
|
2414
|
+
/** Whether the section has been marked as optional ([?]) */
|
|
2415
|
+
__publicField(this, "optional");
|
|
2391
2416
|
this.name = name;
|
|
2417
|
+
if (variants) this.variants = variants;
|
|
2418
|
+
if (optional) this.optional = true;
|
|
2392
2419
|
}
|
|
2393
2420
|
/**
|
|
2394
2421
|
* Checks if the section is blank (has no name and no content).
|
|
@@ -2424,34 +2451,7 @@ function findCompatibleQuantityWithinList(list, quantity) {
|
|
|
2424
2451
|
}
|
|
2425
2452
|
|
|
2426
2453
|
// src/utils/general.ts
|
|
2427
|
-
var
|
|
2428
|
-
if (v === null || typeof v !== "object") {
|
|
2429
|
-
return v;
|
|
2430
|
-
}
|
|
2431
|
-
if (v instanceof Map) {
|
|
2432
|
-
return new Map(
|
|
2433
|
-
Array.from(v.entries()).map(([k, val]) => [
|
|
2434
|
-
legacyDeepClone(k),
|
|
2435
|
-
legacyDeepClone(val)
|
|
2436
|
-
])
|
|
2437
|
-
);
|
|
2438
|
-
}
|
|
2439
|
-
if (v instanceof Set) {
|
|
2440
|
-
return new Set(Array.from(v).map((val) => legacyDeepClone(val)));
|
|
2441
|
-
}
|
|
2442
|
-
if (v instanceof Date) {
|
|
2443
|
-
return new Date(v.getTime());
|
|
2444
|
-
}
|
|
2445
|
-
if (Array.isArray(v)) {
|
|
2446
|
-
return v.map((item) => legacyDeepClone(item));
|
|
2447
|
-
}
|
|
2448
|
-
const cloned = {};
|
|
2449
|
-
for (const key of Object.keys(v)) {
|
|
2450
|
-
cloned[key] = legacyDeepClone(v[key]);
|
|
2451
|
-
}
|
|
2452
|
-
return cloned;
|
|
2453
|
-
};
|
|
2454
|
-
var deepClone = (v) => typeof structuredClone === "function" ? structuredClone(v) : legacyDeepClone(v);
|
|
2454
|
+
var deepClone = (v) => structuredClone(v);
|
|
2455
2455
|
|
|
2456
2456
|
// src/quantities/alternatives.ts
|
|
2457
2457
|
function getEquivalentUnitsLists(...quantities) {
|
|
@@ -2730,6 +2730,47 @@ function addEquivalentsAndSimplify(quantities, system) {
|
|
|
2730
2730
|
return { and: regrouped.map(toPlainUnit) };
|
|
2731
2731
|
}
|
|
2732
2732
|
}
|
|
2733
|
+
function buildEquivalenceRatioMap(unitsLists) {
|
|
2734
|
+
const ratioMap = {};
|
|
2735
|
+
for (const list of unitsLists) {
|
|
2736
|
+
for (const equiv of list) {
|
|
2737
|
+
const equivValue = getAverageValue(equiv.quantity);
|
|
2738
|
+
for (const primary of list) {
|
|
2739
|
+
if (primary === equiv) continue;
|
|
2740
|
+
const primaryValue = getAverageValue(primary.quantity);
|
|
2741
|
+
const equivUnit = normalizeUnit(equiv.unit.name)?.name ?? equiv.unit.name;
|
|
2742
|
+
const primaryUnit = normalizeUnit(primary.unit.name)?.name ?? primary.unit.name;
|
|
2743
|
+
ratioMap[equivUnit] ?? (ratioMap[equivUnit] = {});
|
|
2744
|
+
ratioMap[equivUnit][primaryUnit] = equivValue / primaryValue;
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
return ratioMap;
|
|
2749
|
+
}
|
|
2750
|
+
function recomputeEquivalents(primaries, ratioMap, equivUnits) {
|
|
2751
|
+
const equivalents = [];
|
|
2752
|
+
for (const equivUnit of equivUnits) {
|
|
2753
|
+
const ratios = ratioMap[normalizeUnit(equivUnit)?.name ?? equivUnit];
|
|
2754
|
+
let total = 0;
|
|
2755
|
+
for (const primary of primaries) {
|
|
2756
|
+
const pUnit = normalizeUnit(primary.unit ?? NO_UNIT)?.name ?? primary.unit ?? NO_UNIT;
|
|
2757
|
+
const ratio = ratios[pUnit];
|
|
2758
|
+
if (ratio === void 0) continue;
|
|
2759
|
+
const pValue = getAverageValue(primary.quantity);
|
|
2760
|
+
total += pValue * ratio;
|
|
2761
|
+
}
|
|
2762
|
+
if (total > 0) {
|
|
2763
|
+
equivalents.push({
|
|
2764
|
+
quantity: {
|
|
2765
|
+
type: "fixed",
|
|
2766
|
+
value: { type: "decimal", decimal: total }
|
|
2767
|
+
},
|
|
2768
|
+
...equivUnit !== "" && { unit: equivUnit }
|
|
2769
|
+
});
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
return equivalents.length > 0 ? equivalents : void 0;
|
|
2773
|
+
}
|
|
2733
2774
|
|
|
2734
2775
|
// src/classes/recipe.ts
|
|
2735
2776
|
import Big4 from "big.js";
|
|
@@ -2748,7 +2789,8 @@ var _Recipe = class _Recipe {
|
|
|
2748
2789
|
*/
|
|
2749
2790
|
__publicField(this, "choices", {
|
|
2750
2791
|
ingredientItems: /* @__PURE__ */ new Map(),
|
|
2751
|
-
ingredientGroups: /* @__PURE__ */ new Map()
|
|
2792
|
+
ingredientGroups: /* @__PURE__ */ new Map(),
|
|
2793
|
+
variants: []
|
|
2752
2794
|
});
|
|
2753
2795
|
/**
|
|
2754
2796
|
* The parsed recipe ingredients.
|
|
@@ -2853,7 +2895,7 @@ var _Recipe = class _Recipe {
|
|
|
2853
2895
|
let quantityMatch = quantityRaw.match(quantityAlternativeRegex);
|
|
2854
2896
|
const quantities = [];
|
|
2855
2897
|
while (quantityMatch?.groups) {
|
|
2856
|
-
const value = quantityMatch.groups.quantity ?
|
|
2898
|
+
const value = quantityMatch.groups.quantity ? parseQuantityValue(quantityMatch.groups.quantity) : void 0;
|
|
2857
2899
|
const unit = quantityMatch.groups.unit;
|
|
2858
2900
|
if (value) {
|
|
2859
2901
|
const newQuantity = { quantity: value };
|
|
@@ -3065,6 +3107,10 @@ var _Recipe = class _Recipe {
|
|
|
3065
3107
|
if (itemQuantity) {
|
|
3066
3108
|
Object.assign(alternative, itemQuantity);
|
|
3067
3109
|
}
|
|
3110
|
+
const note = groups.gIngredientNote?.trim();
|
|
3111
|
+
if (note) {
|
|
3112
|
+
alternative.note = note;
|
|
3113
|
+
}
|
|
3068
3114
|
const existingSubgroups = this.choices.ingredientGroups.get(groupKey);
|
|
3069
3115
|
const existingAlternativesFlat = existingSubgroups?.flat();
|
|
3070
3116
|
function upsertAlternativeToIngredient(ingredients, ingredientIdx, newAlternativeIdx) {
|
|
@@ -3158,6 +3204,8 @@ var _Recipe = class _Recipe {
|
|
|
3158
3204
|
/** @internal */
|
|
3159
3205
|
collectQuantityGroups(options) {
|
|
3160
3206
|
const { section, step, choices } = options || {};
|
|
3207
|
+
const activeVariant = choices?.variant;
|
|
3208
|
+
const isDefaultVariant = activeVariant === void 0 || activeVariant === "*";
|
|
3161
3209
|
const sectionsToProcess = section !== void 0 ? (() => {
|
|
3162
3210
|
const idx = typeof section === "number" ? section : this.sections.indexOf(section);
|
|
3163
3211
|
return idx >= 0 && idx < this.sections.length ? [this.sections[idx]] : [];
|
|
@@ -3165,12 +3213,29 @@ var _Recipe = class _Recipe {
|
|
|
3165
3213
|
const ingredientGroups = /* @__PURE__ */ new Map();
|
|
3166
3214
|
const selectedIndices = /* @__PURE__ */ new Set();
|
|
3167
3215
|
const referencedIndices = /* @__PURE__ */ new Set();
|
|
3216
|
+
const dynamicOptionalIndices = /* @__PURE__ */ new Set();
|
|
3168
3217
|
for (const currentSection of sectionsToProcess) {
|
|
3218
|
+
if (currentSection.variants) {
|
|
3219
|
+
if (isDefaultVariant) {
|
|
3220
|
+
if (!currentSection.variants.includes("*")) continue;
|
|
3221
|
+
} else {
|
|
3222
|
+
if (!currentSection.variants.includes(activeVariant)) continue;
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3169
3225
|
const allSteps = currentSection.content.filter(
|
|
3170
3226
|
(item) => item.type === "step"
|
|
3171
3227
|
);
|
|
3228
|
+
const isOptionalSection = currentSection.optional === true;
|
|
3172
3229
|
const stepsToProcess = step === void 0 ? allSteps : typeof step === "number" ? step >= 0 && step < allSteps.length ? [allSteps[step]] : [] : allSteps.includes(step) ? [step] : [];
|
|
3173
3230
|
for (const currentStep of stepsToProcess) {
|
|
3231
|
+
if (currentStep.variants) {
|
|
3232
|
+
if (isDefaultVariant) {
|
|
3233
|
+
if (!currentStep.variants.includes("*")) continue;
|
|
3234
|
+
} else {
|
|
3235
|
+
if (!currentStep.variants.includes(activeVariant)) continue;
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
const isOptionalStep = currentStep.optional === true || isOptionalSection;
|
|
3174
3239
|
for (const item of currentStep.items.filter(
|
|
3175
3240
|
(item2) => item2.type === "ingredient"
|
|
3176
3241
|
)) {
|
|
@@ -3182,18 +3247,55 @@ var _Recipe = class _Recipe {
|
|
|
3182
3247
|
if (isGrouped) {
|
|
3183
3248
|
const groupChoice = choices?.ingredientGroups?.get(item.group);
|
|
3184
3249
|
hasExplicitChoice = groupChoice !== void 0;
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3250
|
+
if (!hasExplicitChoice && !isDefaultVariant) {
|
|
3251
|
+
const matchingSubgroupIdx = groupSubgroups?.findIndex(
|
|
3252
|
+
(sg) => sg.some(
|
|
3253
|
+
(alt) => alt.note && alt.note.toLowerCase().includes(activeVariant.toLowerCase())
|
|
3254
|
+
)
|
|
3255
|
+
);
|
|
3256
|
+
if (matchingSubgroupIdx !== void 0 && matchingSubgroupIdx >= 0) {
|
|
3257
|
+
const matchedSubgroup = groupSubgroups[matchingSubgroupIdx];
|
|
3258
|
+
isSelected = matchedSubgroup.some(
|
|
3259
|
+
(alt) => alt.itemId === item.id
|
|
3260
|
+
);
|
|
3261
|
+
hasExplicitChoice = true;
|
|
3262
|
+
selectedAltIndex = 0;
|
|
3263
|
+
} else {
|
|
3264
|
+
const targetSubgroupIndex = 0;
|
|
3265
|
+
const selectedSubgroup = groupSubgroups?.[targetSubgroupIndex];
|
|
3266
|
+
isSelected = selectedSubgroup.some(
|
|
3267
|
+
(alt) => alt.itemId === item.id
|
|
3268
|
+
);
|
|
3269
|
+
}
|
|
3270
|
+
} else {
|
|
3271
|
+
const targetSubgroupIndex = groupChoice ?? 0;
|
|
3272
|
+
const selectedSubgroup = groupSubgroups?.[targetSubgroupIndex];
|
|
3273
|
+
isSelected = selectedSubgroup?.some((alt) => alt.itemId === item.id) ?? false;
|
|
3274
|
+
}
|
|
3188
3275
|
} else {
|
|
3189
3276
|
const itemChoice = choices?.ingredientItems?.get(item.id);
|
|
3190
3277
|
hasExplicitChoice = itemChoice !== void 0;
|
|
3191
|
-
|
|
3278
|
+
if (!hasExplicitChoice && !isDefaultVariant) {
|
|
3279
|
+
const matchingIndices = item.alternatives.map((alt, idx) => ({ alt, idx })).filter(
|
|
3280
|
+
({ alt }) => alt.note && alt.note.toLowerCase().includes(activeVariant.toLowerCase())
|
|
3281
|
+
).map(({ idx }) => idx);
|
|
3282
|
+
if (matchingIndices.length > 0) {
|
|
3283
|
+
selectedAltIndex = matchingIndices[0];
|
|
3284
|
+
hasExplicitChoice = true;
|
|
3285
|
+
} else {
|
|
3286
|
+
selectedAltIndex = itemChoice ?? 0;
|
|
3287
|
+
}
|
|
3288
|
+
} else {
|
|
3289
|
+
selectedAltIndex = itemChoice ?? 0;
|
|
3290
|
+
}
|
|
3192
3291
|
isSelected = true;
|
|
3193
3292
|
}
|
|
3194
3293
|
const alternative = item.alternatives[selectedAltIndex];
|
|
3195
3294
|
if (!alternative || !isSelected) continue;
|
|
3196
3295
|
selectedIndices.add(alternative.index);
|
|
3296
|
+
if (isOptionalStep) {
|
|
3297
|
+
dynamicOptionalIndices.add(alternative.index);
|
|
3298
|
+
}
|
|
3197
3299
|
const allAltsFlat = isGrouped ? groupSubgroups.flat() : item.alternatives;
|
|
3198
3300
|
for (const alt of allAltsFlat) {
|
|
3199
3301
|
referencedIndices.add(alt.index);
|
|
@@ -3299,7 +3401,12 @@ var _Recipe = class _Recipe {
|
|
|
3299
3401
|
}
|
|
3300
3402
|
}
|
|
3301
3403
|
}
|
|
3302
|
-
return {
|
|
3404
|
+
return {
|
|
3405
|
+
ingredientGroups,
|
|
3406
|
+
selectedIndices,
|
|
3407
|
+
referencedIndices,
|
|
3408
|
+
dynamicOptionalIndices
|
|
3409
|
+
};
|
|
3303
3410
|
}
|
|
3304
3411
|
/**
|
|
3305
3412
|
* Gets the raw (unprocessed) quantity groups for each ingredient, before
|
|
@@ -3318,12 +3425,21 @@ var _Recipe = class _Recipe {
|
|
|
3318
3425
|
* ```
|
|
3319
3426
|
*/
|
|
3320
3427
|
getRawQuantityGroups(options) {
|
|
3321
|
-
const {
|
|
3428
|
+
const {
|
|
3429
|
+
ingredientGroups,
|
|
3430
|
+
selectedIndices,
|
|
3431
|
+
referencedIndices,
|
|
3432
|
+
dynamicOptionalIndices
|
|
3433
|
+
} = this.collectQuantityGroups(options);
|
|
3322
3434
|
const result = [];
|
|
3323
3435
|
for (let index = 0; index < this.ingredients.length; index++) {
|
|
3324
3436
|
if (!referencedIndices.has(index)) continue;
|
|
3325
3437
|
const orig = this.ingredients[index];
|
|
3326
3438
|
const usedAsPrimary = selectedIndices.has(index);
|
|
3439
|
+
let flags = orig.flags;
|
|
3440
|
+
if (dynamicOptionalIndices.has(index) && !flags?.includes("optional")) {
|
|
3441
|
+
flags = [...flags ?? [], "optional"];
|
|
3442
|
+
}
|
|
3327
3443
|
const quantities = [];
|
|
3328
3444
|
if (usedAsPrimary) {
|
|
3329
3445
|
const groupsForIng = ingredientGroups.get(index);
|
|
@@ -3336,7 +3452,7 @@ var _Recipe = class _Recipe {
|
|
|
3336
3452
|
result.push({
|
|
3337
3453
|
name: orig.name,
|
|
3338
3454
|
...usedAsPrimary && { usedAsPrimary: true },
|
|
3339
|
-
...
|
|
3455
|
+
...flags && { flags },
|
|
3340
3456
|
quantities
|
|
3341
3457
|
});
|
|
3342
3458
|
}
|
|
@@ -3370,15 +3486,24 @@ var _Recipe = class _Recipe {
|
|
|
3370
3486
|
* ```
|
|
3371
3487
|
*/
|
|
3372
3488
|
getIngredientQuantities(options) {
|
|
3373
|
-
const {
|
|
3489
|
+
const {
|
|
3490
|
+
ingredientGroups,
|
|
3491
|
+
selectedIndices,
|
|
3492
|
+
referencedIndices,
|
|
3493
|
+
dynamicOptionalIndices
|
|
3494
|
+
} = this.collectQuantityGroups(options);
|
|
3374
3495
|
const result = [];
|
|
3375
3496
|
for (let index = 0; index < this.ingredients.length; index++) {
|
|
3376
3497
|
if (!referencedIndices.has(index)) continue;
|
|
3377
3498
|
const orig = this.ingredients[index];
|
|
3499
|
+
let flags = orig.flags;
|
|
3500
|
+
if (dynamicOptionalIndices.has(index) && !flags?.includes("optional")) {
|
|
3501
|
+
flags = [...flags ?? [], "optional"];
|
|
3502
|
+
}
|
|
3378
3503
|
const ing = {
|
|
3379
3504
|
name: orig.name,
|
|
3380
3505
|
...orig.preparation && { preparation: orig.preparation },
|
|
3381
|
-
...
|
|
3506
|
+
...flags && { flags },
|
|
3382
3507
|
...orig.extras && { extras: orig.extras }
|
|
3383
3508
|
};
|
|
3384
3509
|
if (selectedIndices.has(index)) {
|
|
@@ -3429,6 +3554,59 @@ var _Recipe = class _Recipe {
|
|
|
3429
3554
|
}
|
|
3430
3555
|
return result;
|
|
3431
3556
|
}
|
|
3557
|
+
/**
|
|
3558
|
+
* Returns the list of cookware items that are used in the active variant.
|
|
3559
|
+
* Cookware in steps/sections not matching the active variant are excluded.
|
|
3560
|
+
* Hidden cookware is always excluded.
|
|
3561
|
+
*
|
|
3562
|
+
* @param options - Options for filtering:
|
|
3563
|
+
* - `choices`: The choices to apply (only `variant` is used)
|
|
3564
|
+
* @returns Array of Cookware objects referenced by active steps
|
|
3565
|
+
*
|
|
3566
|
+
* @example
|
|
3567
|
+
* ```typescript
|
|
3568
|
+
* // Get all cookware for the default variant
|
|
3569
|
+
* const cookware = recipe.getCookwareForVariant();
|
|
3570
|
+
*
|
|
3571
|
+
* // Get cookware for a specific variant
|
|
3572
|
+
* const veganCookware = recipe.getCookwareForVariant({ choices: { variant: 'vegan' } });
|
|
3573
|
+
* ```
|
|
3574
|
+
*/
|
|
3575
|
+
getCookwareForVariant(options) {
|
|
3576
|
+
const { choices } = options || {};
|
|
3577
|
+
const activeVariant = choices?.variant;
|
|
3578
|
+
const isDefaultVariant = activeVariant === void 0 || activeVariant === "*";
|
|
3579
|
+
const cookwareIndices = /* @__PURE__ */ new Set();
|
|
3580
|
+
for (const currentSection of this.sections) {
|
|
3581
|
+
if (currentSection.variants) {
|
|
3582
|
+
if (isDefaultVariant) {
|
|
3583
|
+
if (!currentSection.variants.includes("*")) continue;
|
|
3584
|
+
} else {
|
|
3585
|
+
if (!currentSection.variants.includes(activeVariant)) continue;
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
const allSteps = currentSection.content.filter(
|
|
3589
|
+
(item) => item.type === "step"
|
|
3590
|
+
);
|
|
3591
|
+
for (const currentStep of allSteps) {
|
|
3592
|
+
if (currentStep.variants) {
|
|
3593
|
+
if (isDefaultVariant) {
|
|
3594
|
+
if (!currentStep.variants.includes("*")) continue;
|
|
3595
|
+
} else {
|
|
3596
|
+
if (!currentStep.variants.includes(activeVariant)) continue;
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
for (const item of currentStep.items) {
|
|
3600
|
+
if (item.type === "cookware") {
|
|
3601
|
+
cookwareIndices.add(item.index);
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
return this.cookware.filter(
|
|
3607
|
+
(cw, idx) => cookwareIndices.has(idx) && !cw.flags?.includes("hidden")
|
|
3608
|
+
);
|
|
3609
|
+
}
|
|
3432
3610
|
/**
|
|
3433
3611
|
* Parses a recipe from a string.
|
|
3434
3612
|
* @param content - The recipe content to parse.
|
|
@@ -3444,9 +3622,14 @@ var _Recipe = class _Recipe {
|
|
|
3444
3622
|
const items = [];
|
|
3445
3623
|
let noteText = "";
|
|
3446
3624
|
let inNote = false;
|
|
3625
|
+
let stepVariants;
|
|
3626
|
+
let stepOptional;
|
|
3627
|
+
const discoveredVariants = /* @__PURE__ */ new Set();
|
|
3447
3628
|
for (const line of cleanContent) {
|
|
3448
3629
|
if (line.trim().length === 0) {
|
|
3449
|
-
flushPendingItems(section, items);
|
|
3630
|
+
flushPendingItems(section, items, stepVariants, stepOptional);
|
|
3631
|
+
stepVariants = void 0;
|
|
3632
|
+
stepOptional = void 0;
|
|
3450
3633
|
flushPendingNote(
|
|
3451
3634
|
section,
|
|
3452
3635
|
noteText ? this._parseNoteText(noteText) : []
|
|
@@ -3457,26 +3640,42 @@ var _Recipe = class _Recipe {
|
|
|
3457
3640
|
continue;
|
|
3458
3641
|
}
|
|
3459
3642
|
if (line.startsWith("=")) {
|
|
3460
|
-
flushPendingItems(section, items);
|
|
3643
|
+
flushPendingItems(section, items, stepVariants, stepOptional);
|
|
3644
|
+
stepVariants = void 0;
|
|
3645
|
+
stepOptional = void 0;
|
|
3461
3646
|
flushPendingNote(
|
|
3462
3647
|
section,
|
|
3463
3648
|
noteText ? this._parseNoteText(noteText) : []
|
|
3464
3649
|
);
|
|
3465
3650
|
noteText = "";
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3651
|
+
let sectionName = line.replace(/^=+|=+$/g, "").trim();
|
|
3652
|
+
let sectionVariants;
|
|
3653
|
+
let sectionOptional;
|
|
3654
|
+
const sectionVarMatch = sectionName.match(variantTagRegex);
|
|
3655
|
+
if (sectionVarMatch?.groups) {
|
|
3656
|
+
const isOptionalPrefix = sectionVarMatch.groups.variantOptionalPrefix === "?";
|
|
3657
|
+
const names = (sectionVarMatch.groups.variantNames ?? "").split(",").map((n2) => n2.trim()).filter((n2) => n2.length > 0);
|
|
3658
|
+
if (names.length > 0) {
|
|
3659
|
+
sectionVariants = names;
|
|
3660
|
+
for (const v of names) discoveredVariants.add(v);
|
|
3661
|
+
}
|
|
3662
|
+
if (isOptionalPrefix) {
|
|
3663
|
+
sectionOptional = true;
|
|
3471
3664
|
}
|
|
3472
|
-
|
|
3665
|
+
sectionName = sectionName.slice(sectionVarMatch[0].length).trim();
|
|
3473
3666
|
}
|
|
3667
|
+
if (!section.isBlank()) {
|
|
3668
|
+
this.sections.push(section);
|
|
3669
|
+
}
|
|
3670
|
+
section = new Section(sectionName, sectionVariants, sectionOptional);
|
|
3474
3671
|
blankLineBefore = true;
|
|
3475
3672
|
inNote = false;
|
|
3476
3673
|
continue;
|
|
3477
3674
|
}
|
|
3478
3675
|
if (blankLineBefore && line.startsWith(">")) {
|
|
3479
|
-
flushPendingItems(section, items);
|
|
3676
|
+
flushPendingItems(section, items, stepVariants, stepOptional);
|
|
3677
|
+
stepVariants = void 0;
|
|
3678
|
+
stepOptional = void 0;
|
|
3480
3679
|
noteText = line.substring(1).trim();
|
|
3481
3680
|
inNote = true;
|
|
3482
3681
|
blankLineBefore = false;
|
|
@@ -3491,11 +3690,31 @@ var _Recipe = class _Recipe {
|
|
|
3491
3690
|
blankLineBefore = false;
|
|
3492
3691
|
continue;
|
|
3493
3692
|
}
|
|
3693
|
+
let currentLine = line;
|
|
3694
|
+
if (items.length === 0) {
|
|
3695
|
+
const varMatch = currentLine.match(variantTagRegex);
|
|
3696
|
+
if (varMatch?.groups) {
|
|
3697
|
+
const isOptionalPrefix = varMatch.groups.variantOptionalPrefix === "?";
|
|
3698
|
+
const names = (varMatch.groups.variantNames ?? "").split(",").map((n2) => n2.trim()).filter((n2) => n2.length > 0);
|
|
3699
|
+
if (names.length > 0) {
|
|
3700
|
+
stepVariants = names;
|
|
3701
|
+
for (const v of names) discoveredVariants.add(v);
|
|
3702
|
+
}
|
|
3703
|
+
if (isOptionalPrefix) {
|
|
3704
|
+
stepOptional = true;
|
|
3705
|
+
}
|
|
3706
|
+
currentLine = currentLine.slice(varMatch[0].length);
|
|
3707
|
+
if (currentLine.trim().length === 0) {
|
|
3708
|
+
blankLineBefore = false;
|
|
3709
|
+
continue;
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3494
3713
|
let cursor = 0;
|
|
3495
|
-
for (const match of
|
|
3714
|
+
for (const match of currentLine.matchAll(tokensRegex)) {
|
|
3496
3715
|
const idx = match.index;
|
|
3497
3716
|
if (idx > cursor) {
|
|
3498
|
-
items.push(...parseMarkdownSegments(
|
|
3717
|
+
items.push(...parseMarkdownSegments(currentLine.slice(cursor, idx)));
|
|
3499
3718
|
}
|
|
3500
3719
|
const groups = match.groups;
|
|
3501
3720
|
if (groups.mIngredientName || groups.sIngredientName) {
|
|
@@ -3514,7 +3733,7 @@ var _Recipe = class _Recipe {
|
|
|
3514
3733
|
if (modifiers !== void 0 && modifiers.includes("-")) {
|
|
3515
3734
|
flags.push("hidden");
|
|
3516
3735
|
}
|
|
3517
|
-
const quantity = quantityRaw ?
|
|
3736
|
+
const quantity = quantityRaw ? parseQuantityValue(quantityRaw) : void 0;
|
|
3518
3737
|
const newCookware = {
|
|
3519
3738
|
name
|
|
3520
3739
|
};
|
|
@@ -3546,7 +3765,7 @@ var _Recipe = class _Recipe {
|
|
|
3546
3765
|
throw new Error("Timer missing unit");
|
|
3547
3766
|
}
|
|
3548
3767
|
const name = groups.timerName || void 0;
|
|
3549
|
-
const duration =
|
|
3768
|
+
const duration = parseQuantityValue(durationStr);
|
|
3550
3769
|
const timerObj = {
|
|
3551
3770
|
name,
|
|
3552
3771
|
duration,
|
|
@@ -3556,16 +3775,21 @@ var _Recipe = class _Recipe {
|
|
|
3556
3775
|
}
|
|
3557
3776
|
cursor = idx + match[0].length;
|
|
3558
3777
|
}
|
|
3559
|
-
if (cursor <
|
|
3560
|
-
items.push(...parseMarkdownSegments(
|
|
3778
|
+
if (cursor < currentLine.length) {
|
|
3779
|
+
items.push(...parseMarkdownSegments(currentLine.slice(cursor)));
|
|
3561
3780
|
}
|
|
3562
3781
|
blankLineBefore = false;
|
|
3563
3782
|
}
|
|
3564
|
-
flushPendingItems(section, items);
|
|
3783
|
+
flushPendingItems(section, items, stepVariants, stepOptional);
|
|
3565
3784
|
flushPendingNote(section, noteText ? this._parseNoteText(noteText) : []);
|
|
3566
3785
|
if (!section.isBlank()) {
|
|
3567
3786
|
this.sections.push(section);
|
|
3568
3787
|
}
|
|
3788
|
+
const metaVariants = this.metadata.variants ?? [];
|
|
3789
|
+
const allVariants = /* @__PURE__ */ new Set([...metaVariants, ...discoveredVariants]);
|
|
3790
|
+
if (allVariants.size > 0) {
|
|
3791
|
+
this.choices.variants = [...allVariants];
|
|
3792
|
+
}
|
|
3569
3793
|
this._populateIngredientQuantities();
|
|
3570
3794
|
}
|
|
3571
3795
|
/**
|
|
@@ -3674,9 +3898,15 @@ var _Recipe = class _Recipe {
|
|
|
3674
3898
|
}
|
|
3675
3899
|
newRecipe._populateIngredientQuantities();
|
|
3676
3900
|
newRecipe.servings = Big4(originalServings).times(factor).toNumber();
|
|
3677
|
-
for (const metaVar of ["servings", "
|
|
3678
|
-
if (newRecipe.metadata[metaVar]
|
|
3679
|
-
|
|
3901
|
+
for (const metaVar of ["servings", "serves"]) {
|
|
3902
|
+
if (typeof newRecipe.metadata[metaVar] === "number") {
|
|
3903
|
+
newRecipe.metadata[metaVar] = Big4(newRecipe.metadata[metaVar]).times(factor).toNumber();
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
if (newRecipe.metadata.yield && this.metadata.yield) {
|
|
3907
|
+
const original = this.metadata.yield;
|
|
3908
|
+
if (original.quantity.type === "fixed" && original.quantity.value.type === "text") {
|
|
3909
|
+
} else {
|
|
3680
3910
|
const scaledQuantity = multiplyQuantityValue(
|
|
3681
3911
|
original.quantity,
|
|
3682
3912
|
factor
|
|
@@ -3691,8 +3921,7 @@ var _Recipe = class _Recipe {
|
|
|
3691
3921
|
if (optimized.unit) scaled.unit = optimized.unit;
|
|
3692
3922
|
if (original.textBefore) scaled.textBefore = original.textBefore;
|
|
3693
3923
|
if (original.textAfter) scaled.textAfter = original.textAfter;
|
|
3694
|
-
|
|
3695
|
-
newRecipe.metadata[metaVar] = scaled;
|
|
3924
|
+
newRecipe.metadata.yield = scaled;
|
|
3696
3925
|
}
|
|
3697
3926
|
}
|
|
3698
3927
|
return newRecipe;
|
|
@@ -3876,7 +4105,11 @@ var _Recipe = class _Recipe {
|
|
|
3876
4105
|
newRecipe.metadata = deepClone(this.metadata);
|
|
3877
4106
|
newRecipe.ingredients = deepClone(this.ingredients);
|
|
3878
4107
|
newRecipe.sections = this.sections.map((section) => {
|
|
3879
|
-
const newSection = new Section(
|
|
4108
|
+
const newSection = new Section(
|
|
4109
|
+
section.name,
|
|
4110
|
+
section.variants,
|
|
4111
|
+
section.optional
|
|
4112
|
+
);
|
|
3880
4113
|
newSection.content = deepClone(section.content);
|
|
3881
4114
|
return newSection;
|
|
3882
4115
|
});
|
|
@@ -3905,7 +4138,7 @@ __publicField(_Recipe, "subgroupIndices", /* @__PURE__ */ new WeakMap());
|
|
|
3905
4138
|
var Recipe = _Recipe;
|
|
3906
4139
|
|
|
3907
4140
|
// src/classes/shopping_list.ts
|
|
3908
|
-
var ShoppingList = class
|
|
4141
|
+
var ShoppingList = class {
|
|
3909
4142
|
/**
|
|
3910
4143
|
* Creates a new ShoppingList instance
|
|
3911
4144
|
* @param categoryConfigStr - The category configuration to parse.
|
|
@@ -4000,7 +4233,7 @@ var ShoppingList = class _ShoppingList {
|
|
|
4000
4233
|
}
|
|
4001
4234
|
}
|
|
4002
4235
|
if (numericEntries.length > 1) {
|
|
4003
|
-
const ratioMap =
|
|
4236
|
+
const ratioMap = buildEquivalenceRatioMap(
|
|
4004
4237
|
getEquivalentUnitsLists(...numericEntries)
|
|
4005
4238
|
);
|
|
4006
4239
|
if (Object.keys(ratioMap).length > 0) {
|
|
@@ -4057,10 +4290,12 @@ var ShoppingList = class _ShoppingList {
|
|
|
4057
4290
|
const pantryHasUnit = pantryExtended.unit !== void 0 && pantryExtended.unit.name !== "";
|
|
4058
4291
|
const ratioMap = this.equivalenceRatios.get(ingredient.name);
|
|
4059
4292
|
const unitMismatch = leafHasUnit !== pantryHasUnit && ratioMap !== void 0;
|
|
4293
|
+
const leafDef = normalizeUnit(leaf.unit);
|
|
4294
|
+
const pantryDef = normalizeUnit(pantryExtended.unit?.name);
|
|
4060
4295
|
if (unitMismatch) {
|
|
4061
4296
|
const leafUnit = leaf.unit ?? NO_UNIT;
|
|
4062
4297
|
const pantryUnit = pantryExtended.unit?.name ?? NO_UNIT;
|
|
4063
|
-
const ratioFromPantry = ratioMap[leafUnit]?.[pantryUnit];
|
|
4298
|
+
const ratioFromPantry = ratioMap[normalizeUnit(leafUnit)?.name ?? leafUnit]?.[normalizeUnit(pantryUnit)?.name ?? pantryUnit];
|
|
4064
4299
|
if (ratioFromPantry !== void 0) {
|
|
4065
4300
|
const pantryValue = getAverageValue(pantryExtended.quantity);
|
|
4066
4301
|
const leafValue = getAverageValue(ingredientExtended.quantity);
|
|
@@ -4075,7 +4310,7 @@ var ShoppingList = class _ShoppingList {
|
|
|
4075
4310
|
type: "fixed",
|
|
4076
4311
|
value: { type: "decimal", decimal: remainingLeafValue }
|
|
4077
4312
|
};
|
|
4078
|
-
const consumedInPantryUnits =
|
|
4313
|
+
const consumedInPantryUnits = subtracted / ratioFromPantry;
|
|
4079
4314
|
const remainingPantryValue = Math.max(
|
|
4080
4315
|
pantryValue - consumedInPantryUnits,
|
|
4081
4316
|
0
|
|
@@ -4092,9 +4327,10 @@ var ShoppingList = class _ShoppingList {
|
|
|
4092
4327
|
};
|
|
4093
4328
|
continue;
|
|
4094
4329
|
}
|
|
4330
|
+
} else {
|
|
4331
|
+
continue;
|
|
4095
4332
|
}
|
|
4096
|
-
}
|
|
4097
|
-
try {
|
|
4333
|
+
} else if (leafDef && pantryDef && areUnitsConvertible(leafDef, pantryDef) || (leaf.unit ?? "").toLowerCase() === (pantryExtended.unit?.name ?? "").toLowerCase()) {
|
|
4098
4334
|
const remaining = subtractQuantities(
|
|
4099
4335
|
ingredientExtended,
|
|
4100
4336
|
pantryExtended,
|
|
@@ -4109,7 +4345,47 @@ var ShoppingList = class _ShoppingList {
|
|
|
4109
4345
|
const updated = toPlainUnit(remaining);
|
|
4110
4346
|
leaf.quantity = updated.quantity;
|
|
4111
4347
|
leaf.unit = updated.unit;
|
|
4112
|
-
}
|
|
4348
|
+
} else if (ratioMap) {
|
|
4349
|
+
const canonicalLeaf = normalizeUnit(leaf.unit)?.name ?? leaf.unit;
|
|
4350
|
+
const leafValue = getAverageValue(ingredientExtended.quantity);
|
|
4351
|
+
const pantryValue = getAverageValue(pantryExtended.quantity);
|
|
4352
|
+
if (typeof leafValue === "number" && typeof pantryValue === "number" && pantryDef) {
|
|
4353
|
+
for (const [equivUnit, ratios] of Object.entries(ratioMap)) {
|
|
4354
|
+
const ratio = ratios[canonicalLeaf];
|
|
4355
|
+
if (ratio === void 0) continue;
|
|
4356
|
+
const equivDef = normalizeUnit(equivUnit);
|
|
4357
|
+
if (!equivDef || !areUnitsConvertible(equivDef, pantryDef))
|
|
4358
|
+
continue;
|
|
4359
|
+
const pantryInEquiv = pantryValue * getToBase(pantryDef) / getToBase(equivDef);
|
|
4360
|
+
const pantryInLeafUnits = pantryInEquiv / ratio;
|
|
4361
|
+
const subtracted = Math.min(pantryInLeafUnits, leafValue);
|
|
4362
|
+
const remainingLeafValue = Math.max(
|
|
4363
|
+
leafValue - pantryInLeafUnits,
|
|
4364
|
+
0
|
|
4365
|
+
);
|
|
4366
|
+
leaf.quantity = {
|
|
4367
|
+
type: "fixed",
|
|
4368
|
+
value: { type: "decimal", decimal: remainingLeafValue }
|
|
4369
|
+
};
|
|
4370
|
+
const consumedInEquiv = subtracted * ratio;
|
|
4371
|
+
const consumedInPantryUnits = consumedInEquiv * getToBase(equivDef) / getToBase(pantryDef);
|
|
4372
|
+
const remainingPantryValue = Math.max(
|
|
4373
|
+
pantryValue - consumedInPantryUnits,
|
|
4374
|
+
0
|
|
4375
|
+
);
|
|
4376
|
+
pantryExtended = {
|
|
4377
|
+
quantity: {
|
|
4378
|
+
type: "fixed",
|
|
4379
|
+
value: {
|
|
4380
|
+
type: "decimal",
|
|
4381
|
+
decimal: remainingPantryValue
|
|
4382
|
+
}
|
|
4383
|
+
},
|
|
4384
|
+
...pantryExtended.unit && { unit: pantryExtended.unit }
|
|
4385
|
+
};
|
|
4386
|
+
break;
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4113
4389
|
}
|
|
4114
4390
|
}
|
|
4115
4391
|
if ("and" in entry) {
|
|
@@ -4120,8 +4396,8 @@ var ShoppingList = class _ShoppingList {
|
|
|
4120
4396
|
entry.and.push(...nonZero);
|
|
4121
4397
|
const ratioMap = this.equivalenceRatios.get(ingredient.name);
|
|
4122
4398
|
if (entry.equivalents && ratioMap) {
|
|
4123
|
-
const equivUnits = entry.equivalents.map((e2) => e2.unit
|
|
4124
|
-
entry.equivalents =
|
|
4399
|
+
const equivUnits = entry.equivalents.map((e2) => e2.unit);
|
|
4400
|
+
entry.equivalents = recomputeEquivalents(
|
|
4125
4401
|
entry.and,
|
|
4126
4402
|
ratioMap,
|
|
4127
4403
|
equivUnits
|
|
@@ -4132,17 +4408,17 @@ var ShoppingList = class _ShoppingList {
|
|
|
4132
4408
|
ingredient.quantities[i2] = {
|
|
4133
4409
|
quantity: single.quantity,
|
|
4134
4410
|
...single.unit && { unit: single.unit },
|
|
4135
|
-
...entry.equivalents && { equivalents: entry.equivalents }
|
|
4136
|
-
...entry.alternatives && { alternatives: entry.alternatives }
|
|
4411
|
+
...entry.equivalents && { equivalents: entry.equivalents }
|
|
4137
4412
|
};
|
|
4138
4413
|
}
|
|
4139
4414
|
} else if ("equivalents" in entry && entry.equivalents) {
|
|
4140
4415
|
const ratioMap = this.equivalenceRatios.get(ingredient.name);
|
|
4141
4416
|
if (ratioMap) {
|
|
4142
4417
|
const equivUnits = entry.equivalents.map(
|
|
4143
|
-
(e2) => e2.unit
|
|
4418
|
+
(e2) => e2.unit
|
|
4419
|
+
// equivalents always have units
|
|
4144
4420
|
);
|
|
4145
|
-
const recomputed =
|
|
4421
|
+
const recomputed = recomputeEquivalents(
|
|
4146
4422
|
[entry],
|
|
4147
4423
|
ratioMap,
|
|
4148
4424
|
equivUnits
|
|
@@ -4165,57 +4441,6 @@ var ShoppingList = class _ShoppingList {
|
|
|
4165
4441
|
}
|
|
4166
4442
|
this.resultingPantry = clonedPantry;
|
|
4167
4443
|
}
|
|
4168
|
-
/**
|
|
4169
|
-
* Builds a ratio map from equivalence lists.
|
|
4170
|
-
* For each equivalence list, stores ratio = equiv_value / primary_value
|
|
4171
|
-
* for every pair of units, so equivalents can be recomputed after
|
|
4172
|
-
* pantry subtraction modifies primary quantities.
|
|
4173
|
-
*/
|
|
4174
|
-
static buildEquivalenceRatioMap(unitsLists) {
|
|
4175
|
-
const ratioMap = {};
|
|
4176
|
-
for (const list of unitsLists) {
|
|
4177
|
-
for (const equiv of list) {
|
|
4178
|
-
const equivValue = getAverageValue(equiv.quantity);
|
|
4179
|
-
for (const primary of list) {
|
|
4180
|
-
if (primary === equiv) continue;
|
|
4181
|
-
const primaryValue = getAverageValue(primary.quantity);
|
|
4182
|
-
const equivUnit = equiv.unit.name;
|
|
4183
|
-
const primaryUnit = primary.unit.name;
|
|
4184
|
-
ratioMap[equivUnit] ?? (ratioMap[equivUnit] = {});
|
|
4185
|
-
ratioMap[equivUnit][primaryUnit] = equivValue / primaryValue;
|
|
4186
|
-
}
|
|
4187
|
-
}
|
|
4188
|
-
}
|
|
4189
|
-
return ratioMap;
|
|
4190
|
-
}
|
|
4191
|
-
/**
|
|
4192
|
-
* Recomputes equivalent quantities from current primary values and stored ratios.
|
|
4193
|
-
* For each equivalent unit in equivUnits, new_value = Σ (primary_value × ratio[equivUnit][primaryUnit]).
|
|
4194
|
-
* Returns undefined if all equivalents compute to zero.
|
|
4195
|
-
*/
|
|
4196
|
-
static recomputeEquivalents(primaries, ratioMap, equivUnits) {
|
|
4197
|
-
const equivalents = [];
|
|
4198
|
-
for (const equivUnit of equivUnits) {
|
|
4199
|
-
const ratios = ratioMap[equivUnit];
|
|
4200
|
-
let total = 0;
|
|
4201
|
-
for (const primary of primaries) {
|
|
4202
|
-
const pUnit = primary.unit ?? NO_UNIT;
|
|
4203
|
-
const ratio = ratios[pUnit];
|
|
4204
|
-
const pValue = getAverageValue(primary.quantity);
|
|
4205
|
-
total += pValue * ratio;
|
|
4206
|
-
}
|
|
4207
|
-
if (total > 0) {
|
|
4208
|
-
equivalents.push({
|
|
4209
|
-
quantity: {
|
|
4210
|
-
type: "fixed",
|
|
4211
|
-
value: { type: "decimal", decimal: total }
|
|
4212
|
-
},
|
|
4213
|
-
...equivUnit !== "" && { unit: equivUnit }
|
|
4214
|
-
});
|
|
4215
|
-
}
|
|
4216
|
-
}
|
|
4217
|
-
return equivalents.length > 0 ? equivalents : void 0;
|
|
4218
|
-
}
|
|
4219
4444
|
/**
|
|
4220
4445
|
* Adds a recipe to the shopping list, then automatically
|
|
4221
4446
|
* recalculates the quantities and recategorize the ingredients.
|
|
@@ -4749,12 +4974,52 @@ function isAlternativeSelected(recipe, choices, item, alternativeIndex) {
|
|
|
4749
4974
|
const selectedIndex = choices?.ingredientItems?.get(item.id);
|
|
4750
4975
|
return alternativeIndex === selectedIndex;
|
|
4751
4976
|
}
|
|
4977
|
+
function isSectionActive(section, variant) {
|
|
4978
|
+
if (!section.variants) return true;
|
|
4979
|
+
const isDefault = variant === void 0 || variant === "*";
|
|
4980
|
+
if (isDefault) {
|
|
4981
|
+
return section.variants.includes("*");
|
|
4982
|
+
}
|
|
4983
|
+
return section.variants.includes(variant);
|
|
4984
|
+
}
|
|
4985
|
+
function isStepActive(step, variant) {
|
|
4986
|
+
if (!step.variants) return true;
|
|
4987
|
+
const isDefault = variant === void 0 || variant === "*";
|
|
4988
|
+
if (isDefault) {
|
|
4989
|
+
return step.variants.includes("*");
|
|
4990
|
+
}
|
|
4991
|
+
return step.variants.includes(variant);
|
|
4992
|
+
}
|
|
4993
|
+
function getEffectiveChoices(recipe, variant) {
|
|
4994
|
+
const choices = { variant };
|
|
4995
|
+
if (variant === void 0 || variant === "*") return choices;
|
|
4996
|
+
const variantLower = variant.toLowerCase();
|
|
4997
|
+
for (const [itemId, alternatives] of recipe.choices.ingredientItems) {
|
|
4998
|
+
const matchIdx = alternatives.findIndex(
|
|
4999
|
+
(alt) => alt.note && alt.note.toLowerCase().includes(variantLower)
|
|
5000
|
+
);
|
|
5001
|
+
if (matchIdx >= 0) {
|
|
5002
|
+
if (!choices.ingredientItems) choices.ingredientItems = /* @__PURE__ */ new Map();
|
|
5003
|
+
choices.ingredientItems.set(itemId, matchIdx);
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
for (const [groupId, subgroups] of recipe.choices.ingredientGroups) {
|
|
5007
|
+
const matchIdx = subgroups.findIndex(
|
|
5008
|
+
(sg) => sg.some(
|
|
5009
|
+
(alt) => alt.note && alt.note.toLowerCase().includes(variantLower)
|
|
5010
|
+
)
|
|
5011
|
+
);
|
|
5012
|
+
if (matchIdx >= 0) {
|
|
5013
|
+
if (!choices.ingredientGroups) choices.ingredientGroups = /* @__PURE__ */ new Map();
|
|
5014
|
+
choices.ingredientGroups.set(groupId, matchIdx);
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
5017
|
+
return choices;
|
|
5018
|
+
}
|
|
4752
5019
|
export {
|
|
4753
|
-
BadIndentationError,
|
|
4754
5020
|
CategoryConfig,
|
|
4755
5021
|
NoProductCatalogForCartError,
|
|
4756
5022
|
NoShoppingListForCartError,
|
|
4757
|
-
NoTabAsIndentError,
|
|
4758
5023
|
Pantry,
|
|
4759
5024
|
ProductCatalog,
|
|
4760
5025
|
Recipe,
|
|
@@ -4769,11 +5034,14 @@ export {
|
|
|
4769
5034
|
formatQuantityWithUnit,
|
|
4770
5035
|
formatSingleValue,
|
|
4771
5036
|
formatUnit,
|
|
5037
|
+
getEffectiveChoices,
|
|
4772
5038
|
hasAlternatives,
|
|
4773
5039
|
isAlternativeSelected,
|
|
4774
5040
|
isAndGroup,
|
|
4775
5041
|
isGroupedItem,
|
|
5042
|
+
isSectionActive,
|
|
4776
5043
|
isSimpleGroup,
|
|
5044
|
+
isStepActive,
|
|
4777
5045
|
renderFractionAsVulgar
|
|
4778
5046
|
};
|
|
4779
5047
|
/* v8 ignore else -- @preserve */
|
|
@@ -4786,6 +5054,9 @@ export {
|
|
|
4786
5054
|
// v8 ignore if -- @preserve: defensive type guard
|
|
4787
5055
|
/* v8 ignore if -- @preserve */
|
|
4788
5056
|
// v8 ignore next -- @preserve
|
|
5057
|
+
// v8 ignore else -- @preserve: text quantities never reach the equivalence path
|
|
4789
5058
|
// v8 ignore else --@preserve: defensive type guard
|
|
4790
5059
|
// v8 ignore else -- @preserve: detection if
|
|
5060
|
+
/* v8 ignore else -- @preserve: only act when there are matches */
|
|
5061
|
+
/* v8 ignore else -- @preserve: initialization pattern */
|
|
4791
5062
|
//# sourceMappingURL=index.js.map
|