@tmlmt/cooklang-parser 3.0.0-alpha.11 → 3.0.0-alpha.13

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 CHANGED
@@ -320,6 +320,12 @@ var i = (() => {
320
320
  })();
321
321
 
322
322
  // src/regex.ts
323
+ var metadataKeyRegex = /^([^:\n]+?):/gm;
324
+ var numericValueRegex = /^-?\d+(\.\d+)?$/;
325
+ var nestedMetaVarRegex = (varName) => new RegExp(
326
+ `^${varName}:\\s*\\r?\\n((?:[ ]+.+(?:\\r?\\n|$))+)`,
327
+ "m"
328
+ );
323
329
  var metadataRegex = d().literal("---").newline().startCaptureGroup().anyCharacter().zeroOrMore().optional().endGroup().newline().literal("---").dotAll().toRegExp();
324
330
  var scalingMetaValueRegex = (varName) => d().startAnchor().literal(varName).literal(":").anyOf("\\t ").zeroOrMore().startCaptureGroup().startCaptureGroup().notAnyOf(",\\n").oneOrMore().endGroup().startGroup().literal(",").whitespace().zeroOrMore().startCaptureGroup().anyCharacter().oneOrMore().endGroup().endGroup().optional().endGroup().endAnchor().multiline().toRegExp();
325
331
  var nonWordChar = "\\s@#~\\[\\]{(,;:!?";
@@ -931,6 +937,20 @@ var InvalidQuantityFormat = class extends Error {
931
937
  this.name = "InvalidQuantityFormat";
932
938
  }
933
939
  };
940
+ var NoTabAsIndentError = class extends Error {
941
+ constructor() {
942
+ super(
943
+ `Tabs are not allowed for indentation in metadata blocks. Please use spaces only.`
944
+ );
945
+ this.name = "NoTabAsIndentError";
946
+ }
947
+ };
948
+ var BadIndentationError = class extends Error {
949
+ constructor() {
950
+ super(`Bad identation of a nested block. Please use spaces only.`);
951
+ this.name = "BadIndentationError";
952
+ }
953
+ };
934
954
 
935
955
  // src/utils/type_guards.ts
936
956
  function isGroup(x) {
@@ -1488,6 +1508,108 @@ function parseListMetaVar(content, varName) {
1488
1508
  return listMatch[2].split("\n").filter((line) => line.trim() !== "").map((line) => line.replace(/^\s*-\s*/, "").trim());
1489
1509
  }
1490
1510
  }
1511
+ function extractAllMetadataKeys(content) {
1512
+ const keys = [];
1513
+ for (const match of content.matchAll(metadataKeyRegex)) {
1514
+ keys.push(match[1].trim());
1515
+ }
1516
+ return [...new Set(keys)];
1517
+ }
1518
+ function parseNestedMetaVar(content, varName) {
1519
+ const match = content.match(nestedMetaVarRegex(varName));
1520
+ if (!match) return void 0;
1521
+ const nestedContent = match[1];
1522
+ return parseNestedBlock(nestedContent);
1523
+ }
1524
+ function parseNestedBlock(content) {
1525
+ const lines = content.split(/\r?\n/).filter((line) => line.trim() !== "");
1526
+ if (lines.length === 0) return void 0;
1527
+ const baseIndentMatch = lines[0].match(/^(\s*)/);
1528
+ if (baseIndentMatch?.[0]?.includes(" ")) {
1529
+ throw new NoTabAsIndentError();
1530
+ }
1531
+ const baseIndent = baseIndentMatch?.[1]?.length;
1532
+ if (lines[0].trim().startsWith("- ")) return void 0;
1533
+ const result = {};
1534
+ let i2 = 0;
1535
+ while (i2 < lines.length) {
1536
+ const line = lines[i2];
1537
+ const leadingWhitespace = line.match(/^(\s*)/)?.[1];
1538
+ if (leadingWhitespace && leadingWhitespace.includes(" ")) {
1539
+ throw new NoTabAsIndentError();
1540
+ }
1541
+ const currentIndent = leadingWhitespace.length;
1542
+ if (currentIndent < baseIndent) {
1543
+ break;
1544
+ }
1545
+ if (currentIndent !== baseIndent) {
1546
+ throw new BadIndentationError();
1547
+ }
1548
+ const keyValueMatch = line.match(/^[ ]*([^:\n]+?):\s*(.*)$/);
1549
+ if (!keyValueMatch) {
1550
+ i2++;
1551
+ continue;
1552
+ }
1553
+ const key = keyValueMatch[1].trim();
1554
+ const rawValue = keyValueMatch[2].trim();
1555
+ if (rawValue === "") {
1556
+ const childLines = [];
1557
+ let j = i2 + 1;
1558
+ while (j < lines.length) {
1559
+ const childLine = lines[j];
1560
+ const childIndent = childLine.match(/^([ ]*)/)?.[1]?.length;
1561
+ if (childIndent && childIndent > baseIndent) {
1562
+ childLines.push(childLine);
1563
+ j++;
1564
+ } else {
1565
+ break;
1566
+ }
1567
+ }
1568
+ if (childLines.length > 0) {
1569
+ const firstChildTrimmed = childLines[0].trim();
1570
+ if (firstChildTrimmed.startsWith("- ")) {
1571
+ const reconstructedContent = `${key}:
1572
+ ${childLines.join("\n")}`;
1573
+ const listResult = parseListMetaVar(reconstructedContent, key);
1574
+ if (listResult) {
1575
+ result[key] = listResult.map(
1576
+ (item) => parseMetadataValue(item)
1577
+ );
1578
+ }
1579
+ } else {
1580
+ const childContent = childLines.join("\n");
1581
+ const nested = parseNestedBlock(childContent);
1582
+ if (nested) {
1583
+ result[key] = nested;
1584
+ }
1585
+ }
1586
+ }
1587
+ i2 = j;
1588
+ } else {
1589
+ result[key] = parseMetadataValue(rawValue);
1590
+ i2++;
1591
+ }
1592
+ }
1593
+ return result;
1594
+ }
1595
+ function parseMetadataValue(rawValue) {
1596
+ if (rawValue.startsWith("[") && rawValue.endsWith("]")) {
1597
+ return rawValue.slice(1, -1).split(",").map((item) => item.trim());
1598
+ }
1599
+ if (numericValueRegex.test(rawValue)) {
1600
+ return Number(rawValue);
1601
+ }
1602
+ return rawValue;
1603
+ }
1604
+ function parseAnyMetaVar(content, varName) {
1605
+ const nested = parseNestedMetaVar(content, varName);
1606
+ if (nested) return nested;
1607
+ const list = parseListMetaVar(content, varName);
1608
+ if (list) return list;
1609
+ const simple = parseSimpleMetaVar(content, varName);
1610
+ if (simple) return parseMetadataValue(simple);
1611
+ return void 0;
1612
+ }
1491
1613
  function extractMetadata(content) {
1492
1614
  const metadata = {};
1493
1615
  let servings = void 0;
@@ -1495,13 +1617,24 @@ function extractMetadata(content) {
1495
1617
  if (!metadataContent) {
1496
1618
  return { metadata };
1497
1619
  }
1498
- for (const metaVar of [
1620
+ const handledKeys = /* @__PURE__ */ new Set([
1621
+ // Simple string fields
1499
1622
  "title",
1623
+ "author",
1624
+ "locale",
1625
+ "introduction",
1626
+ "description",
1627
+ "course",
1628
+ "category",
1629
+ "diet",
1630
+ "cuisine",
1631
+ "difficulty",
1632
+ // Source fields
1500
1633
  "source",
1501
1634
  "source.name",
1502
1635
  "source.url",
1503
- "author",
1504
1636
  "source.author",
1637
+ // Time fields
1505
1638
  "prep time",
1506
1639
  "time.prep",
1507
1640
  "cook time",
@@ -1509,6 +1642,23 @@ function extractMetadata(content) {
1509
1642
  "time required",
1510
1643
  "time",
1511
1644
  "duration",
1645
+ // Image fields
1646
+ "image",
1647
+ "picture",
1648
+ "images",
1649
+ "pictures",
1650
+ // Unit system
1651
+ "unit system",
1652
+ // Scaling fields
1653
+ "servings",
1654
+ "yield",
1655
+ "serves",
1656
+ // List fields
1657
+ "tags"
1658
+ ]);
1659
+ for (const metaVar of [
1660
+ "title",
1661
+ "author",
1512
1662
  "locale",
1513
1663
  "introduction",
1514
1664
  "description",
@@ -1516,17 +1666,57 @@ function extractMetadata(content) {
1516
1666
  "category",
1517
1667
  "diet",
1518
1668
  "cuisine",
1519
- "difficulty",
1520
- "image",
1521
- "picture"
1669
+ "difficulty"
1522
1670
  ]) {
1523
1671
  const stringMetaValue = parseSimpleMetaVar(metadataContent, metaVar);
1524
1672
  if (stringMetaValue) metadata[metaVar] = stringMetaValue;
1525
1673
  }
1674
+ const sourceNested = parseNestedMetaVar(metadataContent, "source");
1675
+ const sourceTxt = parseSimpleMetaVar(metadataContent, "source");
1676
+ const sourceName = parseSimpleMetaVar(metadataContent, "source.name");
1677
+ const sourceUrl = parseSimpleMetaVar(metadataContent, "source.url");
1678
+ const sourceAuthor = parseSimpleMetaVar(metadataContent, "source.author");
1679
+ if (sourceNested) {
1680
+ const source = {};
1681
+ if (typeof sourceNested.name === "string") source.name = sourceNested.name;
1682
+ if (typeof sourceNested.url === "string") source.url = sourceNested.url;
1683
+ if (typeof sourceNested.author === "string")
1684
+ source.author = sourceNested.author;
1685
+ if (Object.keys(source).length > 0) metadata.source = source;
1686
+ } else if (sourceName || sourceAuthor || sourceUrl) {
1687
+ const source = {};
1688
+ if (sourceName) source.name = sourceName;
1689
+ if (sourceUrl) source.url = sourceUrl;
1690
+ if (sourceAuthor) source.author = sourceAuthor;
1691
+ metadata.source = source;
1692
+ } else if (sourceTxt) {
1693
+ metadata.source = sourceTxt;
1694
+ }
1695
+ const timeNested = parseNestedMetaVar(metadataContent, "time");
1696
+ const prepTime = parseSimpleMetaVar(metadataContent, "prep time") ?? parseSimpleMetaVar(metadataContent, "time.prep");
1697
+ const cookTime = parseSimpleMetaVar(metadataContent, "cook time") ?? parseSimpleMetaVar(metadataContent, "time.cook");
1698
+ const totalTime = parseSimpleMetaVar(metadataContent, "time required") ?? parseSimpleMetaVar(metadataContent, "time") ?? parseSimpleMetaVar(metadataContent, "duration");
1699
+ if (timeNested) {
1700
+ const time = {};
1701
+ if (typeof timeNested.prep === "string") time.prep = timeNested.prep;
1702
+ if (typeof timeNested.cook === "string") time.cook = timeNested.cook;
1703
+ if (typeof timeNested.total === "string") time.total = timeNested.total;
1704
+ if (Object.keys(time).length > 0) metadata.time = time;
1705
+ } else if (prepTime || cookTime || totalTime) {
1706
+ const time = {};
1707
+ if (prepTime) time.prep = prepTime;
1708
+ if (cookTime) time.cook = cookTime;
1709
+ if (totalTime) time.total = totalTime;
1710
+ metadata.time = time;
1711
+ }
1712
+ const image = parseSimpleMetaVar(metadataContent, "image") ?? parseSimpleMetaVar(metadataContent, "picture");
1713
+ if (image) metadata.image = image;
1714
+ const images = parseListMetaVar(metadataContent, "images") ?? parseListMetaVar(metadataContent, "pictures");
1715
+ if (images) metadata.images = images;
1526
1716
  let unitSystem;
1527
1717
  const unitSystemRaw = parseSimpleMetaVar(metadataContent, "unit system");
1528
1718
  if (unitSystemRaw) {
1529
- metadata["unit system"] = unitSystemRaw;
1719
+ metadata.unitSystem = unitSystemRaw;
1530
1720
  const unitSystemMap = {
1531
1721
  metric: "metric",
1532
1722
  us: "US",
@@ -1535,16 +1725,22 @@ function extractMetadata(content) {
1535
1725
  };
1536
1726
  unitSystem = unitSystemMap[unitSystemRaw.toLowerCase()];
1537
1727
  }
1538
- for (const metaVar of ["serves", "yield", "servings"]) {
1728
+ for (const metaVar of ["servings", "yield", "serves"]) {
1539
1729
  const scalingMetaValue = parseScalingMetaVar(metadataContent, metaVar);
1540
1730
  if (scalingMetaValue && scalingMetaValue[1]) {
1541
1731
  metadata[metaVar] = scalingMetaValue[1];
1542
1732
  servings = scalingMetaValue[0];
1543
1733
  }
1544
1734
  }
1545
- for (const metaVar of ["tags", "images", "pictures"]) {
1546
- const listMetaValue = parseListMetaVar(metadataContent, metaVar);
1547
- if (listMetaValue) metadata[metaVar] = listMetaValue;
1735
+ const tags = parseListMetaVar(metadataContent, "tags");
1736
+ if (tags) metadata.tags = tags;
1737
+ const allKeys = extractAllMetadataKeys(metadataContent);
1738
+ for (const key of allKeys) {
1739
+ if (handledKeys.has(key)) continue;
1740
+ const value = parseAnyMetaVar(metadataContent, key);
1741
+ if (value !== void 0) {
1742
+ metadata[key] = value;
1743
+ }
1548
1744
  }
1549
1745
  return { metadata, servings, unitSystem };
1550
1746
  }
@@ -2290,7 +2486,7 @@ var _Recipe = class _Recipe {
2290
2486
  alternative.note = note;
2291
2487
  }
2292
2488
  if (itemQuantity) {
2293
- alternative.itemQuantity = itemQuantity;
2489
+ Object.assign(alternative, itemQuantity);
2294
2490
  }
2295
2491
  alternatives.push(alternative);
2296
2492
  testString = groups.ingredientAlternative || "";
@@ -2398,7 +2594,7 @@ var _Recipe = class _Recipe {
2398
2594
  displayName
2399
2595
  };
2400
2596
  if (itemQuantity) {
2401
- alternative.itemQuantity = itemQuantity;
2597
+ Object.assign(alternative, itemQuantity);
2402
2598
  }
2403
2599
  const existingAlternatives = this.choices.ingredientGroups.get(groupKey);
2404
2600
  function upsertAlternativeToIngredient(ingredients, ingredientIdx, newAlternativeIdx) {
@@ -2445,7 +2641,7 @@ var _Recipe = class _Recipe {
2445
2641
  * Quantities are grouped by their alternative signature and summed using addEquivalentsAndSimplify.
2446
2642
  * @internal
2447
2643
  */
2448
- _populate_ingredient_quantities() {
2644
+ _populateIngredientQuantities() {
2449
2645
  for (const ing of this.ingredients) {
2450
2646
  delete ing.quantities;
2451
2647
  delete ing.usedAsPrimary;
@@ -2534,28 +2730,28 @@ var _Recipe = class _Recipe {
2534
2730
  for (const alt of allAlts) {
2535
2731
  referencedIndices.add(alt.index);
2536
2732
  }
2537
- if (!alternative.itemQuantity) continue;
2733
+ if (!alternative.quantity) continue;
2538
2734
  const baseQty = {
2539
- quantity: alternative.itemQuantity.quantity,
2540
- ...alternative.itemQuantity.unit && {
2541
- unit: alternative.itemQuantity.unit
2735
+ quantity: alternative.quantity,
2736
+ ...alternative.unit && {
2737
+ unit: alternative.unit
2542
2738
  }
2543
2739
  };
2544
- const quantityEntry = alternative.itemQuantity.equivalents?.length ? { or: [baseQty, ...alternative.itemQuantity.equivalents] } : baseQty;
2740
+ const quantityEntry = alternative.equivalents?.length ? { or: [baseQty, ...alternative.equivalents] } : baseQty;
2545
2741
  let alternativeRefs;
2546
2742
  if (!hasExplicitChoice && allAlts.length > 1) {
2547
2743
  alternativeRefs = allAlts.filter(
2548
2744
  (alt) => isGrouped ? alt.itemId !== item.id : alt.index !== alternative.index
2549
2745
  ).map((otherAlt) => {
2550
2746
  const ref = { index: otherAlt.index };
2551
- if (otherAlt.itemQuantity) {
2747
+ if (otherAlt.quantity) {
2552
2748
  const altQty = {
2553
- quantity: otherAlt.itemQuantity.quantity,
2554
- ...otherAlt.itemQuantity.unit && {
2555
- unit: otherAlt.itemQuantity.unit.name
2749
+ quantity: otherAlt.quantity,
2750
+ ...otherAlt.unit && {
2751
+ unit: otherAlt.unit.name
2556
2752
  },
2557
- ...otherAlt.itemQuantity.equivalents && {
2558
- equivalents: otherAlt.itemQuantity.equivalents.map(
2753
+ ...otherAlt.equivalents && {
2754
+ equivalents: otherAlt.equivalents.map(
2559
2755
  (eq) => toPlainUnit(eq)
2560
2756
  )
2561
2757
  }
@@ -2568,14 +2764,10 @@ var _Recipe = class _Recipe {
2568
2764
  const altIndices = getAlternativeSignature(alternativeRefs) ?? "";
2569
2765
  let signature;
2570
2766
  if (isGrouped) {
2571
- const resolvedUnit = resolveUnit(
2572
- alternative.itemQuantity.unit?.name
2573
- );
2767
+ const resolvedUnit = resolveUnit(alternative.unit?.name);
2574
2768
  signature = `group:${item.group}|${altIndices}|${resolvedUnit.type}`;
2575
2769
  } else if (altIndices) {
2576
- const resolvedUnit = resolveUnit(
2577
- alternative.itemQuantity.unit?.name
2578
- );
2770
+ const resolvedUnit = resolveUnit(alternative.unit?.name);
2579
2771
  signature = `${altIndices}|${resolvedUnit.type}}`;
2580
2772
  } else {
2581
2773
  signature = null;
@@ -2810,7 +3002,7 @@ var _Recipe = class _Recipe {
2810
3002
  if (!section.isBlank()) {
2811
3003
  this.sections.push(section);
2812
3004
  }
2813
- this._populate_ingredient_quantities();
3005
+ this._populateIngredientQuantities();
2814
3006
  }
2815
3007
  /**
2816
3008
  * Scales the recipe to a new number of servings. In practice, it calls
@@ -2843,16 +3035,16 @@ var _Recipe = class _Recipe {
2843
3035
  const unitSystem = this.unitSystem;
2844
3036
  function scaleAlternativesBy(alternatives, factor2) {
2845
3037
  for (const alternative of alternatives) {
2846
- if (alternative.itemQuantity) {
2847
- const scaleFactor = alternative.itemQuantity.scalable ? (0, import_big4.default)(factor2) : 1;
2848
- if (alternative.itemQuantity.quantity.type !== "fixed" || alternative.itemQuantity.quantity.value.type !== "text") {
2849
- alternative.itemQuantity.quantity = multiplyQuantityValue(
2850
- alternative.itemQuantity.quantity,
3038
+ if (alternative.quantity) {
3039
+ const scaleFactor = alternative.scalable ? (0, import_big4.default)(factor2) : 1;
3040
+ if (alternative.quantity.type !== "fixed" || alternative.quantity.value.type !== "text") {
3041
+ alternative.quantity = multiplyQuantityValue(
3042
+ alternative.quantity,
2851
3043
  scaleFactor
2852
3044
  );
2853
3045
  }
2854
- if (alternative.itemQuantity.equivalents) {
2855
- alternative.itemQuantity.equivalents = alternative.itemQuantity.equivalents.map(
3046
+ if (alternative.equivalents) {
3047
+ alternative.equivalents = alternative.equivalents.map(
2856
3048
  (altQuantity) => {
2857
3049
  if (altQuantity.quantity.type === "fixed" && altQuantity.quantity.value.type === "text") {
2858
3050
  return altQuantity;
@@ -2870,15 +3062,15 @@ var _Recipe = class _Recipe {
2870
3062
  }
2871
3063
  const optimizedPrimary = applyBestUnit(
2872
3064
  {
2873
- quantity: alternative.itemQuantity.quantity,
2874
- unit: alternative.itemQuantity.unit
3065
+ quantity: alternative.quantity,
3066
+ unit: alternative.unit
2875
3067
  },
2876
3068
  unitSystem
2877
3069
  );
2878
- alternative.itemQuantity.quantity = optimizedPrimary.quantity;
2879
- alternative.itemQuantity.unit = optimizedPrimary.unit;
2880
- if (alternative.itemQuantity.equivalents) {
2881
- alternative.itemQuantity.equivalents = alternative.itemQuantity.equivalents.map(
3070
+ alternative.quantity = optimizedPrimary.quantity;
3071
+ alternative.unit = optimizedPrimary.unit;
3072
+ if (alternative.equivalents) {
3073
+ alternative.equivalents = alternative.equivalents.map(
2882
3074
  (eq) => applyBestUnit(eq, unitSystem)
2883
3075
  );
2884
3076
  }
@@ -2908,7 +3100,7 @@ var _Recipe = class _Recipe {
2908
3100
  factor
2909
3101
  );
2910
3102
  }
2911
- newRecipe._populate_ingredient_quantities();
3103
+ newRecipe._populateIngredientQuantities();
2912
3104
  newRecipe.servings = (0, import_big4.default)(originalServings).times(factor).toNumber();
2913
3105
  if (newRecipe.metadata.servings && this.metadata.servings) {
2914
3106
  if (floatRegex.test(String(this.metadata.servings).replace(",", ".").trim())) {
@@ -2973,27 +3165,36 @@ var _Recipe = class _Recipe {
2973
3165
  if (method === "remove") {
2974
3166
  return newPrimary;
2975
3167
  } else if (method === "replace") {
3168
+ if (source === "converted") remainingEquivalents.push(oldPrimary);
2976
3169
  if (remainingEquivalents.length > 0) {
2977
3170
  newPrimary.equivalents = remainingEquivalents;
2978
- if (source === "converted") newPrimary.equivalents.push(oldPrimary);
2979
3171
  }
2980
3172
  } else {
2981
3173
  newPrimary.equivalents = [oldPrimary, ...remainingEquivalents];
2982
3174
  }
2983
3175
  return newPrimary;
2984
3176
  }
2985
- function convertItemQuantity(itemQuantity) {
2986
- const primaryUnit = resolveUnit(itemQuantity.unit?.name);
2987
- const equivalents = itemQuantity.equivalents ?? [];
3177
+ function convertAlternativeQuantity(alternative) {
3178
+ const primaryUnit = resolveUnit(alternative.unit?.name);
3179
+ const equivalents = alternative.equivalents ?? [];
2988
3180
  const oldPrimary = {
2989
- quantity: itemQuantity.quantity,
2990
- unit: itemQuantity.unit
3181
+ quantity: alternative.quantity,
3182
+ unit: alternative.unit
2991
3183
  };
2992
3184
  if (primaryUnit.type !== "other" && isUnitCompatibleWithSystem(primaryUnit, system)) {
2993
3185
  if (method === "remove") {
2994
- return { ...itemQuantity, equivalents: void 0 };
3186
+ return {
3187
+ quantity: alternative.quantity,
3188
+ unit: alternative.unit,
3189
+ scalable: alternative.scalable
3190
+ };
2995
3191
  }
2996
- return itemQuantity;
3192
+ return {
3193
+ quantity: alternative.quantity,
3194
+ unit: alternative.unit,
3195
+ scalable: alternative.scalable,
3196
+ equivalents
3197
+ };
2997
3198
  }
2998
3199
  const targetEquivIndex = equivalents.findIndex((eq) => {
2999
3200
  const eqUnit = resolveUnit(eq.unit?.name);
@@ -3008,7 +3209,7 @@ var _Recipe = class _Recipe {
3008
3209
  targetEquiv,
3009
3210
  oldPrimary,
3010
3211
  remainingEquivalents,
3011
- itemQuantity.scalable,
3212
+ alternative.scalable,
3012
3213
  targetEquiv.unit?.integerProtected,
3013
3214
  "swapped"
3014
3215
  );
@@ -3019,8 +3220,8 @@ var _Recipe = class _Recipe {
3019
3220
  converted,
3020
3221
  oldPrimary,
3021
3222
  equivalents,
3022
- itemQuantity.scalable,
3023
- itemQuantity.unit?.integerProtected,
3223
+ alternative.scalable,
3224
+ alternative.unit?.integerProtected,
3024
3225
  "swapped"
3025
3226
  );
3026
3227
  }
@@ -3033,24 +3234,37 @@ var _Recipe = class _Recipe {
3033
3234
  convertedEquiv,
3034
3235
  oldPrimary,
3035
3236
  remainingEquivalents,
3036
- itemQuantity.scalable,
3237
+ alternative.scalable,
3037
3238
  equiv.unit?.integerProtected,
3038
3239
  "converted"
3039
3240
  );
3040
3241
  }
3041
3242
  }
3042
3243
  if (method === "remove") {
3043
- return { ...itemQuantity, equivalents: void 0 };
3244
+ return {
3245
+ quantity: alternative.quantity,
3246
+ unit: alternative.unit,
3247
+ scalable: alternative.scalable
3248
+ };
3044
3249
  } else {
3045
- return itemQuantity;
3250
+ return {
3251
+ quantity: alternative.quantity,
3252
+ unit: alternative.unit,
3253
+ scalable: alternative.scalable,
3254
+ equivalents
3255
+ };
3046
3256
  }
3047
3257
  }
3048
3258
  function convertAlternatives(alternatives) {
3049
3259
  for (const alternative of alternatives) {
3050
- if (alternative.itemQuantity) {
3051
- alternative.itemQuantity = convertItemQuantity(
3052
- alternative.itemQuantity
3260
+ if (alternative.quantity) {
3261
+ const converted = convertAlternativeQuantity(
3262
+ alternative
3053
3263
  );
3264
+ alternative.quantity = converted.quantity;
3265
+ alternative.unit = converted.unit;
3266
+ alternative.scalable = converted.scalable;
3267
+ alternative.equivalents = converted.equivalents;
3054
3268
  }
3055
3269
  }
3056
3270
  }
@@ -3071,7 +3285,7 @@ var _Recipe = class _Recipe {
3071
3285
  for (const alternatives of newRecipe.choices.ingredientItems.values()) {
3072
3286
  convertAlternatives(alternatives);
3073
3287
  }
3074
- newRecipe._populate_ingredient_quantities();
3288
+ newRecipe._populateIngredientQuantities();
3075
3289
  if (method !== "keep") _Recipe.unitSystems.set(newRecipe, system);
3076
3290
  return newRecipe;
3077
3291
  }
@@ -3124,9 +3338,9 @@ var Recipe = _Recipe;
3124
3338
  var ShoppingList = class {
3125
3339
  /**
3126
3340
  * Creates a new ShoppingList instance
3127
- * @param category_config_str - The category configuration to parse.
3341
+ * @param categoryConfigStr - The category configuration to parse.
3128
3342
  */
3129
- constructor(category_config_str) {
3343
+ constructor(categoryConfigStr) {
3130
3344
  // TODO: backport type change
3131
3345
  /**
3132
3346
  * The ingredients in the shopping list.
@@ -3139,16 +3353,16 @@ var ShoppingList = class {
3139
3353
  /**
3140
3354
  * The category configuration for the shopping list.
3141
3355
  */
3142
- __publicField(this, "category_config");
3356
+ __publicField(this, "categoryConfig");
3143
3357
  /**
3144
3358
  * The categorized ingredients in the shopping list.
3145
3359
  */
3146
3360
  __publicField(this, "categories");
3147
- if (category_config_str) {
3148
- this.set_category_config(category_config_str);
3361
+ if (categoryConfigStr) {
3362
+ this.setCategoryConfig(categoryConfigStr);
3149
3363
  }
3150
3364
  }
3151
- calculate_ingredients() {
3365
+ calculateIngredients() {
3152
3366
  this.ingredients = [];
3153
3367
  const addIngredientQuantity = (name, quantityTotal) => {
3154
3368
  const quantityTotalExtended = extendAllUnits(quantityTotal);
@@ -3235,7 +3449,7 @@ var ShoppingList = class {
3235
3449
  * @param options - Options for adding the recipe.
3236
3450
  * @throws Error if the recipe has alternatives without corresponding choices.
3237
3451
  */
3238
- add_recipe(recipe, options = {}) {
3452
+ addRecipe(recipe, options = {}) {
3239
3453
  const errorMessage = this.getUnresolvedAlternativesError(
3240
3454
  recipe,
3241
3455
  options.choices
@@ -3264,7 +3478,7 @@ var ShoppingList = class {
3264
3478
  });
3265
3479
  }
3266
3480
  }
3267
- this.calculate_ingredients();
3481
+ this.calculateIngredients();
3268
3482
  this.categorize();
3269
3483
  }
3270
3484
  /**
@@ -3304,15 +3518,15 @@ var ShoppingList = class {
3304
3518
  }
3305
3519
  /**
3306
3520
  * Removes a recipe from the shopping list, then automatically
3307
- * recalculates the quantities and recategorize the ingredients.s
3521
+ * recalculates the quantities and recategorize the ingredients.
3308
3522
  * @param index - The index of the recipe to remove.
3309
3523
  */
3310
- remove_recipe(index) {
3524
+ removeRecipe(index) {
3311
3525
  if (index < 0 || index >= this.recipes.length) {
3312
3526
  throw new Error("Index out of bounds");
3313
3527
  }
3314
3528
  this.recipes.splice(index, 1);
3315
- this.calculate_ingredients();
3529
+ this.calculateIngredients();
3316
3530
  this.categorize();
3317
3531
  }
3318
3532
  /**
@@ -3320,10 +3534,10 @@ var ShoppingList = class {
3320
3534
  * and automatically categorize current ingredients from the list.
3321
3535
  * @param config - The category configuration to parse.
3322
3536
  */
3323
- set_category_config(config) {
3537
+ setCategoryConfig(config) {
3324
3538
  if (typeof config === "string")
3325
- this.category_config = new CategoryConfig(config);
3326
- else if (config instanceof CategoryConfig) this.category_config = config;
3539
+ this.categoryConfig = new CategoryConfig(config);
3540
+ else if (config instanceof CategoryConfig) this.categoryConfig = config;
3327
3541
  else throw new Error("Invalid category configuration");
3328
3542
  this.categorize();
3329
3543
  }
@@ -3332,17 +3546,17 @@ var ShoppingList = class {
3332
3546
  * Will use the category config if any, otherwise all ingredients will be placed in the "other" category
3333
3547
  */
3334
3548
  categorize() {
3335
- if (!this.category_config) {
3549
+ if (!this.categoryConfig) {
3336
3550
  this.categories = { other: this.ingredients };
3337
3551
  return;
3338
3552
  }
3339
3553
  const categories = { other: [] };
3340
- for (const category of this.category_config.categories) {
3554
+ for (const category of this.categoryConfig.categories) {
3341
3555
  categories[category.name] = [];
3342
3556
  }
3343
3557
  for (const ingredient of this.ingredients) {
3344
3558
  let found = false;
3345
- for (const category of this.category_config.categories) {
3559
+ for (const category of this.categoryConfig.categories) {
3346
3560
  for (const categoryIngredient of category.ingredients) {
3347
3561
  if (categoryIngredient.aliases.includes(ingredient.name)) {
3348
3562
  categories[category.name].push(ingredient);
@@ -3406,7 +3620,6 @@ var ShoppingCart = class {
3406
3620
  setProductCatalog(catalog) {
3407
3621
  this.productCatalog = catalog;
3408
3622
  }
3409
- // TODO: harmonize recipe name to use underscores
3410
3623
  /**
3411
3624
  * Sets the shopping list to build the cart from.
3412
3625
  * To use if a shopping list was not provided at the creation of the instance