@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.js CHANGED
@@ -265,6 +265,12 @@ var i = (() => {
265
265
  })();
266
266
 
267
267
  // src/regex.ts
268
+ var metadataKeyRegex = /^([^:\n]+?):/gm;
269
+ var numericValueRegex = /^-?\d+(\.\d+)?$/;
270
+ var nestedMetaVarRegex = (varName) => new RegExp(
271
+ `^${varName}:\\s*\\r?\\n((?:[ ]+.+(?:\\r?\\n|$))+)`,
272
+ "m"
273
+ );
268
274
  var metadataRegex = d().literal("---").newline().startCaptureGroup().anyCharacter().zeroOrMore().optional().endGroup().newline().literal("---").dotAll().toRegExp();
269
275
  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();
270
276
  var nonWordChar = "\\s@#~\\[\\]{(,;:!?";
@@ -876,6 +882,20 @@ var InvalidQuantityFormat = class extends Error {
876
882
  this.name = "InvalidQuantityFormat";
877
883
  }
878
884
  };
885
+ var NoTabAsIndentError = class extends Error {
886
+ constructor() {
887
+ super(
888
+ `Tabs are not allowed for indentation in metadata blocks. Please use spaces only.`
889
+ );
890
+ this.name = "NoTabAsIndentError";
891
+ }
892
+ };
893
+ var BadIndentationError = class extends Error {
894
+ constructor() {
895
+ super(`Bad identation of a nested block. Please use spaces only.`);
896
+ this.name = "BadIndentationError";
897
+ }
898
+ };
879
899
 
880
900
  // src/utils/type_guards.ts
881
901
  function isGroup(x) {
@@ -1433,6 +1453,108 @@ function parseListMetaVar(content, varName) {
1433
1453
  return listMatch[2].split("\n").filter((line) => line.trim() !== "").map((line) => line.replace(/^\s*-\s*/, "").trim());
1434
1454
  }
1435
1455
  }
1456
+ function extractAllMetadataKeys(content) {
1457
+ const keys = [];
1458
+ for (const match of content.matchAll(metadataKeyRegex)) {
1459
+ keys.push(match[1].trim());
1460
+ }
1461
+ return [...new Set(keys)];
1462
+ }
1463
+ function parseNestedMetaVar(content, varName) {
1464
+ const match = content.match(nestedMetaVarRegex(varName));
1465
+ if (!match) return void 0;
1466
+ const nestedContent = match[1];
1467
+ return parseNestedBlock(nestedContent);
1468
+ }
1469
+ function parseNestedBlock(content) {
1470
+ const lines = content.split(/\r?\n/).filter((line) => line.trim() !== "");
1471
+ if (lines.length === 0) return void 0;
1472
+ const baseIndentMatch = lines[0].match(/^(\s*)/);
1473
+ if (baseIndentMatch?.[0]?.includes(" ")) {
1474
+ throw new NoTabAsIndentError();
1475
+ }
1476
+ const baseIndent = baseIndentMatch?.[1]?.length;
1477
+ if (lines[0].trim().startsWith("- ")) return void 0;
1478
+ const result = {};
1479
+ let i2 = 0;
1480
+ while (i2 < lines.length) {
1481
+ const line = lines[i2];
1482
+ const leadingWhitespace = line.match(/^(\s*)/)?.[1];
1483
+ if (leadingWhitespace && leadingWhitespace.includes(" ")) {
1484
+ throw new NoTabAsIndentError();
1485
+ }
1486
+ const currentIndent = leadingWhitespace.length;
1487
+ if (currentIndent < baseIndent) {
1488
+ break;
1489
+ }
1490
+ if (currentIndent !== baseIndent) {
1491
+ throw new BadIndentationError();
1492
+ }
1493
+ const keyValueMatch = line.match(/^[ ]*([^:\n]+?):\s*(.*)$/);
1494
+ if (!keyValueMatch) {
1495
+ i2++;
1496
+ continue;
1497
+ }
1498
+ const key = keyValueMatch[1].trim();
1499
+ const rawValue = keyValueMatch[2].trim();
1500
+ if (rawValue === "") {
1501
+ const childLines = [];
1502
+ let j = i2 + 1;
1503
+ while (j < lines.length) {
1504
+ const childLine = lines[j];
1505
+ const childIndent = childLine.match(/^([ ]*)/)?.[1]?.length;
1506
+ if (childIndent && childIndent > baseIndent) {
1507
+ childLines.push(childLine);
1508
+ j++;
1509
+ } else {
1510
+ break;
1511
+ }
1512
+ }
1513
+ if (childLines.length > 0) {
1514
+ const firstChildTrimmed = childLines[0].trim();
1515
+ if (firstChildTrimmed.startsWith("- ")) {
1516
+ const reconstructedContent = `${key}:
1517
+ ${childLines.join("\n")}`;
1518
+ const listResult = parseListMetaVar(reconstructedContent, key);
1519
+ if (listResult) {
1520
+ result[key] = listResult.map(
1521
+ (item) => parseMetadataValue(item)
1522
+ );
1523
+ }
1524
+ } else {
1525
+ const childContent = childLines.join("\n");
1526
+ const nested = parseNestedBlock(childContent);
1527
+ if (nested) {
1528
+ result[key] = nested;
1529
+ }
1530
+ }
1531
+ }
1532
+ i2 = j;
1533
+ } else {
1534
+ result[key] = parseMetadataValue(rawValue);
1535
+ i2++;
1536
+ }
1537
+ }
1538
+ return result;
1539
+ }
1540
+ function parseMetadataValue(rawValue) {
1541
+ if (rawValue.startsWith("[") && rawValue.endsWith("]")) {
1542
+ return rawValue.slice(1, -1).split(",").map((item) => item.trim());
1543
+ }
1544
+ if (numericValueRegex.test(rawValue)) {
1545
+ return Number(rawValue);
1546
+ }
1547
+ return rawValue;
1548
+ }
1549
+ function parseAnyMetaVar(content, varName) {
1550
+ const nested = parseNestedMetaVar(content, varName);
1551
+ if (nested) return nested;
1552
+ const list = parseListMetaVar(content, varName);
1553
+ if (list) return list;
1554
+ const simple = parseSimpleMetaVar(content, varName);
1555
+ if (simple) return parseMetadataValue(simple);
1556
+ return void 0;
1557
+ }
1436
1558
  function extractMetadata(content) {
1437
1559
  const metadata = {};
1438
1560
  let servings = void 0;
@@ -1440,13 +1562,24 @@ function extractMetadata(content) {
1440
1562
  if (!metadataContent) {
1441
1563
  return { metadata };
1442
1564
  }
1443
- for (const metaVar of [
1565
+ const handledKeys = /* @__PURE__ */ new Set([
1566
+ // Simple string fields
1444
1567
  "title",
1568
+ "author",
1569
+ "locale",
1570
+ "introduction",
1571
+ "description",
1572
+ "course",
1573
+ "category",
1574
+ "diet",
1575
+ "cuisine",
1576
+ "difficulty",
1577
+ // Source fields
1445
1578
  "source",
1446
1579
  "source.name",
1447
1580
  "source.url",
1448
- "author",
1449
1581
  "source.author",
1582
+ // Time fields
1450
1583
  "prep time",
1451
1584
  "time.prep",
1452
1585
  "cook time",
@@ -1454,6 +1587,23 @@ function extractMetadata(content) {
1454
1587
  "time required",
1455
1588
  "time",
1456
1589
  "duration",
1590
+ // Image fields
1591
+ "image",
1592
+ "picture",
1593
+ "images",
1594
+ "pictures",
1595
+ // Unit system
1596
+ "unit system",
1597
+ // Scaling fields
1598
+ "servings",
1599
+ "yield",
1600
+ "serves",
1601
+ // List fields
1602
+ "tags"
1603
+ ]);
1604
+ for (const metaVar of [
1605
+ "title",
1606
+ "author",
1457
1607
  "locale",
1458
1608
  "introduction",
1459
1609
  "description",
@@ -1461,17 +1611,57 @@ function extractMetadata(content) {
1461
1611
  "category",
1462
1612
  "diet",
1463
1613
  "cuisine",
1464
- "difficulty",
1465
- "image",
1466
- "picture"
1614
+ "difficulty"
1467
1615
  ]) {
1468
1616
  const stringMetaValue = parseSimpleMetaVar(metadataContent, metaVar);
1469
1617
  if (stringMetaValue) metadata[metaVar] = stringMetaValue;
1470
1618
  }
1619
+ const sourceNested = parseNestedMetaVar(metadataContent, "source");
1620
+ const sourceTxt = parseSimpleMetaVar(metadataContent, "source");
1621
+ const sourceName = parseSimpleMetaVar(metadataContent, "source.name");
1622
+ const sourceUrl = parseSimpleMetaVar(metadataContent, "source.url");
1623
+ const sourceAuthor = parseSimpleMetaVar(metadataContent, "source.author");
1624
+ if (sourceNested) {
1625
+ const source = {};
1626
+ if (typeof sourceNested.name === "string") source.name = sourceNested.name;
1627
+ if (typeof sourceNested.url === "string") source.url = sourceNested.url;
1628
+ if (typeof sourceNested.author === "string")
1629
+ source.author = sourceNested.author;
1630
+ if (Object.keys(source).length > 0) metadata.source = source;
1631
+ } else if (sourceName || sourceAuthor || sourceUrl) {
1632
+ const source = {};
1633
+ if (sourceName) source.name = sourceName;
1634
+ if (sourceUrl) source.url = sourceUrl;
1635
+ if (sourceAuthor) source.author = sourceAuthor;
1636
+ metadata.source = source;
1637
+ } else if (sourceTxt) {
1638
+ metadata.source = sourceTxt;
1639
+ }
1640
+ const timeNested = parseNestedMetaVar(metadataContent, "time");
1641
+ const prepTime = parseSimpleMetaVar(metadataContent, "prep time") ?? parseSimpleMetaVar(metadataContent, "time.prep");
1642
+ const cookTime = parseSimpleMetaVar(metadataContent, "cook time") ?? parseSimpleMetaVar(metadataContent, "time.cook");
1643
+ const totalTime = parseSimpleMetaVar(metadataContent, "time required") ?? parseSimpleMetaVar(metadataContent, "time") ?? parseSimpleMetaVar(metadataContent, "duration");
1644
+ if (timeNested) {
1645
+ const time = {};
1646
+ if (typeof timeNested.prep === "string") time.prep = timeNested.prep;
1647
+ if (typeof timeNested.cook === "string") time.cook = timeNested.cook;
1648
+ if (typeof timeNested.total === "string") time.total = timeNested.total;
1649
+ if (Object.keys(time).length > 0) metadata.time = time;
1650
+ } else if (prepTime || cookTime || totalTime) {
1651
+ const time = {};
1652
+ if (prepTime) time.prep = prepTime;
1653
+ if (cookTime) time.cook = cookTime;
1654
+ if (totalTime) time.total = totalTime;
1655
+ metadata.time = time;
1656
+ }
1657
+ const image = parseSimpleMetaVar(metadataContent, "image") ?? parseSimpleMetaVar(metadataContent, "picture");
1658
+ if (image) metadata.image = image;
1659
+ const images = parseListMetaVar(metadataContent, "images") ?? parseListMetaVar(metadataContent, "pictures");
1660
+ if (images) metadata.images = images;
1471
1661
  let unitSystem;
1472
1662
  const unitSystemRaw = parseSimpleMetaVar(metadataContent, "unit system");
1473
1663
  if (unitSystemRaw) {
1474
- metadata["unit system"] = unitSystemRaw;
1664
+ metadata.unitSystem = unitSystemRaw;
1475
1665
  const unitSystemMap = {
1476
1666
  metric: "metric",
1477
1667
  us: "US",
@@ -1480,16 +1670,22 @@ function extractMetadata(content) {
1480
1670
  };
1481
1671
  unitSystem = unitSystemMap[unitSystemRaw.toLowerCase()];
1482
1672
  }
1483
- for (const metaVar of ["serves", "yield", "servings"]) {
1673
+ for (const metaVar of ["servings", "yield", "serves"]) {
1484
1674
  const scalingMetaValue = parseScalingMetaVar(metadataContent, metaVar);
1485
1675
  if (scalingMetaValue && scalingMetaValue[1]) {
1486
1676
  metadata[metaVar] = scalingMetaValue[1];
1487
1677
  servings = scalingMetaValue[0];
1488
1678
  }
1489
1679
  }
1490
- for (const metaVar of ["tags", "images", "pictures"]) {
1491
- const listMetaValue = parseListMetaVar(metadataContent, metaVar);
1492
- if (listMetaValue) metadata[metaVar] = listMetaValue;
1680
+ const tags = parseListMetaVar(metadataContent, "tags");
1681
+ if (tags) metadata.tags = tags;
1682
+ const allKeys = extractAllMetadataKeys(metadataContent);
1683
+ for (const key of allKeys) {
1684
+ if (handledKeys.has(key)) continue;
1685
+ const value = parseAnyMetaVar(metadataContent, key);
1686
+ if (value !== void 0) {
1687
+ metadata[key] = value;
1688
+ }
1493
1689
  }
1494
1690
  return { metadata, servings, unitSystem };
1495
1691
  }
@@ -2235,7 +2431,7 @@ var _Recipe = class _Recipe {
2235
2431
  alternative.note = note;
2236
2432
  }
2237
2433
  if (itemQuantity) {
2238
- alternative.itemQuantity = itemQuantity;
2434
+ Object.assign(alternative, itemQuantity);
2239
2435
  }
2240
2436
  alternatives.push(alternative);
2241
2437
  testString = groups.ingredientAlternative || "";
@@ -2343,7 +2539,7 @@ var _Recipe = class _Recipe {
2343
2539
  displayName
2344
2540
  };
2345
2541
  if (itemQuantity) {
2346
- alternative.itemQuantity = itemQuantity;
2542
+ Object.assign(alternative, itemQuantity);
2347
2543
  }
2348
2544
  const existingAlternatives = this.choices.ingredientGroups.get(groupKey);
2349
2545
  function upsertAlternativeToIngredient(ingredients, ingredientIdx, newAlternativeIdx) {
@@ -2390,7 +2586,7 @@ var _Recipe = class _Recipe {
2390
2586
  * Quantities are grouped by their alternative signature and summed using addEquivalentsAndSimplify.
2391
2587
  * @internal
2392
2588
  */
2393
- _populate_ingredient_quantities() {
2589
+ _populateIngredientQuantities() {
2394
2590
  for (const ing of this.ingredients) {
2395
2591
  delete ing.quantities;
2396
2592
  delete ing.usedAsPrimary;
@@ -2479,28 +2675,28 @@ var _Recipe = class _Recipe {
2479
2675
  for (const alt of allAlts) {
2480
2676
  referencedIndices.add(alt.index);
2481
2677
  }
2482
- if (!alternative.itemQuantity) continue;
2678
+ if (!alternative.quantity) continue;
2483
2679
  const baseQty = {
2484
- quantity: alternative.itemQuantity.quantity,
2485
- ...alternative.itemQuantity.unit && {
2486
- unit: alternative.itemQuantity.unit
2680
+ quantity: alternative.quantity,
2681
+ ...alternative.unit && {
2682
+ unit: alternative.unit
2487
2683
  }
2488
2684
  };
2489
- const quantityEntry = alternative.itemQuantity.equivalents?.length ? { or: [baseQty, ...alternative.itemQuantity.equivalents] } : baseQty;
2685
+ const quantityEntry = alternative.equivalents?.length ? { or: [baseQty, ...alternative.equivalents] } : baseQty;
2490
2686
  let alternativeRefs;
2491
2687
  if (!hasExplicitChoice && allAlts.length > 1) {
2492
2688
  alternativeRefs = allAlts.filter(
2493
2689
  (alt) => isGrouped ? alt.itemId !== item.id : alt.index !== alternative.index
2494
2690
  ).map((otherAlt) => {
2495
2691
  const ref = { index: otherAlt.index };
2496
- if (otherAlt.itemQuantity) {
2692
+ if (otherAlt.quantity) {
2497
2693
  const altQty = {
2498
- quantity: otherAlt.itemQuantity.quantity,
2499
- ...otherAlt.itemQuantity.unit && {
2500
- unit: otherAlt.itemQuantity.unit.name
2694
+ quantity: otherAlt.quantity,
2695
+ ...otherAlt.unit && {
2696
+ unit: otherAlt.unit.name
2501
2697
  },
2502
- ...otherAlt.itemQuantity.equivalents && {
2503
- equivalents: otherAlt.itemQuantity.equivalents.map(
2698
+ ...otherAlt.equivalents && {
2699
+ equivalents: otherAlt.equivalents.map(
2504
2700
  (eq) => toPlainUnit(eq)
2505
2701
  )
2506
2702
  }
@@ -2513,14 +2709,10 @@ var _Recipe = class _Recipe {
2513
2709
  const altIndices = getAlternativeSignature(alternativeRefs) ?? "";
2514
2710
  let signature;
2515
2711
  if (isGrouped) {
2516
- const resolvedUnit = resolveUnit(
2517
- alternative.itemQuantity.unit?.name
2518
- );
2712
+ const resolvedUnit = resolveUnit(alternative.unit?.name);
2519
2713
  signature = `group:${item.group}|${altIndices}|${resolvedUnit.type}`;
2520
2714
  } else if (altIndices) {
2521
- const resolvedUnit = resolveUnit(
2522
- alternative.itemQuantity.unit?.name
2523
- );
2715
+ const resolvedUnit = resolveUnit(alternative.unit?.name);
2524
2716
  signature = `${altIndices}|${resolvedUnit.type}}`;
2525
2717
  } else {
2526
2718
  signature = null;
@@ -2755,7 +2947,7 @@ var _Recipe = class _Recipe {
2755
2947
  if (!section.isBlank()) {
2756
2948
  this.sections.push(section);
2757
2949
  }
2758
- this._populate_ingredient_quantities();
2950
+ this._populateIngredientQuantities();
2759
2951
  }
2760
2952
  /**
2761
2953
  * Scales the recipe to a new number of servings. In practice, it calls
@@ -2788,16 +2980,16 @@ var _Recipe = class _Recipe {
2788
2980
  const unitSystem = this.unitSystem;
2789
2981
  function scaleAlternativesBy(alternatives, factor2) {
2790
2982
  for (const alternative of alternatives) {
2791
- if (alternative.itemQuantity) {
2792
- const scaleFactor = alternative.itemQuantity.scalable ? Big4(factor2) : 1;
2793
- if (alternative.itemQuantity.quantity.type !== "fixed" || alternative.itemQuantity.quantity.value.type !== "text") {
2794
- alternative.itemQuantity.quantity = multiplyQuantityValue(
2795
- alternative.itemQuantity.quantity,
2983
+ if (alternative.quantity) {
2984
+ const scaleFactor = alternative.scalable ? Big4(factor2) : 1;
2985
+ if (alternative.quantity.type !== "fixed" || alternative.quantity.value.type !== "text") {
2986
+ alternative.quantity = multiplyQuantityValue(
2987
+ alternative.quantity,
2796
2988
  scaleFactor
2797
2989
  );
2798
2990
  }
2799
- if (alternative.itemQuantity.equivalents) {
2800
- alternative.itemQuantity.equivalents = alternative.itemQuantity.equivalents.map(
2991
+ if (alternative.equivalents) {
2992
+ alternative.equivalents = alternative.equivalents.map(
2801
2993
  (altQuantity) => {
2802
2994
  if (altQuantity.quantity.type === "fixed" && altQuantity.quantity.value.type === "text") {
2803
2995
  return altQuantity;
@@ -2815,15 +3007,15 @@ var _Recipe = class _Recipe {
2815
3007
  }
2816
3008
  const optimizedPrimary = applyBestUnit(
2817
3009
  {
2818
- quantity: alternative.itemQuantity.quantity,
2819
- unit: alternative.itemQuantity.unit
3010
+ quantity: alternative.quantity,
3011
+ unit: alternative.unit
2820
3012
  },
2821
3013
  unitSystem
2822
3014
  );
2823
- alternative.itemQuantity.quantity = optimizedPrimary.quantity;
2824
- alternative.itemQuantity.unit = optimizedPrimary.unit;
2825
- if (alternative.itemQuantity.equivalents) {
2826
- alternative.itemQuantity.equivalents = alternative.itemQuantity.equivalents.map(
3015
+ alternative.quantity = optimizedPrimary.quantity;
3016
+ alternative.unit = optimizedPrimary.unit;
3017
+ if (alternative.equivalents) {
3018
+ alternative.equivalents = alternative.equivalents.map(
2827
3019
  (eq) => applyBestUnit(eq, unitSystem)
2828
3020
  );
2829
3021
  }
@@ -2853,7 +3045,7 @@ var _Recipe = class _Recipe {
2853
3045
  factor
2854
3046
  );
2855
3047
  }
2856
- newRecipe._populate_ingredient_quantities();
3048
+ newRecipe._populateIngredientQuantities();
2857
3049
  newRecipe.servings = Big4(originalServings).times(factor).toNumber();
2858
3050
  if (newRecipe.metadata.servings && this.metadata.servings) {
2859
3051
  if (floatRegex.test(String(this.metadata.servings).replace(",", ".").trim())) {
@@ -2918,27 +3110,36 @@ var _Recipe = class _Recipe {
2918
3110
  if (method === "remove") {
2919
3111
  return newPrimary;
2920
3112
  } else if (method === "replace") {
3113
+ if (source === "converted") remainingEquivalents.push(oldPrimary);
2921
3114
  if (remainingEquivalents.length > 0) {
2922
3115
  newPrimary.equivalents = remainingEquivalents;
2923
- if (source === "converted") newPrimary.equivalents.push(oldPrimary);
2924
3116
  }
2925
3117
  } else {
2926
3118
  newPrimary.equivalents = [oldPrimary, ...remainingEquivalents];
2927
3119
  }
2928
3120
  return newPrimary;
2929
3121
  }
2930
- function convertItemQuantity(itemQuantity) {
2931
- const primaryUnit = resolveUnit(itemQuantity.unit?.name);
2932
- const equivalents = itemQuantity.equivalents ?? [];
3122
+ function convertAlternativeQuantity(alternative) {
3123
+ const primaryUnit = resolveUnit(alternative.unit?.name);
3124
+ const equivalents = alternative.equivalents ?? [];
2933
3125
  const oldPrimary = {
2934
- quantity: itemQuantity.quantity,
2935
- unit: itemQuantity.unit
3126
+ quantity: alternative.quantity,
3127
+ unit: alternative.unit
2936
3128
  };
2937
3129
  if (primaryUnit.type !== "other" && isUnitCompatibleWithSystem(primaryUnit, system)) {
2938
3130
  if (method === "remove") {
2939
- return { ...itemQuantity, equivalents: void 0 };
3131
+ return {
3132
+ quantity: alternative.quantity,
3133
+ unit: alternative.unit,
3134
+ scalable: alternative.scalable
3135
+ };
2940
3136
  }
2941
- return itemQuantity;
3137
+ return {
3138
+ quantity: alternative.quantity,
3139
+ unit: alternative.unit,
3140
+ scalable: alternative.scalable,
3141
+ equivalents
3142
+ };
2942
3143
  }
2943
3144
  const targetEquivIndex = equivalents.findIndex((eq) => {
2944
3145
  const eqUnit = resolveUnit(eq.unit?.name);
@@ -2953,7 +3154,7 @@ var _Recipe = class _Recipe {
2953
3154
  targetEquiv,
2954
3155
  oldPrimary,
2955
3156
  remainingEquivalents,
2956
- itemQuantity.scalable,
3157
+ alternative.scalable,
2957
3158
  targetEquiv.unit?.integerProtected,
2958
3159
  "swapped"
2959
3160
  );
@@ -2964,8 +3165,8 @@ var _Recipe = class _Recipe {
2964
3165
  converted,
2965
3166
  oldPrimary,
2966
3167
  equivalents,
2967
- itemQuantity.scalable,
2968
- itemQuantity.unit?.integerProtected,
3168
+ alternative.scalable,
3169
+ alternative.unit?.integerProtected,
2969
3170
  "swapped"
2970
3171
  );
2971
3172
  }
@@ -2978,24 +3179,37 @@ var _Recipe = class _Recipe {
2978
3179
  convertedEquiv,
2979
3180
  oldPrimary,
2980
3181
  remainingEquivalents,
2981
- itemQuantity.scalable,
3182
+ alternative.scalable,
2982
3183
  equiv.unit?.integerProtected,
2983
3184
  "converted"
2984
3185
  );
2985
3186
  }
2986
3187
  }
2987
3188
  if (method === "remove") {
2988
- return { ...itemQuantity, equivalents: void 0 };
3189
+ return {
3190
+ quantity: alternative.quantity,
3191
+ unit: alternative.unit,
3192
+ scalable: alternative.scalable
3193
+ };
2989
3194
  } else {
2990
- return itemQuantity;
3195
+ return {
3196
+ quantity: alternative.quantity,
3197
+ unit: alternative.unit,
3198
+ scalable: alternative.scalable,
3199
+ equivalents
3200
+ };
2991
3201
  }
2992
3202
  }
2993
3203
  function convertAlternatives(alternatives) {
2994
3204
  for (const alternative of alternatives) {
2995
- if (alternative.itemQuantity) {
2996
- alternative.itemQuantity = convertItemQuantity(
2997
- alternative.itemQuantity
3205
+ if (alternative.quantity) {
3206
+ const converted = convertAlternativeQuantity(
3207
+ alternative
2998
3208
  );
3209
+ alternative.quantity = converted.quantity;
3210
+ alternative.unit = converted.unit;
3211
+ alternative.scalable = converted.scalable;
3212
+ alternative.equivalents = converted.equivalents;
2999
3213
  }
3000
3214
  }
3001
3215
  }
@@ -3016,7 +3230,7 @@ var _Recipe = class _Recipe {
3016
3230
  for (const alternatives of newRecipe.choices.ingredientItems.values()) {
3017
3231
  convertAlternatives(alternatives);
3018
3232
  }
3019
- newRecipe._populate_ingredient_quantities();
3233
+ newRecipe._populateIngredientQuantities();
3020
3234
  if (method !== "keep") _Recipe.unitSystems.set(newRecipe, system);
3021
3235
  return newRecipe;
3022
3236
  }
@@ -3069,9 +3283,9 @@ var Recipe = _Recipe;
3069
3283
  var ShoppingList = class {
3070
3284
  /**
3071
3285
  * Creates a new ShoppingList instance
3072
- * @param category_config_str - The category configuration to parse.
3286
+ * @param categoryConfigStr - The category configuration to parse.
3073
3287
  */
3074
- constructor(category_config_str) {
3288
+ constructor(categoryConfigStr) {
3075
3289
  // TODO: backport type change
3076
3290
  /**
3077
3291
  * The ingredients in the shopping list.
@@ -3084,16 +3298,16 @@ var ShoppingList = class {
3084
3298
  /**
3085
3299
  * The category configuration for the shopping list.
3086
3300
  */
3087
- __publicField(this, "category_config");
3301
+ __publicField(this, "categoryConfig");
3088
3302
  /**
3089
3303
  * The categorized ingredients in the shopping list.
3090
3304
  */
3091
3305
  __publicField(this, "categories");
3092
- if (category_config_str) {
3093
- this.set_category_config(category_config_str);
3306
+ if (categoryConfigStr) {
3307
+ this.setCategoryConfig(categoryConfigStr);
3094
3308
  }
3095
3309
  }
3096
- calculate_ingredients() {
3310
+ calculateIngredients() {
3097
3311
  this.ingredients = [];
3098
3312
  const addIngredientQuantity = (name, quantityTotal) => {
3099
3313
  const quantityTotalExtended = extendAllUnits(quantityTotal);
@@ -3180,7 +3394,7 @@ var ShoppingList = class {
3180
3394
  * @param options - Options for adding the recipe.
3181
3395
  * @throws Error if the recipe has alternatives without corresponding choices.
3182
3396
  */
3183
- add_recipe(recipe, options = {}) {
3397
+ addRecipe(recipe, options = {}) {
3184
3398
  const errorMessage = this.getUnresolvedAlternativesError(
3185
3399
  recipe,
3186
3400
  options.choices
@@ -3209,7 +3423,7 @@ var ShoppingList = class {
3209
3423
  });
3210
3424
  }
3211
3425
  }
3212
- this.calculate_ingredients();
3426
+ this.calculateIngredients();
3213
3427
  this.categorize();
3214
3428
  }
3215
3429
  /**
@@ -3249,15 +3463,15 @@ var ShoppingList = class {
3249
3463
  }
3250
3464
  /**
3251
3465
  * Removes a recipe from the shopping list, then automatically
3252
- * recalculates the quantities and recategorize the ingredients.s
3466
+ * recalculates the quantities and recategorize the ingredients.
3253
3467
  * @param index - The index of the recipe to remove.
3254
3468
  */
3255
- remove_recipe(index) {
3469
+ removeRecipe(index) {
3256
3470
  if (index < 0 || index >= this.recipes.length) {
3257
3471
  throw new Error("Index out of bounds");
3258
3472
  }
3259
3473
  this.recipes.splice(index, 1);
3260
- this.calculate_ingredients();
3474
+ this.calculateIngredients();
3261
3475
  this.categorize();
3262
3476
  }
3263
3477
  /**
@@ -3265,10 +3479,10 @@ var ShoppingList = class {
3265
3479
  * and automatically categorize current ingredients from the list.
3266
3480
  * @param config - The category configuration to parse.
3267
3481
  */
3268
- set_category_config(config) {
3482
+ setCategoryConfig(config) {
3269
3483
  if (typeof config === "string")
3270
- this.category_config = new CategoryConfig(config);
3271
- else if (config instanceof CategoryConfig) this.category_config = config;
3484
+ this.categoryConfig = new CategoryConfig(config);
3485
+ else if (config instanceof CategoryConfig) this.categoryConfig = config;
3272
3486
  else throw new Error("Invalid category configuration");
3273
3487
  this.categorize();
3274
3488
  }
@@ -3277,17 +3491,17 @@ var ShoppingList = class {
3277
3491
  * Will use the category config if any, otherwise all ingredients will be placed in the "other" category
3278
3492
  */
3279
3493
  categorize() {
3280
- if (!this.category_config) {
3494
+ if (!this.categoryConfig) {
3281
3495
  this.categories = { other: this.ingredients };
3282
3496
  return;
3283
3497
  }
3284
3498
  const categories = { other: [] };
3285
- for (const category of this.category_config.categories) {
3499
+ for (const category of this.categoryConfig.categories) {
3286
3500
  categories[category.name] = [];
3287
3501
  }
3288
3502
  for (const ingredient of this.ingredients) {
3289
3503
  let found = false;
3290
- for (const category of this.category_config.categories) {
3504
+ for (const category of this.categoryConfig.categories) {
3291
3505
  for (const categoryIngredient of category.ingredients) {
3292
3506
  if (categoryIngredient.aliases.includes(ingredient.name)) {
3293
3507
  categories[category.name].push(ingredient);
@@ -3351,7 +3565,6 @@ var ShoppingCart = class {
3351
3565
  setProductCatalog(catalog) {
3352
3566
  this.productCatalog = catalog;
3353
3567
  }
3354
- // TODO: harmonize recipe name to use underscores
3355
3568
  /**
3356
3569
  * Sets the shopping list to build the cart from.
3357
3570
  * To use if a shopping list was not provided at the creation of the instance