@tmlmt/cooklang-parser 3.0.0-alpha.12 → 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 +228 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -53
- package/dist/index.d.ts +68 -53
- package/dist/index.js +228 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
|
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 ["
|
|
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
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
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
|
}
|
|
@@ -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
|
-
|
|
2589
|
+
_populateIngredientQuantities() {
|
|
2394
2590
|
for (const ing of this.ingredients) {
|
|
2395
2591
|
delete ing.quantities;
|
|
2396
2592
|
delete ing.usedAsPrimary;
|
|
@@ -2751,7 +2947,7 @@ var _Recipe = class _Recipe {
|
|
|
2751
2947
|
if (!section.isBlank()) {
|
|
2752
2948
|
this.sections.push(section);
|
|
2753
2949
|
}
|
|
2754
|
-
this.
|
|
2950
|
+
this._populateIngredientQuantities();
|
|
2755
2951
|
}
|
|
2756
2952
|
/**
|
|
2757
2953
|
* Scales the recipe to a new number of servings. In practice, it calls
|
|
@@ -2849,7 +3045,7 @@ var _Recipe = class _Recipe {
|
|
|
2849
3045
|
factor
|
|
2850
3046
|
);
|
|
2851
3047
|
}
|
|
2852
|
-
newRecipe.
|
|
3048
|
+
newRecipe._populateIngredientQuantities();
|
|
2853
3049
|
newRecipe.servings = Big4(originalServings).times(factor).toNumber();
|
|
2854
3050
|
if (newRecipe.metadata.servings && this.metadata.servings) {
|
|
2855
3051
|
if (floatRegex.test(String(this.metadata.servings).replace(",", ".").trim())) {
|
|
@@ -2914,9 +3110,9 @@ var _Recipe = class _Recipe {
|
|
|
2914
3110
|
if (method === "remove") {
|
|
2915
3111
|
return newPrimary;
|
|
2916
3112
|
} else if (method === "replace") {
|
|
3113
|
+
if (source === "converted") remainingEquivalents.push(oldPrimary);
|
|
2917
3114
|
if (remainingEquivalents.length > 0) {
|
|
2918
3115
|
newPrimary.equivalents = remainingEquivalents;
|
|
2919
|
-
if (source === "converted") newPrimary.equivalents.push(oldPrimary);
|
|
2920
3116
|
}
|
|
2921
3117
|
} else {
|
|
2922
3118
|
newPrimary.equivalents = [oldPrimary, ...remainingEquivalents];
|
|
@@ -3034,7 +3230,7 @@ var _Recipe = class _Recipe {
|
|
|
3034
3230
|
for (const alternatives of newRecipe.choices.ingredientItems.values()) {
|
|
3035
3231
|
convertAlternatives(alternatives);
|
|
3036
3232
|
}
|
|
3037
|
-
newRecipe.
|
|
3233
|
+
newRecipe._populateIngredientQuantities();
|
|
3038
3234
|
if (method !== "keep") _Recipe.unitSystems.set(newRecipe, system);
|
|
3039
3235
|
return newRecipe;
|
|
3040
3236
|
}
|
|
@@ -3087,9 +3283,9 @@ var Recipe = _Recipe;
|
|
|
3087
3283
|
var ShoppingList = class {
|
|
3088
3284
|
/**
|
|
3089
3285
|
* Creates a new ShoppingList instance
|
|
3090
|
-
* @param
|
|
3286
|
+
* @param categoryConfigStr - The category configuration to parse.
|
|
3091
3287
|
*/
|
|
3092
|
-
constructor(
|
|
3288
|
+
constructor(categoryConfigStr) {
|
|
3093
3289
|
// TODO: backport type change
|
|
3094
3290
|
/**
|
|
3095
3291
|
* The ingredients in the shopping list.
|
|
@@ -3102,16 +3298,16 @@ var ShoppingList = class {
|
|
|
3102
3298
|
/**
|
|
3103
3299
|
* The category configuration for the shopping list.
|
|
3104
3300
|
*/
|
|
3105
|
-
__publicField(this, "
|
|
3301
|
+
__publicField(this, "categoryConfig");
|
|
3106
3302
|
/**
|
|
3107
3303
|
* The categorized ingredients in the shopping list.
|
|
3108
3304
|
*/
|
|
3109
3305
|
__publicField(this, "categories");
|
|
3110
|
-
if (
|
|
3111
|
-
this.
|
|
3306
|
+
if (categoryConfigStr) {
|
|
3307
|
+
this.setCategoryConfig(categoryConfigStr);
|
|
3112
3308
|
}
|
|
3113
3309
|
}
|
|
3114
|
-
|
|
3310
|
+
calculateIngredients() {
|
|
3115
3311
|
this.ingredients = [];
|
|
3116
3312
|
const addIngredientQuantity = (name, quantityTotal) => {
|
|
3117
3313
|
const quantityTotalExtended = extendAllUnits(quantityTotal);
|
|
@@ -3198,7 +3394,7 @@ var ShoppingList = class {
|
|
|
3198
3394
|
* @param options - Options for adding the recipe.
|
|
3199
3395
|
* @throws Error if the recipe has alternatives without corresponding choices.
|
|
3200
3396
|
*/
|
|
3201
|
-
|
|
3397
|
+
addRecipe(recipe, options = {}) {
|
|
3202
3398
|
const errorMessage = this.getUnresolvedAlternativesError(
|
|
3203
3399
|
recipe,
|
|
3204
3400
|
options.choices
|
|
@@ -3227,7 +3423,7 @@ var ShoppingList = class {
|
|
|
3227
3423
|
});
|
|
3228
3424
|
}
|
|
3229
3425
|
}
|
|
3230
|
-
this.
|
|
3426
|
+
this.calculateIngredients();
|
|
3231
3427
|
this.categorize();
|
|
3232
3428
|
}
|
|
3233
3429
|
/**
|
|
@@ -3267,15 +3463,15 @@ var ShoppingList = class {
|
|
|
3267
3463
|
}
|
|
3268
3464
|
/**
|
|
3269
3465
|
* Removes a recipe from the shopping list, then automatically
|
|
3270
|
-
* recalculates the quantities and recategorize the ingredients.
|
|
3466
|
+
* recalculates the quantities and recategorize the ingredients.
|
|
3271
3467
|
* @param index - The index of the recipe to remove.
|
|
3272
3468
|
*/
|
|
3273
|
-
|
|
3469
|
+
removeRecipe(index) {
|
|
3274
3470
|
if (index < 0 || index >= this.recipes.length) {
|
|
3275
3471
|
throw new Error("Index out of bounds");
|
|
3276
3472
|
}
|
|
3277
3473
|
this.recipes.splice(index, 1);
|
|
3278
|
-
this.
|
|
3474
|
+
this.calculateIngredients();
|
|
3279
3475
|
this.categorize();
|
|
3280
3476
|
}
|
|
3281
3477
|
/**
|
|
@@ -3283,10 +3479,10 @@ var ShoppingList = class {
|
|
|
3283
3479
|
* and automatically categorize current ingredients from the list.
|
|
3284
3480
|
* @param config - The category configuration to parse.
|
|
3285
3481
|
*/
|
|
3286
|
-
|
|
3482
|
+
setCategoryConfig(config) {
|
|
3287
3483
|
if (typeof config === "string")
|
|
3288
|
-
this.
|
|
3289
|
-
else if (config instanceof CategoryConfig) this.
|
|
3484
|
+
this.categoryConfig = new CategoryConfig(config);
|
|
3485
|
+
else if (config instanceof CategoryConfig) this.categoryConfig = config;
|
|
3290
3486
|
else throw new Error("Invalid category configuration");
|
|
3291
3487
|
this.categorize();
|
|
3292
3488
|
}
|
|
@@ -3295,17 +3491,17 @@ var ShoppingList = class {
|
|
|
3295
3491
|
* Will use the category config if any, otherwise all ingredients will be placed in the "other" category
|
|
3296
3492
|
*/
|
|
3297
3493
|
categorize() {
|
|
3298
|
-
if (!this.
|
|
3494
|
+
if (!this.categoryConfig) {
|
|
3299
3495
|
this.categories = { other: this.ingredients };
|
|
3300
3496
|
return;
|
|
3301
3497
|
}
|
|
3302
3498
|
const categories = { other: [] };
|
|
3303
|
-
for (const category of this.
|
|
3499
|
+
for (const category of this.categoryConfig.categories) {
|
|
3304
3500
|
categories[category.name] = [];
|
|
3305
3501
|
}
|
|
3306
3502
|
for (const ingredient of this.ingredients) {
|
|
3307
3503
|
let found = false;
|
|
3308
|
-
for (const category of this.
|
|
3504
|
+
for (const category of this.categoryConfig.categories) {
|
|
3309
3505
|
for (const categoryIngredient of category.ingredients) {
|
|
3310
3506
|
if (categoryIngredient.aliases.includes(ingredient.name)) {
|
|
3311
3507
|
categories[category.name].push(ingredient);
|
|
@@ -3369,7 +3565,6 @@ var ShoppingCart = class {
|
|
|
3369
3565
|
setProductCatalog(catalog) {
|
|
3370
3566
|
this.productCatalog = catalog;
|
|
3371
3567
|
}
|
|
3372
|
-
// TODO: harmonize recipe name to use underscores
|
|
3373
3568
|
/**
|
|
3374
3569
|
* Sets the shopping list to build the cart from.
|
|
3375
3570
|
* To use if a shopping list was not provided at the creation of the instance
|