@tmlmt/cooklang-parser 1.4.4 → 2.0.1
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/README.md +4 -1
- package/dist/index.cjs +249 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +105 -34
- package/dist/index.d.ts +105 -34
- package/dist/index.js +249 -94
- package/dist/index.js.map +1 -1
- package/package.json +14 -14
package/README.md
CHANGED
|
@@ -60,7 +60,10 @@ console.log(recipe.timers); // [{ duration: 15, unit: "minutes", name: undefined
|
|
|
60
60
|
|
|
61
61
|
## Future plans
|
|
62
62
|
|
|
63
|
-
I plan to further develop features depending on the needs or bugs I will encounter in using this library in a practical application.
|
|
63
|
+
I plan to further develop features depending on the needs or bugs I will encounter in using this library in a practical application. Current backlog is as follows:
|
|
64
|
+
|
|
65
|
+
- Fixing non-compliance with spec for recipe referencing by path
|
|
66
|
+
- Pantry parsing and basic functions (e.g. take pantry into account when creating a shopping list)
|
|
64
67
|
|
|
65
68
|
## Test coverage
|
|
66
69
|
|
package/dist/index.cjs
CHANGED
|
@@ -316,13 +316,14 @@ var i = (() => {
|
|
|
316
316
|
|
|
317
317
|
// src/regex.ts
|
|
318
318
|
var metadataRegex = d().literal("---").newline().startCaptureGroup().anyCharacter().zeroOrMore().optional().endGroup().newline().literal("---").dotAll().toRegExp();
|
|
319
|
-
var
|
|
320
|
-
var
|
|
321
|
-
var
|
|
319
|
+
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();
|
|
320
|
+
var nonWordChar = "\\s@#~\\[\\]{(,;:!?";
|
|
321
|
+
var multiwordIngredient = d().literal("@").startNamedGroup("mIngredientModifiers").anyOf("@\\-&?").zeroOrMore().endGroup().optional().startNamedGroup("mIngredientRecipeAnchor").literal("./").endGroup().optional().startNamedGroup("mIngredientName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\}|\\([^)]*\\))").startGroup().literal("{").startNamedGroup("mIngredientQuantityModifier").literal("=").exactly(1).endGroup().optional().startNamedGroup("mIngredientQuantity").notAnyOf("}%").oneOrMore().endGroup().optional().startGroup().literal("%").startNamedGroup("mIngredientUnit").notAnyOf("}").oneOrMore().lazy().endGroup().endGroup().optional().literal("}").endGroup().optional().startGroup().literal("(").startNamedGroup("mIngredientPreparation").notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().toRegExp();
|
|
322
|
+
var singleWordIngredient = d().literal("@").startNamedGroup("sIngredientModifiers").anyOf("@\\-&?").zeroOrMore().endGroup().optional().startNamedGroup("sIngredientRecipeAnchor").literal("./").endGroup().optional().startNamedGroup("sIngredientName").notAnyOf(nonWordChar).oneOrMore().endGroup().startGroup().literal("{").startNamedGroup("sIngredientQuantityModifier").literal("=").exactly(1).endGroup().optional().startNamedGroup("sIngredientQuantity").notAnyOf("}%").oneOrMore().endGroup().optional().startGroup().literal("%").startNamedGroup("sIngredientUnit").notAnyOf("}").oneOrMore().lazy().endGroup().endGroup().optional().literal("}").endGroup().optional().startGroup().literal("(").startNamedGroup("sIngredientPreparation").notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().toRegExp();
|
|
322
323
|
var ingredientAliasRegex = d().startAnchor().startNamedGroup("ingredientListName").notAnyOf("|").oneOrMore().endGroup().literal("|").startNamedGroup("ingredientDisplayName").notAnyOf("|").oneOrMore().endGroup().endAnchor().toRegExp();
|
|
323
|
-
var multiwordCookware = d().literal("#").startNamedGroup("
|
|
324
|
-
var singleWordCookware = d().literal("#").startNamedGroup("
|
|
325
|
-
var timer = d().literal("~").startNamedGroup("timerName").anyCharacter().zeroOrMore().lazy().endGroup().literal("{").startNamedGroup("timerQuantity").anyCharacter().oneOrMore().lazy().endGroup().startGroup().literal("%").startNamedGroup("
|
|
324
|
+
var multiwordCookware = d().literal("#").startNamedGroup("mCookwareModifiers").anyOf("\\-&?").zeroOrMore().endGroup().startNamedGroup("mCookwareName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\})").literal("{").startNamedGroup("mCookwareQuantity").anyCharacter().zeroOrMore().lazy().endGroup().literal("}").toRegExp();
|
|
325
|
+
var singleWordCookware = d().literal("#").startNamedGroup("sCookwareModifiers").anyOf("\\-&?").zeroOrMore().endGroup().startNamedGroup("sCookwareName").notAnyOf(nonWordChar).oneOrMore().endGroup().startGroup().literal("{").startNamedGroup("sCookwareQuantity").anyCharacter().zeroOrMore().lazy().endGroup().literal("}").endGroup().optional().toRegExp();
|
|
326
|
+
var timer = 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();
|
|
326
327
|
var tokensRegex = new RegExp(
|
|
327
328
|
[
|
|
328
329
|
multiwordIngredient,
|
|
@@ -338,6 +339,7 @@ var blockCommentRegex = d().whitespace().zeroOrMore().literal("[-").anyCharacter
|
|
|
338
339
|
var shoppingListRegex = d().literal("[").startNamedGroup("name").anyCharacter().oneOrMore().endGroup().literal("]").newline().startNamedGroup("items").anyCharacter().zeroOrMore().lazy().endGroup().startGroup().newline().newline().or().endAnchor().endGroup().global().toRegExp();
|
|
339
340
|
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();
|
|
340
341
|
var numberLikeRegex = d().startAnchor().digit().oneOrMore().startGroup().anyOf(".,/").exactly(1).digit().oneOrMore().endGroup().optional().endAnchor().toRegExp();
|
|
342
|
+
var floatRegex = d().startAnchor().digit().oneOrMore().startGroup().anyOf(".").exactly(1).digit().oneOrMore().endGroup().optional().endAnchor().toRegExp();
|
|
341
343
|
|
|
342
344
|
// src/units.ts
|
|
343
345
|
var units = [
|
|
@@ -452,7 +454,7 @@ for (const unit of units) {
|
|
|
452
454
|
unitMap.set(alias.toLowerCase(), unit);
|
|
453
455
|
}
|
|
454
456
|
}
|
|
455
|
-
function normalizeUnit(unit) {
|
|
457
|
+
function normalizeUnit(unit = "") {
|
|
456
458
|
return unitMap.get(unit.toLowerCase().trim());
|
|
457
459
|
}
|
|
458
460
|
var CannotAddTextValueError = class extends Error {
|
|
@@ -514,7 +516,10 @@ function addNumericValues(val1, val2) {
|
|
|
514
516
|
num2 = val2.num;
|
|
515
517
|
den2 = val2.den;
|
|
516
518
|
}
|
|
517
|
-
if (
|
|
519
|
+
if (num1 === 0 && num2 === 0) {
|
|
520
|
+
return { type: "decimal", value: 0 };
|
|
521
|
+
}
|
|
522
|
+
if (val1.type === "fraction" && val2.type === "fraction" || val1.type === "fraction" && val2.type === "decimal" && val2.value === 0 || val2.type === "fraction" && val1.type === "decimal" && val1.value === 0) {
|
|
518
523
|
const commonDen = den1 * den2;
|
|
519
524
|
const sumNum = num1 * den2 + num2 * den1;
|
|
520
525
|
return simplifyFraction(sumNum, commonDen);
|
|
@@ -555,6 +560,32 @@ var convertQuantityValue = (value, def, targetDef) => {
|
|
|
555
560
|
const factor = def.toBase / targetDef.toBase;
|
|
556
561
|
return multiplyQuantityValue(value, factor);
|
|
557
562
|
};
|
|
563
|
+
function getDefaultQuantityValue() {
|
|
564
|
+
return { type: "fixed", value: { type: "decimal", value: 0 } };
|
|
565
|
+
}
|
|
566
|
+
function addQuantityValues(v1, v2) {
|
|
567
|
+
if (v1.type === "fixed" && v1.value.type === "text" || v2.type === "fixed" && v2.value.type === "text") {
|
|
568
|
+
throw new CannotAddTextValueError();
|
|
569
|
+
}
|
|
570
|
+
if (v1.type === "fixed" && v2.type === "fixed") {
|
|
571
|
+
const res = addNumericValues(
|
|
572
|
+
v1.value,
|
|
573
|
+
v2.value
|
|
574
|
+
);
|
|
575
|
+
return { type: "fixed", value: res };
|
|
576
|
+
}
|
|
577
|
+
const r1 = v1.type === "range" ? v1 : { type: "range", min: v1.value, max: v1.value };
|
|
578
|
+
const r2 = v2.type === "range" ? v2 : { type: "range", min: v2.value, max: v2.value };
|
|
579
|
+
const newMin = addNumericValues(
|
|
580
|
+
r1.min,
|
|
581
|
+
r2.min
|
|
582
|
+
);
|
|
583
|
+
const newMax = addNumericValues(
|
|
584
|
+
r1.max,
|
|
585
|
+
r2.max
|
|
586
|
+
);
|
|
587
|
+
return { type: "range", min: newMin, max: newMax };
|
|
588
|
+
}
|
|
558
589
|
function addQuantities(q1, q2) {
|
|
559
590
|
const v1 = q1.value;
|
|
560
591
|
const v2 = q2.value;
|
|
@@ -563,33 +594,14 @@ function addQuantities(q1, q2) {
|
|
|
563
594
|
}
|
|
564
595
|
const unit1Def = normalizeUnit(q1.unit);
|
|
565
596
|
const unit2Def = normalizeUnit(q2.unit);
|
|
566
|
-
const addQuantityValuesAndSetUnit = (val1, val2, unit) => {
|
|
567
|
-
|
|
568
|
-
const res = addNumericValues(
|
|
569
|
-
val1.value,
|
|
570
|
-
val2.value
|
|
571
|
-
);
|
|
572
|
-
return { value: { type: "fixed", value: res }, unit };
|
|
573
|
-
}
|
|
574
|
-
const r1 = val1.type === "range" ? val1 : { type: "range", min: val1.value, max: val1.value };
|
|
575
|
-
const r2 = val2.type === "range" ? val2 : { type: "range", min: val2.value, max: val2.value };
|
|
576
|
-
const newMin = addNumericValues(
|
|
577
|
-
r1.min,
|
|
578
|
-
r2.min
|
|
579
|
-
);
|
|
580
|
-
const newMax = addNumericValues(
|
|
581
|
-
r1.max,
|
|
582
|
-
r2.max
|
|
583
|
-
);
|
|
584
|
-
return { value: { type: "range", min: newMin, max: newMax }, unit };
|
|
585
|
-
};
|
|
586
|
-
if (q1.unit === "" && unit2Def) {
|
|
597
|
+
const addQuantityValuesAndSetUnit = (val1, val2, unit) => ({ value: addQuantityValues(val1, val2), unit });
|
|
598
|
+
if ((q1.unit === "" || q1.unit === void 0) && q2.unit !== void 0) {
|
|
587
599
|
return addQuantityValuesAndSetUnit(v1, v2, q2.unit);
|
|
588
600
|
}
|
|
589
|
-
if (q2.unit === "" &&
|
|
601
|
+
if ((q2.unit === "" || q2.unit === void 0) && q1.unit !== void 0) {
|
|
590
602
|
return addQuantityValuesAndSetUnit(v1, v2, q1.unit);
|
|
591
603
|
}
|
|
592
|
-
if (q1.unit.toLowerCase() === q2.unit.toLowerCase()) {
|
|
604
|
+
if (!q1.unit && !q2.unit || q1.unit && q2.unit && q1.unit.toLowerCase() === q2.unit.toLowerCase()) {
|
|
593
605
|
return addQuantityValuesAndSetUnit(v1, v2, q1.unit);
|
|
594
606
|
}
|
|
595
607
|
if (unit1Def && unit2Def) {
|
|
@@ -619,6 +631,17 @@ function addQuantities(q1, q2) {
|
|
|
619
631
|
throw new IncompatibleUnitsError(q1.unit, q2.unit);
|
|
620
632
|
}
|
|
621
633
|
|
|
634
|
+
// src/errors.ts
|
|
635
|
+
var ReferencedItemCannotBeRedefinedError = class extends Error {
|
|
636
|
+
constructor(item_type, item_name, new_modifier) {
|
|
637
|
+
super(
|
|
638
|
+
`The referenced ${item_type} "${item_name}" cannot be redefined as ${new_modifier}.
|
|
639
|
+
You can either remove the reference to create a new ${item_type} defined as ${new_modifier} or add the ${new_modifier} flag to the original definition of the ${item_type}`
|
|
640
|
+
);
|
|
641
|
+
this.name = "ReferencedItemCannotBeRedefinedError";
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
|
|
622
645
|
// src/parser_helpers.ts
|
|
623
646
|
function flushPendingNote(section, note) {
|
|
624
647
|
if (note.length > 0) {
|
|
@@ -638,21 +661,28 @@ function flushPendingItems(section, items) {
|
|
|
638
661
|
function findAndUpsertIngredient(ingredients, newIngredient, isReference) {
|
|
639
662
|
const { name, quantity, unit } = newIngredient;
|
|
640
663
|
if (isReference) {
|
|
641
|
-
const
|
|
664
|
+
const indexFind = ingredients.findIndex(
|
|
642
665
|
(i2) => i2.name.toLowerCase() === name.toLowerCase()
|
|
643
666
|
);
|
|
644
|
-
if (
|
|
667
|
+
if (indexFind === -1) {
|
|
645
668
|
throw new Error(
|
|
646
669
|
`Referenced ingredient "${name}" not found. A referenced ingredient must be declared before being referenced with '&'.`
|
|
647
670
|
);
|
|
648
671
|
}
|
|
649
|
-
const existingIngredient = ingredients[
|
|
672
|
+
const existingIngredient = ingredients[indexFind];
|
|
673
|
+
for (const flag of newIngredient.flags) {
|
|
674
|
+
if (!existingIngredient.flags.includes(flag)) {
|
|
675
|
+
throw new ReferencedItemCannotBeRedefinedError(
|
|
676
|
+
"ingredient",
|
|
677
|
+
existingIngredient.name,
|
|
678
|
+
flag
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
let quantityPartIndex = void 0;
|
|
650
683
|
if (quantity !== void 0) {
|
|
651
684
|
const currentQuantity = {
|
|
652
|
-
value: existingIngredient.quantity ??
|
|
653
|
-
type: "fixed",
|
|
654
|
-
value: { type: "decimal", value: 0 }
|
|
655
|
-
},
|
|
685
|
+
value: existingIngredient.quantity ?? getDefaultQuantityValue(),
|
|
656
686
|
unit: existingIngredient.unit ?? ""
|
|
657
687
|
};
|
|
658
688
|
const newQuantity = { value: quantity, unit: unit ?? "" };
|
|
@@ -660,18 +690,35 @@ function findAndUpsertIngredient(ingredients, newIngredient, isReference) {
|
|
|
660
690
|
const total = addQuantities(currentQuantity, newQuantity);
|
|
661
691
|
existingIngredient.quantity = total.value;
|
|
662
692
|
existingIngredient.unit = total.unit || void 0;
|
|
693
|
+
if (existingIngredient.quantityParts) {
|
|
694
|
+
existingIngredient.quantityParts.push(
|
|
695
|
+
...newIngredient.quantityParts
|
|
696
|
+
);
|
|
697
|
+
} else {
|
|
698
|
+
existingIngredient.quantityParts = newIngredient.quantityParts;
|
|
699
|
+
}
|
|
700
|
+
quantityPartIndex = existingIngredient.quantityParts.length - 1;
|
|
663
701
|
} catch (e2) {
|
|
664
702
|
if (e2 instanceof IncompatibleUnitsError || e2 instanceof CannotAddTextValueError) {
|
|
665
|
-
return
|
|
703
|
+
return {
|
|
704
|
+
ingredientIndex: ingredients.push(newIngredient) - 1,
|
|
705
|
+
quantityPartIndex: 0
|
|
706
|
+
};
|
|
666
707
|
}
|
|
667
708
|
}
|
|
668
709
|
}
|
|
669
|
-
return
|
|
710
|
+
return {
|
|
711
|
+
ingredientIndex: indexFind,
|
|
712
|
+
quantityPartIndex
|
|
713
|
+
};
|
|
670
714
|
}
|
|
671
|
-
return
|
|
715
|
+
return {
|
|
716
|
+
ingredientIndex: ingredients.push(newIngredient) - 1,
|
|
717
|
+
quantityPartIndex: 0
|
|
718
|
+
};
|
|
672
719
|
}
|
|
673
720
|
function findAndUpsertCookware(cookware, newCookware, isReference) {
|
|
674
|
-
const { name } = newCookware;
|
|
721
|
+
const { name, quantity } = newCookware;
|
|
675
722
|
if (isReference) {
|
|
676
723
|
const index = cookware.findIndex(
|
|
677
724
|
(i2) => i2.name.toLowerCase() === name.toLowerCase()
|
|
@@ -681,9 +728,55 @@ function findAndUpsertCookware(cookware, newCookware, isReference) {
|
|
|
681
728
|
`Referenced cookware "${name}" not found. A referenced cookware must be declared before being referenced with '&'.`
|
|
682
729
|
);
|
|
683
730
|
}
|
|
684
|
-
|
|
731
|
+
const existingCookware = cookware[index];
|
|
732
|
+
for (const flag of newCookware.flags) {
|
|
733
|
+
if (!existingCookware.flags.includes(flag)) {
|
|
734
|
+
throw new ReferencedItemCannotBeRedefinedError(
|
|
735
|
+
"cookware",
|
|
736
|
+
existingCookware.name,
|
|
737
|
+
flag
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
let quantityPartIndex = void 0;
|
|
742
|
+
if (quantity !== void 0) {
|
|
743
|
+
if (!existingCookware.quantity) {
|
|
744
|
+
existingCookware.quantity = quantity;
|
|
745
|
+
existingCookware.quantityParts = newCookware.quantityParts;
|
|
746
|
+
quantityPartIndex = 0;
|
|
747
|
+
} else {
|
|
748
|
+
try {
|
|
749
|
+
existingCookware.quantity = addQuantityValues(
|
|
750
|
+
existingCookware.quantity,
|
|
751
|
+
quantity
|
|
752
|
+
);
|
|
753
|
+
if (!existingCookware.quantityParts) {
|
|
754
|
+
existingCookware.quantityParts = newCookware.quantityParts;
|
|
755
|
+
quantityPartIndex = 0;
|
|
756
|
+
} else {
|
|
757
|
+
quantityPartIndex = existingCookware.quantityParts.push(
|
|
758
|
+
...newCookware.quantityParts
|
|
759
|
+
) - 1;
|
|
760
|
+
}
|
|
761
|
+
} catch (e2) {
|
|
762
|
+
if (e2 instanceof CannotAddTextValueError) {
|
|
763
|
+
return {
|
|
764
|
+
cookwareIndex: cookware.push(newCookware) - 1,
|
|
765
|
+
quantityPartIndex: 0
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return {
|
|
772
|
+
cookwareIndex: index,
|
|
773
|
+
quantityPartIndex
|
|
774
|
+
};
|
|
685
775
|
}
|
|
686
|
-
return
|
|
776
|
+
return {
|
|
777
|
+
cookwareIndex: cookware.push(newCookware) - 1,
|
|
778
|
+
quantityPartIndex: quantity ? 0 : void 0
|
|
779
|
+
};
|
|
687
780
|
}
|
|
688
781
|
var parseFixedValue = (input_str) => {
|
|
689
782
|
if (!numberLikeRegex.test(input_str)) {
|
|
@@ -715,9 +808,7 @@ function parseSimpleMetaVar(content, varName) {
|
|
|
715
808
|
return varMatch ? varMatch[1]?.trim().replace(/\s*\r?\n\s+/g, " ") : void 0;
|
|
716
809
|
}
|
|
717
810
|
function parseScalingMetaVar(content, varName) {
|
|
718
|
-
const varMatch = content.match(
|
|
719
|
-
new RegExp(`^${varName}:[\\t ]*(([^,\\n]*),? ?(?:.*)?)`, "m")
|
|
720
|
-
);
|
|
811
|
+
const varMatch = content.match(scalingMetaValueRegex(varName));
|
|
721
812
|
if (!varMatch) return void 0;
|
|
722
813
|
if (isNaN(Number(varMatch[2]?.trim()))) {
|
|
723
814
|
throw new Error("Scaling variables should be numbers");
|
|
@@ -773,7 +864,7 @@ function extractMetadata(content) {
|
|
|
773
864
|
const stringMetaValue = parseSimpleMetaVar(metadataContent, metaVar);
|
|
774
865
|
if (stringMetaValue) metadata[metaVar] = stringMetaValue;
|
|
775
866
|
}
|
|
776
|
-
for (const metaVar of ["
|
|
867
|
+
for (const metaVar of ["serves", "yield", "servings"]) {
|
|
777
868
|
const scalingMetaValue = parseScalingMetaVar(metadataContent, metaVar);
|
|
778
869
|
if (scalingMetaValue && scalingMetaValue[1]) {
|
|
779
870
|
metadata[metaVar] = scalingMetaValue[1];
|
|
@@ -889,15 +980,28 @@ var Recipe = class _Recipe {
|
|
|
889
980
|
}
|
|
890
981
|
const groups = match.groups;
|
|
891
982
|
if (groups.mIngredientName || groups.sIngredientName) {
|
|
892
|
-
|
|
983
|
+
let name = groups.mIngredientName || groups.sIngredientName;
|
|
984
|
+
const scalableQuantity = (groups.mIngredientQuantityModifier || groups.sIngredientQuantityModifier) !== "=";
|
|
893
985
|
const quantityRaw = groups.mIngredientQuantity || groups.sIngredientQuantity;
|
|
894
|
-
const
|
|
986
|
+
const unit = groups.mIngredientUnit || groups.sIngredientUnit;
|
|
895
987
|
const preparation = groups.mIngredientPreparation || groups.sIngredientPreparation;
|
|
896
|
-
const
|
|
897
|
-
const
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
988
|
+
const modifiers = groups.mIngredientModifiers || groups.sIngredientModifiers;
|
|
989
|
+
const reference = modifiers !== void 0 && modifiers.includes("&");
|
|
990
|
+
const flags = [];
|
|
991
|
+
if (modifiers !== void 0 && modifiers.includes("?")) {
|
|
992
|
+
flags.push("optional");
|
|
993
|
+
}
|
|
994
|
+
if (modifiers !== void 0 && modifiers.includes("-")) {
|
|
995
|
+
flags.push("hidden");
|
|
996
|
+
}
|
|
997
|
+
if (modifiers !== void 0 && modifiers.includes("@") || groups.mIngredientRecipeAnchor || groups.sIngredientRecipeAnchor) {
|
|
998
|
+
flags.push("recipe");
|
|
999
|
+
}
|
|
1000
|
+
let extras = void 0;
|
|
1001
|
+
if (flags.includes("recipe")) {
|
|
1002
|
+
extras = { path: `${name}.cook` };
|
|
1003
|
+
name = name.substring(name.lastIndexOf("/") + 1);
|
|
1004
|
+
}
|
|
901
1005
|
const quantity = quantityRaw ? parseQuantityInput(quantityRaw) : void 0;
|
|
902
1006
|
const aliasMatch = name.match(ingredientAliasRegex);
|
|
903
1007
|
let listName, displayName;
|
|
@@ -908,50 +1012,70 @@ var Recipe = class _Recipe {
|
|
|
908
1012
|
listName = name;
|
|
909
1013
|
displayName = name;
|
|
910
1014
|
}
|
|
911
|
-
const
|
|
1015
|
+
const newIngredient = {
|
|
1016
|
+
name: listName,
|
|
1017
|
+
quantity,
|
|
1018
|
+
quantityParts: quantity ? [
|
|
1019
|
+
{
|
|
1020
|
+
value: quantity,
|
|
1021
|
+
unit,
|
|
1022
|
+
scalable: scalableQuantity
|
|
1023
|
+
}
|
|
1024
|
+
] : void 0,
|
|
1025
|
+
unit,
|
|
1026
|
+
preparation,
|
|
1027
|
+
flags
|
|
1028
|
+
};
|
|
1029
|
+
if (extras) {
|
|
1030
|
+
newIngredient.extras = extras;
|
|
1031
|
+
}
|
|
1032
|
+
const idxsInList = findAndUpsertIngredient(
|
|
912
1033
|
this.ingredients,
|
|
913
|
-
|
|
914
|
-
name: listName,
|
|
915
|
-
quantity,
|
|
916
|
-
unit: units2,
|
|
917
|
-
optional,
|
|
918
|
-
hidden,
|
|
919
|
-
preparation,
|
|
920
|
-
isRecipe
|
|
921
|
-
},
|
|
1034
|
+
newIngredient,
|
|
922
1035
|
reference
|
|
923
1036
|
);
|
|
924
1037
|
const newItem = {
|
|
925
1038
|
type: "ingredient",
|
|
926
|
-
|
|
927
|
-
itemQuantity: quantity,
|
|
928
|
-
itemUnit: units2,
|
|
1039
|
+
index: idxsInList.ingredientIndex,
|
|
929
1040
|
displayName
|
|
930
1041
|
};
|
|
1042
|
+
if (idxsInList.quantityPartIndex !== void 0) {
|
|
1043
|
+
newItem.quantityPartIndex = idxsInList.quantityPartIndex;
|
|
1044
|
+
}
|
|
931
1045
|
items.push(newItem);
|
|
932
1046
|
} else if (groups.mCookwareName || groups.sCookwareName) {
|
|
933
1047
|
const name = groups.mCookwareName || groups.sCookwareName;
|
|
934
|
-
const
|
|
1048
|
+
const modifiers = groups.mCookwareModifiers || groups.sCookwareModifiers;
|
|
935
1049
|
const quantityRaw = groups.mCookwareQuantity || groups.sCookwareQuantity;
|
|
936
|
-
const
|
|
937
|
-
const
|
|
938
|
-
|
|
1050
|
+
const reference = modifiers !== void 0 && modifiers.includes("&");
|
|
1051
|
+
const flags = [];
|
|
1052
|
+
if (modifiers !== void 0 && modifiers.includes("?")) {
|
|
1053
|
+
flags.push("optional");
|
|
1054
|
+
}
|
|
1055
|
+
if (modifiers !== void 0 && modifiers.includes("-")) {
|
|
1056
|
+
flags.push("hidden");
|
|
1057
|
+
}
|
|
939
1058
|
const quantity = quantityRaw ? parseQuantityInput(quantityRaw) : void 0;
|
|
940
|
-
const
|
|
1059
|
+
const idxsInList = findAndUpsertCookware(
|
|
941
1060
|
this.cookware,
|
|
942
|
-
{
|
|
1061
|
+
{
|
|
1062
|
+
name,
|
|
1063
|
+
quantity,
|
|
1064
|
+
quantityParts: quantity ? [quantity] : void 0,
|
|
1065
|
+
flags
|
|
1066
|
+
},
|
|
943
1067
|
reference
|
|
944
1068
|
);
|
|
945
1069
|
items.push({
|
|
946
1070
|
type: "cookware",
|
|
947
|
-
|
|
948
|
-
|
|
1071
|
+
index: idxsInList.cookwareIndex,
|
|
1072
|
+
quantityPartIndex: idxsInList.quantityPartIndex
|
|
949
1073
|
});
|
|
950
|
-
} else
|
|
1074
|
+
} else {
|
|
951
1075
|
const durationStr = groups.timerQuantity.trim();
|
|
952
|
-
const unit = (groups.
|
|
1076
|
+
const unit = (groups.timerUnit || "").trim();
|
|
953
1077
|
if (!unit) {
|
|
954
|
-
throw new Error("Timer missing
|
|
1078
|
+
throw new Error("Timer missing unit");
|
|
955
1079
|
}
|
|
956
1080
|
const name = groups.timerName || void 0;
|
|
957
1081
|
const duration = parseQuantityInput(durationStr);
|
|
@@ -960,7 +1084,7 @@ var Recipe = class _Recipe {
|
|
|
960
1084
|
duration,
|
|
961
1085
|
unit
|
|
962
1086
|
};
|
|
963
|
-
items.push({ type: "timer",
|
|
1087
|
+
items.push({ type: "timer", index: this.timers.push(timerObj) - 1 });
|
|
964
1088
|
}
|
|
965
1089
|
cursor = idx + match[0].length;
|
|
966
1090
|
}
|
|
@@ -1003,30 +1127,57 @@ var Recipe = class _Recipe {
|
|
|
1003
1127
|
throw new Error("Error scaling recipe: no initial servings value set");
|
|
1004
1128
|
}
|
|
1005
1129
|
newRecipe.ingredients = newRecipe.ingredients.map((ingredient) => {
|
|
1006
|
-
if (ingredient.
|
|
1007
|
-
ingredient.
|
|
1008
|
-
|
|
1009
|
-
|
|
1130
|
+
if (ingredient.quantityParts) {
|
|
1131
|
+
ingredient.quantityParts = ingredient.quantityParts.map(
|
|
1132
|
+
(quantityPart) => {
|
|
1133
|
+
if (quantityPart.value.type === "fixed" && quantityPart.value.value.type === "text") {
|
|
1134
|
+
return quantityPart;
|
|
1135
|
+
}
|
|
1136
|
+
return {
|
|
1137
|
+
...quantityPart,
|
|
1138
|
+
value: multiplyQuantityValue(
|
|
1139
|
+
quantityPart.value,
|
|
1140
|
+
quantityPart.scalable ? factor : 1
|
|
1141
|
+
)
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1010
1144
|
);
|
|
1145
|
+
if (ingredient.quantityParts.length === 1) {
|
|
1146
|
+
ingredient.quantity = ingredient.quantityParts[0].value;
|
|
1147
|
+
ingredient.unit = ingredient.quantityParts[0].unit;
|
|
1148
|
+
} else {
|
|
1149
|
+
const totalQuantity = ingredient.quantityParts.reduce(
|
|
1150
|
+
(acc, val) => addQuantities(acc, { value: val.value, unit: val.unit }),
|
|
1151
|
+
{ value: getDefaultQuantityValue() }
|
|
1152
|
+
);
|
|
1153
|
+
ingredient.quantity = totalQuantity.value;
|
|
1154
|
+
ingredient.unit = totalQuantity.unit;
|
|
1155
|
+
}
|
|
1011
1156
|
}
|
|
1012
1157
|
return ingredient;
|
|
1013
1158
|
}).filter((ingredient) => ingredient.quantity !== null);
|
|
1014
1159
|
newRecipe.servings = originalServings * factor;
|
|
1015
1160
|
if (newRecipe.metadata.servings && this.metadata.servings) {
|
|
1016
|
-
|
|
1017
|
-
|
|
1161
|
+
if (floatRegex.test(String(this.metadata.servings).replace(",", ".").trim())) {
|
|
1162
|
+
const servingsValue = parseFloat(
|
|
1163
|
+
String(this.metadata.servings).replace(",", ".")
|
|
1164
|
+
);
|
|
1018
1165
|
newRecipe.metadata.servings = String(servingsValue * factor);
|
|
1019
1166
|
}
|
|
1020
1167
|
}
|
|
1021
1168
|
if (newRecipe.metadata.yield && this.metadata.yield) {
|
|
1022
|
-
|
|
1023
|
-
|
|
1169
|
+
if (floatRegex.test(String(this.metadata.yield).replace(",", ".").trim())) {
|
|
1170
|
+
const yieldValue = parseFloat(
|
|
1171
|
+
String(this.metadata.yield).replace(",", ".")
|
|
1172
|
+
);
|
|
1024
1173
|
newRecipe.metadata.yield = String(yieldValue * factor);
|
|
1025
1174
|
}
|
|
1026
1175
|
}
|
|
1027
1176
|
if (newRecipe.metadata.serves && this.metadata.serves) {
|
|
1028
|
-
|
|
1029
|
-
|
|
1177
|
+
if (floatRegex.test(String(this.metadata.serves).replace(",", ".").trim())) {
|
|
1178
|
+
const servesValue = parseFloat(
|
|
1179
|
+
String(this.metadata.serves).replace(",", ".")
|
|
1180
|
+
);
|
|
1030
1181
|
newRecipe.metadata.serves = String(servesValue * factor);
|
|
1031
1182
|
}
|
|
1032
1183
|
}
|
|
@@ -1095,7 +1246,7 @@ var ShoppingList = class {
|
|
|
1095
1246
|
for (const { recipe, factor } of this.recipes) {
|
|
1096
1247
|
const scaledRecipe = factor === 1 ? recipe : recipe.scaleBy(factor);
|
|
1097
1248
|
for (const ingredient of scaledRecipe.ingredients) {
|
|
1098
|
-
if (ingredient.hidden) {
|
|
1249
|
+
if (ingredient.flags && ingredient.flags.includes("hidden")) {
|
|
1099
1250
|
continue;
|
|
1100
1251
|
}
|
|
1101
1252
|
const existingIngredient = this.ingredients.find(
|
|
@@ -1103,8 +1254,8 @@ var ShoppingList = class {
|
|
|
1103
1254
|
);
|
|
1104
1255
|
let addSeparate = false;
|
|
1105
1256
|
try {
|
|
1106
|
-
if (existingIngredient) {
|
|
1107
|
-
if (existingIngredient.quantity
|
|
1257
|
+
if (existingIngredient && ingredient.quantity) {
|
|
1258
|
+
if (existingIngredient.quantity) {
|
|
1108
1259
|
const newQuantity = addQuantities(
|
|
1109
1260
|
{
|
|
1110
1261
|
value: existingIngredient.quantity,
|
|
@@ -1119,7 +1270,7 @@ var ShoppingList = class {
|
|
|
1119
1270
|
if (newQuantity.unit) {
|
|
1120
1271
|
existingIngredient.unit = newQuantity.unit;
|
|
1121
1272
|
}
|
|
1122
|
-
} else
|
|
1273
|
+
} else {
|
|
1123
1274
|
existingIngredient.quantity = ingredient.quantity;
|
|
1124
1275
|
if (ingredient.unit) {
|
|
1125
1276
|
existingIngredient.unit = ingredient.unit;
|
|
@@ -1219,4 +1370,8 @@ var ShoppingList = class {
|
|
|
1219
1370
|
Section,
|
|
1220
1371
|
ShoppingList
|
|
1221
1372
|
});
|
|
1373
|
+
/* v8 ignore else -- @preserve */
|
|
1374
|
+
/* v8 ignore else -- expliciting error types -- @preserve */
|
|
1375
|
+
/* v8 ignore else -- expliciting error type -- @preserve */
|
|
1376
|
+
/* v8 ignore else -- only set unit if it is given -- @preserve */
|
|
1222
1377
|
//# sourceMappingURL=index.cjs.map
|