@tmlmt/cooklang-parser 3.0.0-alpha.16 → 3.0.0-alpha.17
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 +81 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -2
- package/dist/index.d.ts +17 -2
- package/dist/index.js +81 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -335,7 +335,7 @@ var nonWordCharStrict = "\\s@#~\\[\\]{(,;:!?|";
|
|
|
335
335
|
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();
|
|
336
336
|
var inlineIngredientAlternativesRegex = new RegExp("\\|" + ingredientWithAlternativeRegex.source.slice(1));
|
|
337
337
|
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();
|
|
338
|
-
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();
|
|
338
|
+
var ingredientWithGroupKeyRegex = d().literal("@|").startNamedGroup("gIngredientGroupKey").notAnyOf(nonWordCharStrict + "/").oneOrMore().endGroup().startGroup().literal("/").startNamedGroup("gIngredientSubgroupKey").notAnyOf(nonWordCharStrict).oneOrMore().endGroup().endGroup().optional().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();
|
|
339
339
|
var ingredientAliasRegex = d().startAnchor().startNamedGroup("ingredientListName").notAnyOf("|").oneOrMore().endGroup().literal("|").startNamedGroup("ingredientDisplayName").notAnyOf("|").oneOrMore().endGroup().endAnchor().toRegExp();
|
|
340
340
|
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();
|
|
341
341
|
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();
|
|
@@ -2837,6 +2837,7 @@ var _Recipe = class _Recipe {
|
|
|
2837
2837
|
*/
|
|
2838
2838
|
__publicField(this, "servings");
|
|
2839
2839
|
_Recipe.itemCounts.set(this, 0);
|
|
2840
|
+
_Recipe.subgroupIndices.set(this, /* @__PURE__ */ new Map());
|
|
2840
2841
|
if (content) {
|
|
2841
2842
|
this.parse(content);
|
|
2842
2843
|
}
|
|
@@ -3054,6 +3055,7 @@ var _Recipe = class _Recipe {
|
|
|
3054
3055
|
if (!match?.groups) return;
|
|
3055
3056
|
const groups = match.groups;
|
|
3056
3057
|
const groupKey = groups.gIngredientGroupKey;
|
|
3058
|
+
const subgroupKey = groups.gIngredientSubgroupKey;
|
|
3057
3059
|
let name = groups.gmIngredientName || groups.gsIngredientName;
|
|
3058
3060
|
const preparation = groups.gIngredientPreparation;
|
|
3059
3061
|
const modifiers = groups.gIngredientModifiers;
|
|
@@ -3121,7 +3123,8 @@ var _Recipe = class _Recipe {
|
|
|
3121
3123
|
if (itemQuantity) {
|
|
3122
3124
|
Object.assign(alternative, itemQuantity);
|
|
3123
3125
|
}
|
|
3124
|
-
const
|
|
3126
|
+
const existingSubgroups = this.choices.ingredientGroups.get(groupKey);
|
|
3127
|
+
const existingAlternativesFlat = existingSubgroups?.flat();
|
|
3125
3128
|
function upsertAlternativeToIngredient(ingredients, ingredientIdx, newAlternativeIdx) {
|
|
3126
3129
|
const ingredient = ingredients[ingredientIdx];
|
|
3127
3130
|
if (ingredient) {
|
|
@@ -3132,8 +3135,8 @@ var _Recipe = class _Recipe {
|
|
|
3132
3135
|
}
|
|
3133
3136
|
}
|
|
3134
3137
|
}
|
|
3135
|
-
if (
|
|
3136
|
-
for (const alt of
|
|
3138
|
+
if (existingAlternativesFlat) {
|
|
3139
|
+
for (const alt of existingAlternativesFlat) {
|
|
3137
3140
|
upsertAlternativeToIngredient(this.ingredients, alt.index, idxInList);
|
|
3138
3141
|
upsertAlternativeToIngredient(this.ingredients, idxInList, alt.index);
|
|
3139
3142
|
}
|
|
@@ -3145,14 +3148,35 @@ var _Recipe = class _Recipe {
|
|
|
3145
3148
|
group: groupKey,
|
|
3146
3149
|
alternatives: [alternative]
|
|
3147
3150
|
};
|
|
3151
|
+
if (subgroupKey !== void 0) {
|
|
3152
|
+
newItem.subgroup = subgroupKey;
|
|
3153
|
+
}
|
|
3148
3154
|
items.push(newItem);
|
|
3149
3155
|
const choiceAlternative = deepClone(alternative);
|
|
3150
3156
|
choiceAlternative.itemId = id;
|
|
3151
3157
|
const existingChoice = this.choices.ingredientGroups.get(groupKey);
|
|
3158
|
+
const sgMap = _Recipe.subgroupIndices.get(this);
|
|
3152
3159
|
if (!existingChoice) {
|
|
3153
|
-
this.choices.ingredientGroups.set(groupKey, [choiceAlternative]);
|
|
3160
|
+
this.choices.ingredientGroups.set(groupKey, [[choiceAlternative]]);
|
|
3161
|
+
if (subgroupKey !== void 0) {
|
|
3162
|
+
sgMap.set(groupKey, /* @__PURE__ */ new Map([[subgroupKey, 0]]));
|
|
3163
|
+
}
|
|
3164
|
+
} else if (subgroupKey !== void 0) {
|
|
3165
|
+
const groupSgMap = sgMap.get(groupKey);
|
|
3166
|
+
const existingIdx = groupSgMap?.get(subgroupKey);
|
|
3167
|
+
if (existingIdx !== void 0) {
|
|
3168
|
+
existingChoice[existingIdx].push(choiceAlternative);
|
|
3169
|
+
} else {
|
|
3170
|
+
const newIdx = existingChoice.length;
|
|
3171
|
+
existingChoice.push([choiceAlternative]);
|
|
3172
|
+
if (!groupSgMap) {
|
|
3173
|
+
sgMap.set(groupKey, /* @__PURE__ */ new Map([[subgroupKey, newIdx]]));
|
|
3174
|
+
} else {
|
|
3175
|
+
groupSgMap.set(subgroupKey, newIdx);
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3154
3178
|
} else {
|
|
3155
|
-
existingChoice.push(choiceAlternative);
|
|
3179
|
+
existingChoice.push([choiceAlternative]);
|
|
3156
3180
|
}
|
|
3157
3181
|
}
|
|
3158
3182
|
/**
|
|
@@ -3209,15 +3233,16 @@ var _Recipe = class _Recipe {
|
|
|
3209
3233
|
(item2) => item2.type === "ingredient"
|
|
3210
3234
|
)) {
|
|
3211
3235
|
const isGrouped = "group" in item && item.group !== void 0;
|
|
3212
|
-
const
|
|
3236
|
+
const groupSubgroups = isGrouped ? this.choices.ingredientGroups.get(item.group) : void 0;
|
|
3213
3237
|
let selectedAltIndex = 0;
|
|
3214
3238
|
let isSelected;
|
|
3215
3239
|
let hasExplicitChoice;
|
|
3216
3240
|
if (isGrouped) {
|
|
3217
3241
|
const groupChoice = choices?.ingredientGroups?.get(item.group);
|
|
3218
3242
|
hasExplicitChoice = groupChoice !== void 0;
|
|
3219
|
-
const
|
|
3220
|
-
|
|
3243
|
+
const targetSubgroupIndex = groupChoice ?? 0;
|
|
3244
|
+
const selectedSubgroup = groupSubgroups?.[targetSubgroupIndex];
|
|
3245
|
+
isSelected = selectedSubgroup?.some((alt) => alt.itemId === item.id) ?? false;
|
|
3221
3246
|
} else {
|
|
3222
3247
|
const itemChoice = choices?.ingredientItems?.get(item.id);
|
|
3223
3248
|
hasExplicitChoice = itemChoice !== void 0;
|
|
@@ -3227,8 +3252,8 @@ var _Recipe = class _Recipe {
|
|
|
3227
3252
|
const alternative = item.alternatives[selectedAltIndex];
|
|
3228
3253
|
if (!alternative || !isSelected) continue;
|
|
3229
3254
|
selectedIndices.add(alternative.index);
|
|
3230
|
-
const
|
|
3231
|
-
for (const alt of
|
|
3255
|
+
const allAltsFlat = isGrouped ? groupSubgroups.flat() : item.alternatives;
|
|
3256
|
+
for (const alt of allAltsFlat) {
|
|
3232
3257
|
referencedIndices.add(alt.index);
|
|
3233
3258
|
}
|
|
3234
3259
|
if (!alternative.quantity) continue;
|
|
@@ -3240,10 +3265,34 @@ var _Recipe = class _Recipe {
|
|
|
3240
3265
|
};
|
|
3241
3266
|
const quantityEntry = alternative.equivalents?.length ? { or: [baseQty, ...alternative.equivalents] } : baseQty;
|
|
3242
3267
|
let alternativeRefs;
|
|
3243
|
-
if (!hasExplicitChoice &&
|
|
3244
|
-
|
|
3245
|
-
(
|
|
3246
|
-
)
|
|
3268
|
+
if (!hasExplicitChoice && groupSubgroups && groupSubgroups.length > 1) {
|
|
3269
|
+
const currentSubgroupIdx = groupSubgroups.findIndex(
|
|
3270
|
+
(sg) => sg.some((alt) => alt.itemId === item.id)
|
|
3271
|
+
);
|
|
3272
|
+
alternativeRefs = groupSubgroups.filter((_, idx) => idx !== currentSubgroupIdx).flatMap(
|
|
3273
|
+
(subgroup) => subgroup.map((otherAlt) => {
|
|
3274
|
+
const ref = {
|
|
3275
|
+
index: otherAlt.index
|
|
3276
|
+
};
|
|
3277
|
+
if (otherAlt.quantity) {
|
|
3278
|
+
const altQty = {
|
|
3279
|
+
quantity: otherAlt.quantity,
|
|
3280
|
+
...otherAlt.unit && {
|
|
3281
|
+
unit: otherAlt.unit.name
|
|
3282
|
+
},
|
|
3283
|
+
...otherAlt.equivalents && {
|
|
3284
|
+
equivalents: otherAlt.equivalents.map(
|
|
3285
|
+
(eq) => toPlainUnit(eq)
|
|
3286
|
+
)
|
|
3287
|
+
}
|
|
3288
|
+
};
|
|
3289
|
+
ref.quantities = [altQty];
|
|
3290
|
+
}
|
|
3291
|
+
return ref;
|
|
3292
|
+
})
|
|
3293
|
+
);
|
|
3294
|
+
} else if (!hasExplicitChoice && !isGrouped && allAltsFlat.length > 1) {
|
|
3295
|
+
alternativeRefs = allAltsFlat.filter((alt) => alt.index !== alternative.index).map((otherAlt) => {
|
|
3247
3296
|
const ref = { index: otherAlt.index };
|
|
3248
3297
|
if (otherAlt.quantity) {
|
|
3249
3298
|
const altQty = {
|
|
@@ -3661,8 +3710,10 @@ var _Recipe = class _Recipe {
|
|
|
3661
3710
|
}
|
|
3662
3711
|
}
|
|
3663
3712
|
}
|
|
3664
|
-
for (const
|
|
3665
|
-
|
|
3713
|
+
for (const subgroups of newRecipe.choices.ingredientGroups.values()) {
|
|
3714
|
+
for (const subgroup of subgroups) {
|
|
3715
|
+
scaleAlternativesBy(subgroup, factor);
|
|
3716
|
+
}
|
|
3666
3717
|
}
|
|
3667
3718
|
for (const alternatives of newRecipe.choices.ingredientItems.values()) {
|
|
3668
3719
|
scaleAlternativesBy(alternatives, factor);
|
|
@@ -3849,8 +3900,10 @@ var _Recipe = class _Recipe {
|
|
|
3849
3900
|
}
|
|
3850
3901
|
}
|
|
3851
3902
|
}
|
|
3852
|
-
for (const
|
|
3853
|
-
|
|
3903
|
+
for (const subgroups of newRecipe.choices.ingredientGroups.values()) {
|
|
3904
|
+
for (const subgroup of subgroups) {
|
|
3905
|
+
convertAlternatives(subgroup);
|
|
3906
|
+
}
|
|
3854
3907
|
}
|
|
3855
3908
|
for (const alternatives of newRecipe.choices.ingredientItems.values()) {
|
|
3856
3909
|
convertAlternatives(alternatives);
|
|
@@ -3902,6 +3955,11 @@ __publicField(_Recipe, "unitSystems", /* @__PURE__ */ new WeakMap());
|
|
|
3902
3955
|
* Used for giving ID numbers to items during parsing.
|
|
3903
3956
|
*/
|
|
3904
3957
|
__publicField(_Recipe, "itemCounts", /* @__PURE__ */ new WeakMap());
|
|
3958
|
+
/**
|
|
3959
|
+
* External storage for subgroup index tracking during parsing.
|
|
3960
|
+
* Maps groupKey → subgroupKey → index within the subgroups array.
|
|
3961
|
+
*/
|
|
3962
|
+
__publicField(_Recipe, "subgroupIndices", /* @__PURE__ */ new WeakMap());
|
|
3905
3963
|
var Recipe = _Recipe;
|
|
3906
3964
|
|
|
3907
3965
|
// src/classes/shopping_list.ts
|
|
@@ -4739,10 +4797,10 @@ function isGroupedItem(item) {
|
|
|
4739
4797
|
function isAlternativeSelected(recipe, choices, item, alternativeIndex) {
|
|
4740
4798
|
if (item.group) {
|
|
4741
4799
|
const selectedIndex2 = choices?.ingredientGroups?.get(item.group);
|
|
4742
|
-
const
|
|
4743
|
-
if (
|
|
4744
|
-
const
|
|
4745
|
-
return
|
|
4800
|
+
const groupSubgroups = recipe.choices.ingredientGroups.get(item.group);
|
|
4801
|
+
if (groupSubgroups && selectedIndex2 !== void 0 && selectedIndex2 < groupSubgroups.length) {
|
|
4802
|
+
const selectedSubgroup = groupSubgroups[selectedIndex2];
|
|
4803
|
+
return selectedSubgroup?.some((alt) => alt.itemId === item.id);
|
|
4746
4804
|
}
|
|
4747
4805
|
return false;
|
|
4748
4806
|
}
|