@tmlmt/cooklang-parser 3.0.0-alpha.4 → 3.0.0-alpha.5
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 +120 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -18
- package/dist/index.d.ts +56 -18
- package/dist/index.js +120 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -86,6 +86,10 @@ declare class Recipe {
|
|
|
86
86
|
* The parsed recipe timers.
|
|
87
87
|
*/
|
|
88
88
|
timers: Timer[];
|
|
89
|
+
/**
|
|
90
|
+
* The parsed arbitrary quantities.
|
|
91
|
+
*/
|
|
92
|
+
arbitraries: ArbitraryScalable[];
|
|
89
93
|
/**
|
|
90
94
|
* The parsed recipe servings. Used for scaling. Parsed from one of
|
|
91
95
|
* {@link Metadata.servings}, {@link Metadata.yield} or {@link Metadata.serves}
|
|
@@ -112,6 +116,19 @@ declare class Recipe {
|
|
|
112
116
|
* @param content - The recipe content to parse.
|
|
113
117
|
*/
|
|
114
118
|
constructor(content?: string);
|
|
119
|
+
/**
|
|
120
|
+
* Parses a matched arbitrary scalable quantity and adds it to the given array.
|
|
121
|
+
* @private
|
|
122
|
+
* @param regexMatchGroups - The regex match groups from arbitrary scalable regex.
|
|
123
|
+
* @param intoArray - The array to push the parsed arbitrary scalable item into.
|
|
124
|
+
*/
|
|
125
|
+
private _parseArbitraryScalable;
|
|
126
|
+
/**
|
|
127
|
+
* Parses text for arbitrary scalables and returns NoteItem array.
|
|
128
|
+
* @param text - The text to parse for arbitrary scalables.
|
|
129
|
+
* @returns Array of NoteItem (text and arbitrary scalable items).
|
|
130
|
+
*/
|
|
131
|
+
private _parseNoteText;
|
|
115
132
|
private _parseQuantityRecursive;
|
|
116
133
|
private _parseIngredientWithAlternativeRecursive;
|
|
117
134
|
private _parseIngredientWithGroupKey;
|
|
@@ -371,7 +388,7 @@ interface AlternativeIngredientRef {
|
|
|
371
388
|
/** The index of the alternative ingredient within the {@link Recipe.ingredients} array. */
|
|
372
389
|
index: number;
|
|
373
390
|
/** The quantities of the alternative ingredient. Multiple entries when units are incompatible. */
|
|
374
|
-
|
|
391
|
+
quantities?: QuantityWithPlainUnit[];
|
|
375
392
|
}
|
|
376
393
|
/**
|
|
377
394
|
* Represents a group of summed quantities for an ingredient, optionally with alternatives.
|
|
@@ -379,18 +396,12 @@ interface AlternativeIngredientRef {
|
|
|
379
396
|
* When units are incompatible, separate IngredientQuantityGroup entries are created instead of merging.
|
|
380
397
|
* @category Types
|
|
381
398
|
*/
|
|
382
|
-
interface IngredientQuantityGroup {
|
|
399
|
+
interface IngredientQuantityGroup extends QuantityWithPlainUnit {
|
|
383
400
|
/**
|
|
384
401
|
* References to alternative ingredients for this quantity group.
|
|
385
402
|
* If undefined, this group has no alternatives.
|
|
386
403
|
*/
|
|
387
404
|
alternatives?: AlternativeIngredientRef[];
|
|
388
|
-
/**
|
|
389
|
-
* The summed quantity for this group, potentially with equivalents.
|
|
390
|
-
* OR groups from addEquivalentsAndSimplify are converted back to QuantityWithPlainUnit
|
|
391
|
-
* (first entry as main, rest as equivalents).
|
|
392
|
-
*/
|
|
393
|
-
groupQuantity: QuantityWithPlainUnit;
|
|
394
405
|
}
|
|
395
406
|
/**
|
|
396
407
|
* Represents an AND group of quantities when primary units are incompatible but equivalents can be summed.
|
|
@@ -525,12 +536,12 @@ interface IngredientItem {
|
|
|
525
536
|
* @category Types
|
|
526
537
|
*/
|
|
527
538
|
interface RecipeAlternatives {
|
|
528
|
-
/** Map of choices that can be made at Ingredient
|
|
529
|
-
* - Keys are the Ingredient
|
|
539
|
+
/** Map of choices that can be made at Ingredient StepItem level
|
|
540
|
+
* - Keys are the Ingredient StepItem IDs (e.g. "ingredient-item-2")
|
|
530
541
|
* - Values are arrays of IngredientAlternative objects representing the choices available for that item
|
|
531
542
|
*/
|
|
532
543
|
ingredientItems: Map<string, IngredientAlternative[]>;
|
|
533
|
-
/** Map of choices that can be made for Grouped Ingredient
|
|
544
|
+
/** Map of choices that can be made for Grouped Ingredient StepItem's
|
|
534
545
|
* - Keys are the Group IDs (e.g. "eggs" for `@|eggs|...`)
|
|
535
546
|
* - Values are arrays of IngredientAlternative objects representing the choices available for that group
|
|
536
547
|
*/
|
|
@@ -542,9 +553,9 @@ interface RecipeAlternatives {
|
|
|
542
553
|
* @category Types
|
|
543
554
|
*/
|
|
544
555
|
interface RecipeChoices {
|
|
545
|
-
/** Map of choices that can be made at Ingredient
|
|
556
|
+
/** Map of choices that can be made at Ingredient StepItem level */
|
|
546
557
|
ingredientItems?: Map<string, number>;
|
|
547
|
-
/** Map of choices that can be made for Grouped Ingredient
|
|
558
|
+
/** Map of choices that can be made for Grouped Ingredient StepItem's */
|
|
548
559
|
ingredientGroups?: Map<string, number>;
|
|
549
560
|
}
|
|
550
561
|
/**
|
|
@@ -591,11 +602,33 @@ interface TextItem {
|
|
|
591
602
|
/** The content of the text item. */
|
|
592
603
|
value: string;
|
|
593
604
|
}
|
|
605
|
+
/**
|
|
606
|
+
* Represents an arbitrary scalable quantity in a recipe.
|
|
607
|
+
* @category Types
|
|
608
|
+
*/
|
|
609
|
+
interface ArbitraryScalable {
|
|
610
|
+
/** The name of the arbitrary scalable quantity. */
|
|
611
|
+
name?: string;
|
|
612
|
+
/** The numerical value of the arbitrary scalable quantity. */
|
|
613
|
+
quantity: FixedNumericValue;
|
|
614
|
+
/** The unit of the arbitrary scalable quantity. */
|
|
615
|
+
unit?: string;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Represents an arbitrary scalable quantity item in a recipe step.
|
|
619
|
+
* @category Types
|
|
620
|
+
*/
|
|
621
|
+
interface ArbitraryScalableItem {
|
|
622
|
+
/** The type of the item. */
|
|
623
|
+
type: "arbitrary";
|
|
624
|
+
/** The index of the arbitrary scalable quantity, within the {@link Recipe.arbitraries | list of arbitrary scalable quantities} */
|
|
625
|
+
index: number;
|
|
626
|
+
}
|
|
594
627
|
/**
|
|
595
628
|
* Represents an item in a recipe step.
|
|
596
629
|
* @category Types
|
|
597
630
|
*/
|
|
598
|
-
type
|
|
631
|
+
type StepItem = TextItem | IngredientItem | CookwareItem | TimerItem | ArbitraryScalableItem;
|
|
599
632
|
/**
|
|
600
633
|
* Represents a step in a recipe.
|
|
601
634
|
* @category Types
|
|
@@ -603,16 +636,21 @@ type Item = TextItem | IngredientItem | CookwareItem | TimerItem;
|
|
|
603
636
|
interface Step {
|
|
604
637
|
type: "step";
|
|
605
638
|
/** The items in the step. */
|
|
606
|
-
items:
|
|
639
|
+
items: StepItem[];
|
|
607
640
|
}
|
|
641
|
+
/**
|
|
642
|
+
* Represents an item in a note (can be text or arbitrary scalable).
|
|
643
|
+
* @category Types
|
|
644
|
+
*/
|
|
645
|
+
type NoteItem = TextItem | ArbitraryScalableItem;
|
|
608
646
|
/**
|
|
609
647
|
* Represents a note in a recipe.
|
|
610
648
|
* @category Types
|
|
611
649
|
*/
|
|
612
650
|
interface Note {
|
|
613
651
|
type: "note";
|
|
614
|
-
/** The
|
|
615
|
-
|
|
652
|
+
/** The items in the note. */
|
|
653
|
+
items: NoteItem[];
|
|
616
654
|
}
|
|
617
655
|
/**
|
|
618
656
|
* Represents a possible state modifier or other flag for cookware used in a recipe
|
|
@@ -1232,4 +1270,4 @@ declare class NoShoppingListForCartError extends Error {
|
|
|
1232
1270
|
constructor();
|
|
1233
1271
|
}
|
|
1234
1272
|
|
|
1235
|
-
export { type AddedIngredient, type AddedRecipe, type AddedRecipeOptions, type AlternativeIngredientRef, type CartContent, type CartMatch, type CartMisMatch, type CategorizedIngredients, type Category, CategoryConfig, type CategoryIngredient, type ComputedIngredient, type Cookware, type CookwareFlag, type CookwareItem, type DecimalValue, type FixedNumericValue, type FixedValue, type FractionValue, type Ingredient, type IngredientAlternative, type IngredientExtras, type IngredientFlag, type IngredientItem, type IngredientItemQuantity, type IngredientQuantityAndGroup, type IngredientQuantityGroup, type
|
|
1273
|
+
export { type AddedIngredient, type AddedRecipe, type AddedRecipeOptions, type AlternativeIngredientRef, type ArbitraryScalable, type ArbitraryScalableItem, type CartContent, type CartMatch, type CartMisMatch, type CategorizedIngredients, type Category, CategoryConfig, type CategoryIngredient, type ComputedIngredient, type Cookware, type CookwareFlag, type CookwareItem, type DecimalValue, type FixedNumericValue, type FixedValue, type FractionValue, type Ingredient, type IngredientAlternative, type IngredientExtras, type IngredientFlag, type IngredientItem, type IngredientItemQuantity, type IngredientQuantityAndGroup, type IngredientQuantityGroup, type MaybeNestedAndGroup, type MaybeNestedGroup, type MaybeNestedOrGroup, type Metadata, NoProductCatalogForCartError, type NoProductMatchErrorCode, NoShoppingListForCartError, type Note, type NoteItem, ProductCatalog, type ProductMatch, type ProductMisMatch, type ProductOption, type ProductOptionBase, type ProductOptionCore, type ProductSelection, type ProductSize, type QuantityBase, type QuantityWithExtendedUnit, type QuantityWithPlainUnit, type QuantityWithUnitDef, type QuantityWithUnitLike, type Range, Recipe, type RecipeAlternatives, type RecipeChoices, type RecipeWithFactor, type RecipeWithServings, Section, ShoppingCart, type ShoppingCartOptions, type ShoppingCartSummary, ShoppingList, type Step, type StepItem, type TextItem, type TextValue, type Timer, type TimerItem, type Unit, type UnitDefinition, type UnitDefinitionLike, type UnitSystem, type UnitType };
|
package/dist/index.d.ts
CHANGED
|
@@ -86,6 +86,10 @@ declare class Recipe {
|
|
|
86
86
|
* The parsed recipe timers.
|
|
87
87
|
*/
|
|
88
88
|
timers: Timer[];
|
|
89
|
+
/**
|
|
90
|
+
* The parsed arbitrary quantities.
|
|
91
|
+
*/
|
|
92
|
+
arbitraries: ArbitraryScalable[];
|
|
89
93
|
/**
|
|
90
94
|
* The parsed recipe servings. Used for scaling. Parsed from one of
|
|
91
95
|
* {@link Metadata.servings}, {@link Metadata.yield} or {@link Metadata.serves}
|
|
@@ -112,6 +116,19 @@ declare class Recipe {
|
|
|
112
116
|
* @param content - The recipe content to parse.
|
|
113
117
|
*/
|
|
114
118
|
constructor(content?: string);
|
|
119
|
+
/**
|
|
120
|
+
* Parses a matched arbitrary scalable quantity and adds it to the given array.
|
|
121
|
+
* @private
|
|
122
|
+
* @param regexMatchGroups - The regex match groups from arbitrary scalable regex.
|
|
123
|
+
* @param intoArray - The array to push the parsed arbitrary scalable item into.
|
|
124
|
+
*/
|
|
125
|
+
private _parseArbitraryScalable;
|
|
126
|
+
/**
|
|
127
|
+
* Parses text for arbitrary scalables and returns NoteItem array.
|
|
128
|
+
* @param text - The text to parse for arbitrary scalables.
|
|
129
|
+
* @returns Array of NoteItem (text and arbitrary scalable items).
|
|
130
|
+
*/
|
|
131
|
+
private _parseNoteText;
|
|
115
132
|
private _parseQuantityRecursive;
|
|
116
133
|
private _parseIngredientWithAlternativeRecursive;
|
|
117
134
|
private _parseIngredientWithGroupKey;
|
|
@@ -371,7 +388,7 @@ interface AlternativeIngredientRef {
|
|
|
371
388
|
/** The index of the alternative ingredient within the {@link Recipe.ingredients} array. */
|
|
372
389
|
index: number;
|
|
373
390
|
/** The quantities of the alternative ingredient. Multiple entries when units are incompatible. */
|
|
374
|
-
|
|
391
|
+
quantities?: QuantityWithPlainUnit[];
|
|
375
392
|
}
|
|
376
393
|
/**
|
|
377
394
|
* Represents a group of summed quantities for an ingredient, optionally with alternatives.
|
|
@@ -379,18 +396,12 @@ interface AlternativeIngredientRef {
|
|
|
379
396
|
* When units are incompatible, separate IngredientQuantityGroup entries are created instead of merging.
|
|
380
397
|
* @category Types
|
|
381
398
|
*/
|
|
382
|
-
interface IngredientQuantityGroup {
|
|
399
|
+
interface IngredientQuantityGroup extends QuantityWithPlainUnit {
|
|
383
400
|
/**
|
|
384
401
|
* References to alternative ingredients for this quantity group.
|
|
385
402
|
* If undefined, this group has no alternatives.
|
|
386
403
|
*/
|
|
387
404
|
alternatives?: AlternativeIngredientRef[];
|
|
388
|
-
/**
|
|
389
|
-
* The summed quantity for this group, potentially with equivalents.
|
|
390
|
-
* OR groups from addEquivalentsAndSimplify are converted back to QuantityWithPlainUnit
|
|
391
|
-
* (first entry as main, rest as equivalents).
|
|
392
|
-
*/
|
|
393
|
-
groupQuantity: QuantityWithPlainUnit;
|
|
394
405
|
}
|
|
395
406
|
/**
|
|
396
407
|
* Represents an AND group of quantities when primary units are incompatible but equivalents can be summed.
|
|
@@ -525,12 +536,12 @@ interface IngredientItem {
|
|
|
525
536
|
* @category Types
|
|
526
537
|
*/
|
|
527
538
|
interface RecipeAlternatives {
|
|
528
|
-
/** Map of choices that can be made at Ingredient
|
|
529
|
-
* - Keys are the Ingredient
|
|
539
|
+
/** Map of choices that can be made at Ingredient StepItem level
|
|
540
|
+
* - Keys are the Ingredient StepItem IDs (e.g. "ingredient-item-2")
|
|
530
541
|
* - Values are arrays of IngredientAlternative objects representing the choices available for that item
|
|
531
542
|
*/
|
|
532
543
|
ingredientItems: Map<string, IngredientAlternative[]>;
|
|
533
|
-
/** Map of choices that can be made for Grouped Ingredient
|
|
544
|
+
/** Map of choices that can be made for Grouped Ingredient StepItem's
|
|
534
545
|
* - Keys are the Group IDs (e.g. "eggs" for `@|eggs|...`)
|
|
535
546
|
* - Values are arrays of IngredientAlternative objects representing the choices available for that group
|
|
536
547
|
*/
|
|
@@ -542,9 +553,9 @@ interface RecipeAlternatives {
|
|
|
542
553
|
* @category Types
|
|
543
554
|
*/
|
|
544
555
|
interface RecipeChoices {
|
|
545
|
-
/** Map of choices that can be made at Ingredient
|
|
556
|
+
/** Map of choices that can be made at Ingredient StepItem level */
|
|
546
557
|
ingredientItems?: Map<string, number>;
|
|
547
|
-
/** Map of choices that can be made for Grouped Ingredient
|
|
558
|
+
/** Map of choices that can be made for Grouped Ingredient StepItem's */
|
|
548
559
|
ingredientGroups?: Map<string, number>;
|
|
549
560
|
}
|
|
550
561
|
/**
|
|
@@ -591,11 +602,33 @@ interface TextItem {
|
|
|
591
602
|
/** The content of the text item. */
|
|
592
603
|
value: string;
|
|
593
604
|
}
|
|
605
|
+
/**
|
|
606
|
+
* Represents an arbitrary scalable quantity in a recipe.
|
|
607
|
+
* @category Types
|
|
608
|
+
*/
|
|
609
|
+
interface ArbitraryScalable {
|
|
610
|
+
/** The name of the arbitrary scalable quantity. */
|
|
611
|
+
name?: string;
|
|
612
|
+
/** The numerical value of the arbitrary scalable quantity. */
|
|
613
|
+
quantity: FixedNumericValue;
|
|
614
|
+
/** The unit of the arbitrary scalable quantity. */
|
|
615
|
+
unit?: string;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Represents an arbitrary scalable quantity item in a recipe step.
|
|
619
|
+
* @category Types
|
|
620
|
+
*/
|
|
621
|
+
interface ArbitraryScalableItem {
|
|
622
|
+
/** The type of the item. */
|
|
623
|
+
type: "arbitrary";
|
|
624
|
+
/** The index of the arbitrary scalable quantity, within the {@link Recipe.arbitraries | list of arbitrary scalable quantities} */
|
|
625
|
+
index: number;
|
|
626
|
+
}
|
|
594
627
|
/**
|
|
595
628
|
* Represents an item in a recipe step.
|
|
596
629
|
* @category Types
|
|
597
630
|
*/
|
|
598
|
-
type
|
|
631
|
+
type StepItem = TextItem | IngredientItem | CookwareItem | TimerItem | ArbitraryScalableItem;
|
|
599
632
|
/**
|
|
600
633
|
* Represents a step in a recipe.
|
|
601
634
|
* @category Types
|
|
@@ -603,16 +636,21 @@ type Item = TextItem | IngredientItem | CookwareItem | TimerItem;
|
|
|
603
636
|
interface Step {
|
|
604
637
|
type: "step";
|
|
605
638
|
/** The items in the step. */
|
|
606
|
-
items:
|
|
639
|
+
items: StepItem[];
|
|
607
640
|
}
|
|
641
|
+
/**
|
|
642
|
+
* Represents an item in a note (can be text or arbitrary scalable).
|
|
643
|
+
* @category Types
|
|
644
|
+
*/
|
|
645
|
+
type NoteItem = TextItem | ArbitraryScalableItem;
|
|
608
646
|
/**
|
|
609
647
|
* Represents a note in a recipe.
|
|
610
648
|
* @category Types
|
|
611
649
|
*/
|
|
612
650
|
interface Note {
|
|
613
651
|
type: "note";
|
|
614
|
-
/** The
|
|
615
|
-
|
|
652
|
+
/** The items in the note. */
|
|
653
|
+
items: NoteItem[];
|
|
616
654
|
}
|
|
617
655
|
/**
|
|
618
656
|
* Represents a possible state modifier or other flag for cookware used in a recipe
|
|
@@ -1232,4 +1270,4 @@ declare class NoShoppingListForCartError extends Error {
|
|
|
1232
1270
|
constructor();
|
|
1233
1271
|
}
|
|
1234
1272
|
|
|
1235
|
-
export { type AddedIngredient, type AddedRecipe, type AddedRecipeOptions, type AlternativeIngredientRef, type CartContent, type CartMatch, type CartMisMatch, type CategorizedIngredients, type Category, CategoryConfig, type CategoryIngredient, type ComputedIngredient, type Cookware, type CookwareFlag, type CookwareItem, type DecimalValue, type FixedNumericValue, type FixedValue, type FractionValue, type Ingredient, type IngredientAlternative, type IngredientExtras, type IngredientFlag, type IngredientItem, type IngredientItemQuantity, type IngredientQuantityAndGroup, type IngredientQuantityGroup, type
|
|
1273
|
+
export { type AddedIngredient, type AddedRecipe, type AddedRecipeOptions, type AlternativeIngredientRef, type ArbitraryScalable, type ArbitraryScalableItem, type CartContent, type CartMatch, type CartMisMatch, type CategorizedIngredients, type Category, CategoryConfig, type CategoryIngredient, type ComputedIngredient, type Cookware, type CookwareFlag, type CookwareItem, type DecimalValue, type FixedNumericValue, type FixedValue, type FractionValue, type Ingredient, type IngredientAlternative, type IngredientExtras, type IngredientFlag, type IngredientItem, type IngredientItemQuantity, type IngredientQuantityAndGroup, type IngredientQuantityGroup, type MaybeNestedAndGroup, type MaybeNestedGroup, type MaybeNestedOrGroup, type Metadata, NoProductCatalogForCartError, type NoProductMatchErrorCode, NoShoppingListForCartError, type Note, type NoteItem, ProductCatalog, type ProductMatch, type ProductMisMatch, type ProductOption, type ProductOptionBase, type ProductOptionCore, type ProductSelection, type ProductSize, type QuantityBase, type QuantityWithExtendedUnit, type QuantityWithPlainUnit, type QuantityWithUnitDef, type QuantityWithUnitLike, type Range, Recipe, type RecipeAlternatives, type RecipeChoices, type RecipeWithFactor, type RecipeWithServings, Section, ShoppingCart, type ShoppingCartOptions, type ShoppingCartSummary, ShoppingList, type Step, type StepItem, type TextItem, type TextValue, type Timer, type TimerItem, type Unit, type UnitDefinition, type UnitDefinitionLike, type UnitSystem, type UnitType };
|
package/dist/index.js
CHANGED
|
@@ -271,17 +271,19 @@ var nonWordChar = "\\s@#~\\[\\]{(,;:!?";
|
|
|
271
271
|
var nonWordCharStrict = "\\s@#~\\[\\]{(,;:!?|";
|
|
272
272
|
var ingredientWithAlternativeRegex = d().literal("@").startNamedGroup("ingredientModifiers").anyOf("@\\-&?").zeroOrMore().endGroup().optional().startNamedGroup("ingredientRecipeAnchor").literal("./").endGroup().optional().startGroup().startGroup().startNamedGroup("mIngredientName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\}|\\([^)]*\\))").endGroup().or().startNamedGroup("sIngredientName").notAnyOf(nonWordChar).zeroOrMore().notAnyOf("\\." + nonWordChar).endGroup().endGroup().startGroup().literal("{").startNamedGroup("ingredientQuantityModifier").literal("=").exactly(1).endGroup().optional().startNamedGroup("ingredientQuantity").startGroup().notAnyOf("}|%").oneOrMore().endGroup().optional().startGroup().literal("%").notAnyOf("|}").oneOrMore().lazy().endGroup().optional().startGroup().literal("|").notAnyOf("}").oneOrMore().lazy().endGroup().zeroOrMore().endGroup().literal("}").endGroup().optional().startGroup().literal("(").startNamedGroup("ingredientPreparation").notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().startGroup().literal("[").startNamedGroup("ingredientNote").notAnyOf("\\]").oneOrMore().lazy().endGroup().literal("]").endGroup().optional().startNamedGroup("ingredientAlternative").startGroup().literal("|").startGroup().anyOf("@\\-&?").zeroOrMore().endGroup().optional().startGroup().literal("./").endGroup().optional().startGroup().startGroup().startGroup().notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\}|\\([^)]*\\))").endGroup().or().startGroup().notAnyOf(nonWordChar).oneOrMore().endGroup().endGroup().startGroup().literal("{").startGroup().literal("=").exactly(1).endGroup().optional().startGroup().notAnyOf("}%").oneOrMore().endGroup().optional().startGroup().literal("%").startGroup().notAnyOf("}").oneOrMore().lazy().endGroup().endGroup().optional().literal("}").endGroup().optional().startGroup().literal("(").startGroup().notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().startGroup().literal("[").startGroup().notAnyOf("\\]").oneOrMore().lazy().endGroup().literal("]").endGroup().optional().endGroup().zeroOrMore().endGroup().toRegExp();
|
|
273
273
|
var inlineIngredientAlternativesRegex = new RegExp("\\|" + ingredientWithAlternativeRegex.source.slice(1));
|
|
274
|
-
var quantityAlternativeRegex = d().startNamedGroup("
|
|
274
|
+
var quantityAlternativeRegex = d().startNamedGroup("quantity").notAnyOf("}|%").oneOrMore().endGroup().optional().startGroup().literal("%").startNamedGroup("unit").notAnyOf("|}").oneOrMore().endGroup().endGroup().optional().startGroup().literal("|").startNamedGroup("alternative").startGroup().notAnyOf("}").oneOrMore().endGroup().zeroOrMore().endGroup().endGroup().optional().toRegExp();
|
|
275
275
|
var ingredientWithGroupKeyRegex = d().literal("@|").startNamedGroup("gIngredientGroupKey").notAnyOf(nonWordCharStrict).oneOrMore().endGroup().literal("|").startNamedGroup("gIngredientModifiers").anyOf("@\\-&?").zeroOrMore().endGroup().optional().startNamedGroup("gIngredientRecipeAnchor").literal("./").endGroup().optional().startGroup().startGroup().startNamedGroup("gmIngredientName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\}|\\([^)]*\\))").endGroup().or().startNamedGroup("gsIngredientName").notAnyOf(nonWordChar).zeroOrMore().notAnyOf("\\." + nonWordChar).endGroup().endGroup().startGroup().literal("{").startNamedGroup("gIngredientQuantityModifier").literal("=").exactly(1).endGroup().optional().startNamedGroup("gIngredientQuantity").startGroup().notAnyOf("}|%").oneOrMore().endGroup().optional().startGroup().literal("%").notAnyOf("|}").oneOrMore().lazy().endGroup().optional().startGroup().literal("|").notAnyOf("}").oneOrMore().lazy().endGroup().zeroOrMore().endGroup().literal("}").endGroup().optional().startGroup().literal("(").startNamedGroup("gIngredientPreparation").notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().toRegExp();
|
|
276
276
|
var ingredientAliasRegex = d().startAnchor().startNamedGroup("ingredientListName").notAnyOf("|").oneOrMore().endGroup().literal("|").startNamedGroup("ingredientDisplayName").notAnyOf("|").oneOrMore().endGroup().endAnchor().toRegExp();
|
|
277
277
|
var cookwareRegex = d().literal("#").startNamedGroup("cookwareModifiers").anyOf("\\-&?").zeroOrMore().endGroup().startGroup().startGroup().startNamedGroup("mCookwareName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\})").endGroup().or().startNamedGroup("sCookwareName").notAnyOf(nonWordChar).zeroOrMore().notAnyOf("\\." + nonWordChar).endGroup().endGroup().startGroup().literal("{").startNamedGroup("cookwareQuantity").anyCharacter().zeroOrMore().lazy().endGroup().literal("}").endGroup().optional().toRegExp();
|
|
278
278
|
var timerRegex = d().literal("~").startNamedGroup("timerName").anyCharacter().zeroOrMore().lazy().endGroup().literal("{").startNamedGroup("timerQuantity").anyCharacter().oneOrMore().lazy().endGroup().startGroup().literal("%").startNamedGroup("timerUnit").anyCharacter().oneOrMore().lazy().endGroup().endGroup().optional().literal("}").toRegExp();
|
|
279
|
+
var arbitraryScalableRegex = d().literal("{{").startGroup().startNamedGroup("arbitraryName").notAnyOf("}:%").oneOrMore().endGroup().literal(":").endGroup().optional().startNamedGroup("arbitraryQuantity").startGroup().notAnyOf("}|%").oneOrMore().endGroup().optional().startGroup().literal("%").notAnyOf("|}").oneOrMore().lazy().endGroup().optional().startGroup().literal("|").notAnyOf("}").oneOrMore().lazy().endGroup().zeroOrMore().endGroup().literal("}}").toRegExp();
|
|
279
280
|
var tokensRegex = new RegExp(
|
|
280
281
|
[
|
|
281
282
|
ingredientWithGroupKeyRegex,
|
|
282
283
|
ingredientWithAlternativeRegex,
|
|
283
284
|
cookwareRegex,
|
|
284
|
-
timerRegex
|
|
285
|
+
timerRegex,
|
|
286
|
+
arbitraryScalableRegex
|
|
285
287
|
].map((r2) => r2.source).join("|"),
|
|
286
288
|
"gu"
|
|
287
289
|
);
|
|
@@ -513,8 +515,8 @@ function multiplyQuantityValue(value, factor) {
|
|
|
513
515
|
value.value,
|
|
514
516
|
Big(factor)
|
|
515
517
|
);
|
|
516
|
-
if (factor === parseInt(factor.toString()) || // e.g. 2 === int
|
|
517
|
-
Big(1).div(factor).toNumber() === parseInt(Big(1).div(factor).toString())) {
|
|
518
|
+
if (newValue.type === "fraction" && (Big(factor).toNumber() === parseInt(Big(factor).toString()) || // e.g. 2 === int
|
|
519
|
+
Big(1).div(factor).toNumber() === parseInt(Big(1).div(factor).toString()))) {
|
|
518
520
|
return {
|
|
519
521
|
type: "fixed",
|
|
520
522
|
value: newValue
|
|
@@ -601,8 +603,10 @@ var IncompatibleUnitsError = class extends Error {
|
|
|
601
603
|
}
|
|
602
604
|
};
|
|
603
605
|
var InvalidQuantityFormat = class extends Error {
|
|
604
|
-
constructor(value) {
|
|
605
|
-
super(
|
|
606
|
+
constructor(value, extra) {
|
|
607
|
+
super(
|
|
608
|
+
`Invalid quantity format found in: ${value}${extra ? ` (${extra})` : ""}`
|
|
609
|
+
);
|
|
606
610
|
this.name = "InvalidQuantityFormat";
|
|
607
611
|
}
|
|
608
612
|
};
|
|
@@ -792,7 +796,7 @@ var flattenPlainUnitGroup = (summed) => {
|
|
|
792
796
|
}
|
|
793
797
|
];
|
|
794
798
|
} else {
|
|
795
|
-
return andEntries
|
|
799
|
+
return andEntries;
|
|
796
800
|
}
|
|
797
801
|
}
|
|
798
802
|
const simpleEntries = entries.filter(
|
|
@@ -806,12 +810,10 @@ var flattenPlainUnitGroup = (summed) => {
|
|
|
806
810
|
if (simpleEntries.length > 1) {
|
|
807
811
|
result.equivalents = simpleEntries.slice(1);
|
|
808
812
|
}
|
|
809
|
-
return [
|
|
813
|
+
return [result];
|
|
810
814
|
} else {
|
|
811
815
|
const first = entries[0];
|
|
812
|
-
return [
|
|
813
|
-
{ groupQuantity: { quantity: first.quantity, unit: first.unit } }
|
|
814
|
-
];
|
|
816
|
+
return [{ quantity: first.quantity, unit: first.unit }];
|
|
815
817
|
}
|
|
816
818
|
} else if (isGroup(summed)) {
|
|
817
819
|
const andEntries = [];
|
|
@@ -836,7 +838,7 @@ var flattenPlainUnitGroup = (summed) => {
|
|
|
836
838
|
}
|
|
837
839
|
}
|
|
838
840
|
if (equivalentsList.length === 0) {
|
|
839
|
-
return andEntries
|
|
841
|
+
return andEntries;
|
|
840
842
|
}
|
|
841
843
|
const result = {
|
|
842
844
|
type: "and",
|
|
@@ -845,19 +847,17 @@ var flattenPlainUnitGroup = (summed) => {
|
|
|
845
847
|
};
|
|
846
848
|
return [result];
|
|
847
849
|
} else {
|
|
848
|
-
return [
|
|
849
|
-
{ groupQuantity: { quantity: summed.quantity, unit: summed.unit } }
|
|
850
|
-
];
|
|
850
|
+
return [{ quantity: summed.quantity, unit: summed.unit }];
|
|
851
851
|
}
|
|
852
852
|
};
|
|
853
853
|
|
|
854
854
|
// src/utils/parser_helpers.ts
|
|
855
|
-
function flushPendingNote(section,
|
|
856
|
-
if (
|
|
857
|
-
section.content.push({ type: "note",
|
|
858
|
-
return
|
|
855
|
+
function flushPendingNote(section, noteItems) {
|
|
856
|
+
if (noteItems.length > 0) {
|
|
857
|
+
section.content.push({ type: "note", items: [...noteItems] });
|
|
858
|
+
return [];
|
|
859
859
|
}
|
|
860
|
-
return
|
|
860
|
+
return noteItems;
|
|
861
861
|
}
|
|
862
862
|
function flushPendingItems(section, items) {
|
|
863
863
|
if (items.length > 0) {
|
|
@@ -1630,6 +1630,10 @@ var _Recipe = class _Recipe {
|
|
|
1630
1630
|
* The parsed recipe timers.
|
|
1631
1631
|
*/
|
|
1632
1632
|
__publicField(this, "timers", []);
|
|
1633
|
+
/**
|
|
1634
|
+
* The parsed arbitrary quantities.
|
|
1635
|
+
*/
|
|
1636
|
+
__publicField(this, "arbitraries", []);
|
|
1633
1637
|
/**
|
|
1634
1638
|
* The parsed recipe servings. Used for scaling. Parsed from one of
|
|
1635
1639
|
* {@link Metadata.servings}, {@link Metadata.yield} or {@link Metadata.serves}
|
|
@@ -1657,12 +1661,64 @@ var _Recipe = class _Recipe {
|
|
|
1657
1661
|
_Recipe.itemCounts.set(this, current + 1);
|
|
1658
1662
|
return current;
|
|
1659
1663
|
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Parses a matched arbitrary scalable quantity and adds it to the given array.
|
|
1666
|
+
* @private
|
|
1667
|
+
* @param regexMatchGroups - The regex match groups from arbitrary scalable regex.
|
|
1668
|
+
* @param intoArray - The array to push the parsed arbitrary scalable item into.
|
|
1669
|
+
*/
|
|
1670
|
+
_parseArbitraryScalable(regexMatchGroups, intoArray) {
|
|
1671
|
+
if (!regexMatchGroups || !regexMatchGroups.arbitraryQuantity) return;
|
|
1672
|
+
const quantityMatch = regexMatchGroups.arbitraryQuantity?.trim().match(quantityAlternativeRegex);
|
|
1673
|
+
if (quantityMatch?.groups) {
|
|
1674
|
+
const value = quantityMatch.groups.quantity ? parseQuantityInput(quantityMatch.groups.quantity) : void 0;
|
|
1675
|
+
const unit = quantityMatch.groups.unit;
|
|
1676
|
+
const name = regexMatchGroups.arbitraryName || void 0;
|
|
1677
|
+
if (!value || value.type === "fixed" && value.value.type === "text") {
|
|
1678
|
+
throw new InvalidQuantityFormat(
|
|
1679
|
+
regexMatchGroups.arbitraryQuantity?.trim(),
|
|
1680
|
+
"Arbitrary quantities must have a numerical value"
|
|
1681
|
+
);
|
|
1682
|
+
}
|
|
1683
|
+
const arbitrary = {
|
|
1684
|
+
quantity: value
|
|
1685
|
+
};
|
|
1686
|
+
if (name) arbitrary.name = name;
|
|
1687
|
+
if (unit) arbitrary.unit = unit;
|
|
1688
|
+
intoArray.push({
|
|
1689
|
+
type: "arbitrary",
|
|
1690
|
+
index: this.arbitraries.push(arbitrary) - 1
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Parses text for arbitrary scalables and returns NoteItem array.
|
|
1696
|
+
* @param text - The text to parse for arbitrary scalables.
|
|
1697
|
+
* @returns Array of NoteItem (text and arbitrary scalable items).
|
|
1698
|
+
*/
|
|
1699
|
+
_parseNoteText(text) {
|
|
1700
|
+
const noteItems = [];
|
|
1701
|
+
let cursor = 0;
|
|
1702
|
+
const globalRegex = new RegExp(arbitraryScalableRegex.source, "g");
|
|
1703
|
+
for (const match of text.matchAll(globalRegex)) {
|
|
1704
|
+
const idx = match.index;
|
|
1705
|
+
if (idx > cursor) {
|
|
1706
|
+
noteItems.push({ type: "text", value: text.slice(cursor, idx) });
|
|
1707
|
+
}
|
|
1708
|
+
this._parseArbitraryScalable(match.groups, noteItems);
|
|
1709
|
+
cursor = idx + match[0].length;
|
|
1710
|
+
}
|
|
1711
|
+
if (cursor < text.length) {
|
|
1712
|
+
noteItems.push({ type: "text", value: text.slice(cursor) });
|
|
1713
|
+
}
|
|
1714
|
+
return noteItems;
|
|
1715
|
+
}
|
|
1660
1716
|
_parseQuantityRecursive(quantityRaw) {
|
|
1661
1717
|
let quantityMatch = quantityRaw.match(quantityAlternativeRegex);
|
|
1662
1718
|
const quantities = [];
|
|
1663
1719
|
while (quantityMatch?.groups) {
|
|
1664
|
-
const value = quantityMatch.groups.
|
|
1665
|
-
const unit = quantityMatch.groups.
|
|
1720
|
+
const value = quantityMatch.groups.quantity ? parseQuantityInput(quantityMatch.groups.quantity) : void 0;
|
|
1721
|
+
const unit = quantityMatch.groups.unit;
|
|
1666
1722
|
if (value) {
|
|
1667
1723
|
const newQuantity = { quantity: value };
|
|
1668
1724
|
if (unit) {
|
|
@@ -1679,9 +1735,7 @@ var _Recipe = class _Recipe {
|
|
|
1679
1735
|
} else {
|
|
1680
1736
|
throw new InvalidQuantityFormat(quantityRaw);
|
|
1681
1737
|
}
|
|
1682
|
-
quantityMatch = quantityMatch.groups.
|
|
1683
|
-
quantityAlternativeRegex
|
|
1684
|
-
) : null;
|
|
1738
|
+
quantityMatch = quantityMatch.groups.alternative ? quantityMatch.groups.alternative.match(quantityAlternativeRegex) : null;
|
|
1685
1739
|
}
|
|
1686
1740
|
return quantities;
|
|
1687
1741
|
}
|
|
@@ -1984,7 +2038,7 @@ var _Recipe = class _Recipe {
|
|
|
1984
2038
|
(eq) => toPlainUnit(eq)
|
|
1985
2039
|
);
|
|
1986
2040
|
}
|
|
1987
|
-
newRef.
|
|
2041
|
+
newRef.quantities = [altQty];
|
|
1988
2042
|
}
|
|
1989
2043
|
alternativeRefs.push(newRef);
|
|
1990
2044
|
}
|
|
@@ -2009,7 +2063,7 @@ var _Recipe = class _Recipe {
|
|
|
2009
2063
|
}
|
|
2010
2064
|
alternativeRefs.push({
|
|
2011
2065
|
index: otherAlt.index,
|
|
2012
|
-
|
|
2066
|
+
quantities: [altQty]
|
|
2013
2067
|
});
|
|
2014
2068
|
}
|
|
2015
2069
|
}
|
|
@@ -2036,8 +2090,8 @@ var _Recipe = class _Recipe {
|
|
|
2036
2090
|
if (!group.alternativeQuantities.has(ref.index)) {
|
|
2037
2091
|
group.alternativeQuantities.set(ref.index, []);
|
|
2038
2092
|
}
|
|
2039
|
-
if (ref.
|
|
2040
|
-
for (const altQty of ref.
|
|
2093
|
+
if (ref.quantities && ref.quantities.length > 0) {
|
|
2094
|
+
for (const altQty of ref.quantities) {
|
|
2041
2095
|
if (altQty.equivalents && altQty.equivalents.length > 0) {
|
|
2042
2096
|
const entries = [
|
|
2043
2097
|
toExtendedUnit({
|
|
@@ -2080,9 +2134,9 @@ var _Recipe = class _Recipe {
|
|
|
2080
2134
|
...altQuantities
|
|
2081
2135
|
);
|
|
2082
2136
|
const flattenedAlt = flattenPlainUnitGroup(summedAltQuantity);
|
|
2083
|
-
ref.
|
|
2084
|
-
if ("
|
|
2085
|
-
return [item
|
|
2137
|
+
ref.quantities = flattenedAlt.flatMap((item) => {
|
|
2138
|
+
if ("quantity" in item) {
|
|
2139
|
+
return [item];
|
|
2086
2140
|
} else {
|
|
2087
2141
|
return item.entries;
|
|
2088
2142
|
}
|
|
@@ -2213,19 +2267,27 @@ var _Recipe = class _Recipe {
|
|
|
2213
2267
|
let blankLineBefore = true;
|
|
2214
2268
|
let section = new Section();
|
|
2215
2269
|
const items = [];
|
|
2216
|
-
let
|
|
2270
|
+
let noteText = "";
|
|
2217
2271
|
let inNote = false;
|
|
2218
2272
|
for (const line of cleanContent) {
|
|
2219
2273
|
if (line.trim().length === 0) {
|
|
2220
2274
|
flushPendingItems(section, items);
|
|
2221
|
-
|
|
2275
|
+
flushPendingNote(
|
|
2276
|
+
section,
|
|
2277
|
+
noteText ? this._parseNoteText(noteText) : []
|
|
2278
|
+
);
|
|
2279
|
+
noteText = "";
|
|
2222
2280
|
blankLineBefore = true;
|
|
2223
2281
|
inNote = false;
|
|
2224
2282
|
continue;
|
|
2225
2283
|
}
|
|
2226
2284
|
if (line.startsWith("=")) {
|
|
2227
2285
|
flushPendingItems(section, items);
|
|
2228
|
-
|
|
2286
|
+
flushPendingNote(
|
|
2287
|
+
section,
|
|
2288
|
+
noteText ? this._parseNoteText(noteText) : []
|
|
2289
|
+
);
|
|
2290
|
+
noteText = "";
|
|
2229
2291
|
if (this.sections.length === 0 && section.isBlank()) {
|
|
2230
2292
|
section.name = line.replace(/^=+|=+$/g, "").trim();
|
|
2231
2293
|
} else {
|
|
@@ -2240,22 +2302,26 @@ var _Recipe = class _Recipe {
|
|
|
2240
2302
|
}
|
|
2241
2303
|
if (blankLineBefore && line.startsWith(">")) {
|
|
2242
2304
|
flushPendingItems(section, items);
|
|
2243
|
-
|
|
2244
|
-
|
|
2305
|
+
flushPendingNote(
|
|
2306
|
+
section,
|
|
2307
|
+
noteText ? this._parseNoteText(noteText) : []
|
|
2308
|
+
);
|
|
2309
|
+
noteText = line.substring(1).trim();
|
|
2245
2310
|
inNote = true;
|
|
2246
2311
|
blankLineBefore = false;
|
|
2247
2312
|
continue;
|
|
2248
2313
|
}
|
|
2249
2314
|
if (inNote) {
|
|
2250
2315
|
if (line.startsWith(">")) {
|
|
2251
|
-
|
|
2316
|
+
noteText += " " + line.substring(1).trim();
|
|
2252
2317
|
} else {
|
|
2253
|
-
|
|
2318
|
+
noteText += " " + line.trim();
|
|
2254
2319
|
}
|
|
2255
2320
|
blankLineBefore = false;
|
|
2256
2321
|
continue;
|
|
2257
2322
|
}
|
|
2258
|
-
|
|
2323
|
+
flushPendingNote(section, noteText ? this._parseNoteText(noteText) : []);
|
|
2324
|
+
noteText = "";
|
|
2259
2325
|
let cursor = 0;
|
|
2260
2326
|
for (const match of line.matchAll(tokensRegex)) {
|
|
2261
2327
|
const idx = match.index;
|
|
@@ -2302,6 +2368,8 @@ var _Recipe = class _Recipe {
|
|
|
2302
2368
|
newItem.quantity = quantity;
|
|
2303
2369
|
}
|
|
2304
2370
|
items.push(newItem);
|
|
2371
|
+
} else if (groups.arbitraryQuantity) {
|
|
2372
|
+
this._parseArbitraryScalable(groups, items);
|
|
2305
2373
|
} else {
|
|
2306
2374
|
const durationStr = groups.timerQuantity.trim();
|
|
2307
2375
|
const unit = (groups.timerUnit || "").trim();
|
|
@@ -2325,7 +2393,7 @@ var _Recipe = class _Recipe {
|
|
|
2325
2393
|
blankLineBefore = false;
|
|
2326
2394
|
}
|
|
2327
2395
|
flushPendingItems(section, items);
|
|
2328
|
-
|
|
2396
|
+
flushPendingNote(section, noteText ? this._parseNoteText(noteText) : []);
|
|
2329
2397
|
if (!section.isBlank()) {
|
|
2330
2398
|
this.sections.push(section);
|
|
2331
2399
|
}
|
|
@@ -2340,9 +2408,9 @@ var _Recipe = class _Recipe {
|
|
|
2340
2408
|
* @throws `Error` if the recipe does not contains an initial {@link Recipe.servings | servings} value
|
|
2341
2409
|
*/
|
|
2342
2410
|
scaleTo(newServings) {
|
|
2343
|
-
|
|
2411
|
+
let originalServings = this.getServings();
|
|
2344
2412
|
if (originalServings === void 0 || originalServings === 0) {
|
|
2345
|
-
|
|
2413
|
+
originalServings = 1;
|
|
2346
2414
|
}
|
|
2347
2415
|
const factor = Big4(newServings).div(originalServings);
|
|
2348
2416
|
return this.scaleBy(factor);
|
|
@@ -2355,9 +2423,9 @@ var _Recipe = class _Recipe {
|
|
|
2355
2423
|
*/
|
|
2356
2424
|
scaleBy(factor) {
|
|
2357
2425
|
const newRecipe = this.clone();
|
|
2358
|
-
|
|
2426
|
+
let originalServings = newRecipe.getServings();
|
|
2359
2427
|
if (originalServings === void 0 || originalServings === 0) {
|
|
2360
|
-
|
|
2428
|
+
originalServings = 1;
|
|
2361
2429
|
}
|
|
2362
2430
|
function scaleAlternativesBy(alternatives, factor2) {
|
|
2363
2431
|
for (const alternative of alternatives) {
|
|
@@ -2406,6 +2474,12 @@ var _Recipe = class _Recipe {
|
|
|
2406
2474
|
for (const alternatives of newRecipe.choices.ingredientItems.values()) {
|
|
2407
2475
|
scaleAlternativesBy(alternatives, factor);
|
|
2408
2476
|
}
|
|
2477
|
+
for (const arbitrary of newRecipe.arbitraries) {
|
|
2478
|
+
arbitrary.quantity = multiplyQuantityValue(
|
|
2479
|
+
arbitrary.quantity,
|
|
2480
|
+
factor
|
|
2481
|
+
);
|
|
2482
|
+
}
|
|
2409
2483
|
newRecipe._populate_ingredient_quantities();
|
|
2410
2484
|
newRecipe.servings = Big4(originalServings).times(factor).toNumber();
|
|
2411
2485
|
if (newRecipe.metadata.servings && this.metadata.servings) {
|
|
@@ -2468,6 +2542,7 @@ var _Recipe = class _Recipe {
|
|
|
2468
2542
|
});
|
|
2469
2543
|
newRecipe.cookware = deepClone(this.cookware);
|
|
2470
2544
|
newRecipe.timers = deepClone(this.timers);
|
|
2545
|
+
newRecipe.arbitraries = deepClone(this.arbitraries);
|
|
2471
2546
|
newRecipe.servings = this.servings;
|
|
2472
2547
|
return newRecipe;
|
|
2473
2548
|
}
|