@tmlmt/cooklang-parser 3.0.0-alpha.17 → 3.0.0-alpha.19
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 +280 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +145 -13
- package/dist/index.d.ts +145 -13
- package/dist/index.js +277 -28
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -32,11 +32,9 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
32
32
|
// src/index.ts
|
|
33
33
|
var index_exports = {};
|
|
34
34
|
__export(index_exports, {
|
|
35
|
-
BadIndentationError: () => BadIndentationError,
|
|
36
35
|
CategoryConfig: () => CategoryConfig,
|
|
37
36
|
NoProductCatalogForCartError: () => NoProductCatalogForCartError,
|
|
38
37
|
NoShoppingListForCartError: () => NoShoppingListForCartError,
|
|
39
|
-
NoTabAsIndentError: () => NoTabAsIndentError,
|
|
40
38
|
Pantry: () => Pantry,
|
|
41
39
|
ProductCatalog: () => ProductCatalog,
|
|
42
40
|
Recipe: () => Recipe,
|
|
@@ -51,11 +49,14 @@ __export(index_exports, {
|
|
|
51
49
|
formatQuantityWithUnit: () => formatQuantityWithUnit,
|
|
52
50
|
formatSingleValue: () => formatSingleValue,
|
|
53
51
|
formatUnit: () => formatUnit,
|
|
52
|
+
getEffectiveChoices: () => getEffectiveChoices,
|
|
54
53
|
hasAlternatives: () => hasAlternatives,
|
|
55
54
|
isAlternativeSelected: () => isAlternativeSelected,
|
|
56
55
|
isAndGroup: () => isAndGroup,
|
|
57
56
|
isGroupedItem: () => isGroupedItem,
|
|
57
|
+
isSectionActive: () => isSectionActive,
|
|
58
58
|
isSimpleGroup: () => isSimpleGroup,
|
|
59
|
+
isStepActive: () => isStepActive,
|
|
59
60
|
renderFractionAsVulgar: () => renderFractionAsVulgar
|
|
60
61
|
});
|
|
61
62
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -335,7 +336,7 @@ var nonWordCharStrict = "\\s@#~\\[\\]{(,;:!?|";
|
|
|
335
336
|
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
337
|
var inlineIngredientAlternativesRegex = new RegExp("\\|" + ingredientWithAlternativeRegex.source.slice(1));
|
|
337
338
|
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().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
|
+
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().startGroup().literal("[").startNamedGroup("gIngredientNote").notAnyOf("\\]").oneOrMore().lazy().endGroup().literal("]").endGroup().optional().toRegExp();
|
|
339
340
|
var ingredientAliasRegex = d().startAnchor().startNamedGroup("ingredientListName").notAnyOf("|").oneOrMore().endGroup().literal("|").startNamedGroup("ingredientDisplayName").notAnyOf("|").oneOrMore().endGroup().endAnchor().toRegExp();
|
|
340
341
|
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
342
|
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();
|
|
@@ -363,6 +364,7 @@ var shoppingListRegex = d().literal("[").startNamedGroup("name").anyCharacter().
|
|
|
363
364
|
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();
|
|
364
365
|
var numberLikeRegex = d().startAnchor().digit().oneOrMore().startGroup().anyOf(".,/").exactly(1).digit().oneOrMore().endGroup().optional().endAnchor().toRegExp();
|
|
365
366
|
var floatRegex = d().startAnchor().digit().oneOrMore().startGroup().anyOf(".").exactly(1).digit().oneOrMore().endGroup().optional().endAnchor().toRegExp();
|
|
367
|
+
var variantTagRegex = d().startAnchor().literal("[").startNamedGroup("variantOptionalPrefix").literal("?").endGroup().optional().startNamedGroup("variantNames").notAnyOf("\\]").zeroOrMore().endGroup().literal("]").whitespace().zeroOrMore().toRegExp();
|
|
366
368
|
var mdEscaped = d().literal("\\").startCaptureGroup().anyOf("*_`").endGroup();
|
|
367
369
|
var mdInlineCode = d().literal("`").startCaptureGroup().notAnyOf("`").oneOrMore().lazy().endGroup().literal("`");
|
|
368
370
|
var mdLink = d().literal("[").startCaptureGroup().notAnyOf("\\]").oneOrMore().lazy().endGroup().literal("](").startCaptureGroup().notAnyOf(")").oneOrMore().lazy().endGroup().literal(")");
|
|
@@ -1417,9 +1419,12 @@ function flushPendingNote(section, noteItems) {
|
|
|
1417
1419
|
}
|
|
1418
1420
|
return noteItems;
|
|
1419
1421
|
}
|
|
1420
|
-
function flushPendingItems(section, items) {
|
|
1422
|
+
function flushPendingItems(section, items, stepVariants, stepOptional) {
|
|
1421
1423
|
if (items.length > 0) {
|
|
1422
|
-
|
|
1424
|
+
const step = { type: "step", items: [...items] };
|
|
1425
|
+
if (stepVariants) step.variants = stepVariants;
|
|
1426
|
+
if (stepOptional) step.optional = true;
|
|
1427
|
+
section.content.push(step);
|
|
1423
1428
|
items.length = 0;
|
|
1424
1429
|
return true;
|
|
1425
1430
|
}
|
|
@@ -1963,7 +1968,8 @@ function extractMetadata(content) {
|
|
|
1963
1968
|
"yield",
|
|
1964
1969
|
"serves",
|
|
1965
1970
|
// List fields
|
|
1966
|
-
"tags"
|
|
1971
|
+
"tags",
|
|
1972
|
+
"variants"
|
|
1967
1973
|
]);
|
|
1968
1974
|
for (const metaVar of [
|
|
1969
1975
|
"title",
|
|
@@ -2050,6 +2056,8 @@ function extractMetadata(content) {
|
|
|
2050
2056
|
}
|
|
2051
2057
|
const tags = parseListMetaVar(metadataContent, "tags");
|
|
2052
2058
|
if (tags) metadata.tags = tags;
|
|
2059
|
+
const variants = parseListMetaVar(metadataContent, "variants");
|
|
2060
|
+
if (variants) metadata.variants = variants;
|
|
2053
2061
|
const allKeys = extractAllMetadataKeys(metadataContent);
|
|
2054
2062
|
for (const key of allKeys) {
|
|
2055
2063
|
if (handledKeys.has(key)) continue;
|
|
@@ -2437,8 +2445,10 @@ var Section = class {
|
|
|
2437
2445
|
/**
|
|
2438
2446
|
* Creates an instance of Section.
|
|
2439
2447
|
* @param name - The name of the section. Defaults to an empty string.
|
|
2448
|
+
* @param variants - Optional variant names for this section.
|
|
2449
|
+
* @param optional - Whether the section is optional.
|
|
2440
2450
|
*/
|
|
2441
|
-
constructor(name = "") {
|
|
2451
|
+
constructor(name = "", variants, optional) {
|
|
2442
2452
|
/**
|
|
2443
2453
|
* The name of the section. Can be an empty string for the default (first) section.
|
|
2444
2454
|
* @defaultValue `""`
|
|
@@ -2446,7 +2456,13 @@ var Section = class {
|
|
|
2446
2456
|
__publicField(this, "name");
|
|
2447
2457
|
/** An array of steps and notes that make up the content of the section. */
|
|
2448
2458
|
__publicField(this, "content", []);
|
|
2459
|
+
/** Optional list of variant names this section belongs to. */
|
|
2460
|
+
__publicField(this, "variants");
|
|
2461
|
+
/** Whether the section has been marked as optional ([?]) */
|
|
2462
|
+
__publicField(this, "optional");
|
|
2449
2463
|
this.name = name;
|
|
2464
|
+
if (variants) this.variants = variants;
|
|
2465
|
+
if (optional) this.optional = true;
|
|
2450
2466
|
}
|
|
2451
2467
|
/**
|
|
2452
2468
|
* Checks if the section is blank (has no name and no content).
|
|
@@ -2806,7 +2822,8 @@ var _Recipe = class _Recipe {
|
|
|
2806
2822
|
*/
|
|
2807
2823
|
__publicField(this, "choices", {
|
|
2808
2824
|
ingredientItems: /* @__PURE__ */ new Map(),
|
|
2809
|
-
ingredientGroups: /* @__PURE__ */ new Map()
|
|
2825
|
+
ingredientGroups: /* @__PURE__ */ new Map(),
|
|
2826
|
+
variants: []
|
|
2810
2827
|
});
|
|
2811
2828
|
/**
|
|
2812
2829
|
* The parsed recipe ingredients.
|
|
@@ -3123,6 +3140,10 @@ var _Recipe = class _Recipe {
|
|
|
3123
3140
|
if (itemQuantity) {
|
|
3124
3141
|
Object.assign(alternative, itemQuantity);
|
|
3125
3142
|
}
|
|
3143
|
+
const note = groups.gIngredientNote?.trim();
|
|
3144
|
+
if (note) {
|
|
3145
|
+
alternative.note = note;
|
|
3146
|
+
}
|
|
3126
3147
|
const existingSubgroups = this.choices.ingredientGroups.get(groupKey);
|
|
3127
3148
|
const existingAlternativesFlat = existingSubgroups?.flat();
|
|
3128
3149
|
function upsertAlternativeToIngredient(ingredients, ingredientIdx, newAlternativeIdx) {
|
|
@@ -3216,6 +3237,8 @@ var _Recipe = class _Recipe {
|
|
|
3216
3237
|
/** @internal */
|
|
3217
3238
|
collectQuantityGroups(options) {
|
|
3218
3239
|
const { section, step, choices } = options || {};
|
|
3240
|
+
const activeVariant = choices?.variant;
|
|
3241
|
+
const isDefaultVariant = activeVariant === void 0 || activeVariant === "*";
|
|
3219
3242
|
const sectionsToProcess = section !== void 0 ? (() => {
|
|
3220
3243
|
const idx = typeof section === "number" ? section : this.sections.indexOf(section);
|
|
3221
3244
|
return idx >= 0 && idx < this.sections.length ? [this.sections[idx]] : [];
|
|
@@ -3223,12 +3246,29 @@ var _Recipe = class _Recipe {
|
|
|
3223
3246
|
const ingredientGroups = /* @__PURE__ */ new Map();
|
|
3224
3247
|
const selectedIndices = /* @__PURE__ */ new Set();
|
|
3225
3248
|
const referencedIndices = /* @__PURE__ */ new Set();
|
|
3249
|
+
const dynamicOptionalIndices = /* @__PURE__ */ new Set();
|
|
3226
3250
|
for (const currentSection of sectionsToProcess) {
|
|
3251
|
+
if (currentSection.variants) {
|
|
3252
|
+
if (isDefaultVariant) {
|
|
3253
|
+
if (!currentSection.variants.includes("*")) continue;
|
|
3254
|
+
} else {
|
|
3255
|
+
if (!currentSection.variants.includes(activeVariant)) continue;
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3227
3258
|
const allSteps = currentSection.content.filter(
|
|
3228
3259
|
(item) => item.type === "step"
|
|
3229
3260
|
);
|
|
3261
|
+
const isOptionalSection = currentSection.optional === true;
|
|
3230
3262
|
const stepsToProcess = step === void 0 ? allSteps : typeof step === "number" ? step >= 0 && step < allSteps.length ? [allSteps[step]] : [] : allSteps.includes(step) ? [step] : [];
|
|
3231
3263
|
for (const currentStep of stepsToProcess) {
|
|
3264
|
+
if (currentStep.variants) {
|
|
3265
|
+
if (isDefaultVariant) {
|
|
3266
|
+
if (!currentStep.variants.includes("*")) continue;
|
|
3267
|
+
} else {
|
|
3268
|
+
if (!currentStep.variants.includes(activeVariant)) continue;
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
const isOptionalStep = currentStep.optional === true || isOptionalSection;
|
|
3232
3272
|
for (const item of currentStep.items.filter(
|
|
3233
3273
|
(item2) => item2.type === "ingredient"
|
|
3234
3274
|
)) {
|
|
@@ -3240,18 +3280,53 @@ var _Recipe = class _Recipe {
|
|
|
3240
3280
|
if (isGrouped) {
|
|
3241
3281
|
const groupChoice = choices?.ingredientGroups?.get(item.group);
|
|
3242
3282
|
hasExplicitChoice = groupChoice !== void 0;
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3283
|
+
if (!hasExplicitChoice && !isDefaultVariant) {
|
|
3284
|
+
const matchingSubgroupIdx = groupSubgroups?.findIndex(
|
|
3285
|
+
(sg) => sg.some(
|
|
3286
|
+
(alt) => alt.note && alt.note.toLowerCase().includes(activeVariant.toLowerCase())
|
|
3287
|
+
)
|
|
3288
|
+
);
|
|
3289
|
+
if (matchingSubgroupIdx !== void 0 && matchingSubgroupIdx >= 0) {
|
|
3290
|
+
const matchedSubgroup = groupSubgroups[matchingSubgroupIdx];
|
|
3291
|
+
isSelected = matchedSubgroup.some(
|
|
3292
|
+
(alt) => alt.itemId === item.id
|
|
3293
|
+
);
|
|
3294
|
+
hasExplicitChoice = true;
|
|
3295
|
+
selectedAltIndex = 0;
|
|
3296
|
+
} else {
|
|
3297
|
+
const targetSubgroupIndex = 0;
|
|
3298
|
+
const selectedSubgroup = groupSubgroups?.[targetSubgroupIndex];
|
|
3299
|
+
isSelected = selectedSubgroup?.some((alt) => alt.itemId === item.id) ?? false;
|
|
3300
|
+
}
|
|
3301
|
+
} else {
|
|
3302
|
+
const targetSubgroupIndex = groupChoice ?? 0;
|
|
3303
|
+
const selectedSubgroup = groupSubgroups?.[targetSubgroupIndex];
|
|
3304
|
+
isSelected = selectedSubgroup?.some((alt) => alt.itemId === item.id) ?? false;
|
|
3305
|
+
}
|
|
3246
3306
|
} else {
|
|
3247
3307
|
const itemChoice = choices?.ingredientItems?.get(item.id);
|
|
3248
3308
|
hasExplicitChoice = itemChoice !== void 0;
|
|
3249
|
-
|
|
3309
|
+
if (!hasExplicitChoice && !isDefaultVariant) {
|
|
3310
|
+
const matchingIndices = item.alternatives.map((alt, idx) => ({ alt, idx })).filter(
|
|
3311
|
+
({ alt }) => alt.note && alt.note.toLowerCase().includes(activeVariant.toLowerCase())
|
|
3312
|
+
).map(({ idx }) => idx);
|
|
3313
|
+
if (matchingIndices.length > 0) {
|
|
3314
|
+
selectedAltIndex = matchingIndices[0];
|
|
3315
|
+
hasExplicitChoice = true;
|
|
3316
|
+
} else {
|
|
3317
|
+
selectedAltIndex = itemChoice ?? 0;
|
|
3318
|
+
}
|
|
3319
|
+
} else {
|
|
3320
|
+
selectedAltIndex = itemChoice ?? 0;
|
|
3321
|
+
}
|
|
3250
3322
|
isSelected = true;
|
|
3251
3323
|
}
|
|
3252
3324
|
const alternative = item.alternatives[selectedAltIndex];
|
|
3253
3325
|
if (!alternative || !isSelected) continue;
|
|
3254
3326
|
selectedIndices.add(alternative.index);
|
|
3327
|
+
if (isOptionalStep) {
|
|
3328
|
+
dynamicOptionalIndices.add(alternative.index);
|
|
3329
|
+
}
|
|
3255
3330
|
const allAltsFlat = isGrouped ? groupSubgroups.flat() : item.alternatives;
|
|
3256
3331
|
for (const alt of allAltsFlat) {
|
|
3257
3332
|
referencedIndices.add(alt.index);
|
|
@@ -3357,7 +3432,12 @@ var _Recipe = class _Recipe {
|
|
|
3357
3432
|
}
|
|
3358
3433
|
}
|
|
3359
3434
|
}
|
|
3360
|
-
return {
|
|
3435
|
+
return {
|
|
3436
|
+
ingredientGroups,
|
|
3437
|
+
selectedIndices,
|
|
3438
|
+
referencedIndices,
|
|
3439
|
+
dynamicOptionalIndices
|
|
3440
|
+
};
|
|
3361
3441
|
}
|
|
3362
3442
|
/**
|
|
3363
3443
|
* Gets the raw (unprocessed) quantity groups for each ingredient, before
|
|
@@ -3376,12 +3456,21 @@ var _Recipe = class _Recipe {
|
|
|
3376
3456
|
* ```
|
|
3377
3457
|
*/
|
|
3378
3458
|
getRawQuantityGroups(options) {
|
|
3379
|
-
const {
|
|
3459
|
+
const {
|
|
3460
|
+
ingredientGroups,
|
|
3461
|
+
selectedIndices,
|
|
3462
|
+
referencedIndices,
|
|
3463
|
+
dynamicOptionalIndices
|
|
3464
|
+
} = this.collectQuantityGroups(options);
|
|
3380
3465
|
const result = [];
|
|
3381
3466
|
for (let index = 0; index < this.ingredients.length; index++) {
|
|
3382
3467
|
if (!referencedIndices.has(index)) continue;
|
|
3383
3468
|
const orig = this.ingredients[index];
|
|
3384
3469
|
const usedAsPrimary = selectedIndices.has(index);
|
|
3470
|
+
let flags = orig.flags;
|
|
3471
|
+
if (dynamicOptionalIndices.has(index) && !flags?.includes("optional")) {
|
|
3472
|
+
flags = [...flags ?? [], "optional"];
|
|
3473
|
+
}
|
|
3385
3474
|
const quantities = [];
|
|
3386
3475
|
if (usedAsPrimary) {
|
|
3387
3476
|
const groupsForIng = ingredientGroups.get(index);
|
|
@@ -3394,7 +3483,7 @@ var _Recipe = class _Recipe {
|
|
|
3394
3483
|
result.push({
|
|
3395
3484
|
name: orig.name,
|
|
3396
3485
|
...usedAsPrimary && { usedAsPrimary: true },
|
|
3397
|
-
...
|
|
3486
|
+
...flags && { flags },
|
|
3398
3487
|
quantities
|
|
3399
3488
|
});
|
|
3400
3489
|
}
|
|
@@ -3428,15 +3517,24 @@ var _Recipe = class _Recipe {
|
|
|
3428
3517
|
* ```
|
|
3429
3518
|
*/
|
|
3430
3519
|
getIngredientQuantities(options) {
|
|
3431
|
-
const {
|
|
3520
|
+
const {
|
|
3521
|
+
ingredientGroups,
|
|
3522
|
+
selectedIndices,
|
|
3523
|
+
referencedIndices,
|
|
3524
|
+
dynamicOptionalIndices
|
|
3525
|
+
} = this.collectQuantityGroups(options);
|
|
3432
3526
|
const result = [];
|
|
3433
3527
|
for (let index = 0; index < this.ingredients.length; index++) {
|
|
3434
3528
|
if (!referencedIndices.has(index)) continue;
|
|
3435
3529
|
const orig = this.ingredients[index];
|
|
3530
|
+
let flags = orig.flags;
|
|
3531
|
+
if (dynamicOptionalIndices.has(index) && !flags?.includes("optional")) {
|
|
3532
|
+
flags = [...flags ?? [], "optional"];
|
|
3533
|
+
}
|
|
3436
3534
|
const ing = {
|
|
3437
3535
|
name: orig.name,
|
|
3438
3536
|
...orig.preparation && { preparation: orig.preparation },
|
|
3439
|
-
...
|
|
3537
|
+
...flags && { flags },
|
|
3440
3538
|
...orig.extras && { extras: orig.extras }
|
|
3441
3539
|
};
|
|
3442
3540
|
if (selectedIndices.has(index)) {
|
|
@@ -3487,6 +3585,59 @@ var _Recipe = class _Recipe {
|
|
|
3487
3585
|
}
|
|
3488
3586
|
return result;
|
|
3489
3587
|
}
|
|
3588
|
+
/**
|
|
3589
|
+
* Returns the list of cookware items that are used in the active variant.
|
|
3590
|
+
* Cookware in steps/sections not matching the active variant are excluded.
|
|
3591
|
+
* Hidden cookware is always excluded.
|
|
3592
|
+
*
|
|
3593
|
+
* @param options - Options for filtering:
|
|
3594
|
+
* - `choices`: The choices to apply (only `variant` is used)
|
|
3595
|
+
* @returns Array of Cookware objects referenced by active steps
|
|
3596
|
+
*
|
|
3597
|
+
* @example
|
|
3598
|
+
* ```typescript
|
|
3599
|
+
* // Get all cookware for the default variant
|
|
3600
|
+
* const cookware = recipe.getCookwareForVariant();
|
|
3601
|
+
*
|
|
3602
|
+
* // Get cookware for a specific variant
|
|
3603
|
+
* const veganCookware = recipe.getCookwareForVariant({ choices: { variant: 'vegan' } });
|
|
3604
|
+
* ```
|
|
3605
|
+
*/
|
|
3606
|
+
getCookwareForVariant(options) {
|
|
3607
|
+
const { choices } = options || {};
|
|
3608
|
+
const activeVariant = choices?.variant;
|
|
3609
|
+
const isDefaultVariant = activeVariant === void 0 || activeVariant === "*";
|
|
3610
|
+
const cookwareIndices = /* @__PURE__ */ new Set();
|
|
3611
|
+
for (const currentSection of this.sections) {
|
|
3612
|
+
if (currentSection.variants) {
|
|
3613
|
+
if (isDefaultVariant) {
|
|
3614
|
+
if (!currentSection.variants.includes("*")) continue;
|
|
3615
|
+
} else {
|
|
3616
|
+
if (!currentSection.variants.includes(activeVariant)) continue;
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
const allSteps = currentSection.content.filter(
|
|
3620
|
+
(item) => item.type === "step"
|
|
3621
|
+
);
|
|
3622
|
+
for (const currentStep of allSteps) {
|
|
3623
|
+
if (currentStep.variants) {
|
|
3624
|
+
if (isDefaultVariant) {
|
|
3625
|
+
if (!currentStep.variants.includes("*")) continue;
|
|
3626
|
+
} else {
|
|
3627
|
+
if (!currentStep.variants.includes(activeVariant)) continue;
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
for (const item of currentStep.items) {
|
|
3631
|
+
if (item.type === "cookware") {
|
|
3632
|
+
cookwareIndices.add(item.index);
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
return this.cookware.filter(
|
|
3638
|
+
(cw, idx) => cookwareIndices.has(idx) && !cw.flags?.includes("hidden")
|
|
3639
|
+
);
|
|
3640
|
+
}
|
|
3490
3641
|
/**
|
|
3491
3642
|
* Parses a recipe from a string.
|
|
3492
3643
|
* @param content - The recipe content to parse.
|
|
@@ -3502,9 +3653,14 @@ var _Recipe = class _Recipe {
|
|
|
3502
3653
|
const items = [];
|
|
3503
3654
|
let noteText = "";
|
|
3504
3655
|
let inNote = false;
|
|
3656
|
+
let stepVariants;
|
|
3657
|
+
let stepOptional;
|
|
3658
|
+
const discoveredVariants = /* @__PURE__ */ new Set();
|
|
3505
3659
|
for (const line of cleanContent) {
|
|
3506
3660
|
if (line.trim().length === 0) {
|
|
3507
|
-
flushPendingItems(section, items);
|
|
3661
|
+
flushPendingItems(section, items, stepVariants, stepOptional);
|
|
3662
|
+
stepVariants = void 0;
|
|
3663
|
+
stepOptional = void 0;
|
|
3508
3664
|
flushPendingNote(
|
|
3509
3665
|
section,
|
|
3510
3666
|
noteText ? this._parseNoteText(noteText) : []
|
|
@@ -3515,26 +3671,48 @@ var _Recipe = class _Recipe {
|
|
|
3515
3671
|
continue;
|
|
3516
3672
|
}
|
|
3517
3673
|
if (line.startsWith("=")) {
|
|
3518
|
-
flushPendingItems(section, items);
|
|
3674
|
+
flushPendingItems(section, items, stepVariants, stepOptional);
|
|
3675
|
+
stepVariants = void 0;
|
|
3676
|
+
stepOptional = void 0;
|
|
3519
3677
|
flushPendingNote(
|
|
3520
3678
|
section,
|
|
3521
3679
|
noteText ? this._parseNoteText(noteText) : []
|
|
3522
3680
|
);
|
|
3523
3681
|
noteText = "";
|
|
3682
|
+
let sectionName = line.replace(/^=+|=+$/g, "").trim();
|
|
3683
|
+
let sectionVariants;
|
|
3684
|
+
let sectionOptional;
|
|
3685
|
+
const sectionVarMatch = sectionName.match(variantTagRegex);
|
|
3686
|
+
if (sectionVarMatch?.groups) {
|
|
3687
|
+
const isOptionalPrefix = sectionVarMatch.groups.variantOptionalPrefix === "?";
|
|
3688
|
+
const names = (sectionVarMatch.groups.variantNames ?? "").split(",").map((n2) => n2.trim()).filter((n2) => n2.length > 0);
|
|
3689
|
+
if (names.length > 0) {
|
|
3690
|
+
sectionVariants = names;
|
|
3691
|
+
for (const v of names) discoveredVariants.add(v);
|
|
3692
|
+
}
|
|
3693
|
+
if (isOptionalPrefix) {
|
|
3694
|
+
sectionOptional = true;
|
|
3695
|
+
}
|
|
3696
|
+
sectionName = sectionName.slice(sectionVarMatch[0].length).trim();
|
|
3697
|
+
}
|
|
3524
3698
|
if (this.sections.length === 0 && section.isBlank()) {
|
|
3525
|
-
section.name =
|
|
3699
|
+
section.name = sectionName;
|
|
3700
|
+
if (sectionVariants) section.variants = sectionVariants;
|
|
3701
|
+
if (sectionOptional) section.optional = true;
|
|
3526
3702
|
} else {
|
|
3527
3703
|
if (!section.isBlank()) {
|
|
3528
3704
|
this.sections.push(section);
|
|
3529
3705
|
}
|
|
3530
|
-
section = new Section(
|
|
3706
|
+
section = new Section(sectionName, sectionVariants, sectionOptional);
|
|
3531
3707
|
}
|
|
3532
3708
|
blankLineBefore = true;
|
|
3533
3709
|
inNote = false;
|
|
3534
3710
|
continue;
|
|
3535
3711
|
}
|
|
3536
3712
|
if (blankLineBefore && line.startsWith(">")) {
|
|
3537
|
-
flushPendingItems(section, items);
|
|
3713
|
+
flushPendingItems(section, items, stepVariants, stepOptional);
|
|
3714
|
+
stepVariants = void 0;
|
|
3715
|
+
stepOptional = void 0;
|
|
3538
3716
|
noteText = line.substring(1).trim();
|
|
3539
3717
|
inNote = true;
|
|
3540
3718
|
blankLineBefore = false;
|
|
@@ -3549,11 +3727,31 @@ var _Recipe = class _Recipe {
|
|
|
3549
3727
|
blankLineBefore = false;
|
|
3550
3728
|
continue;
|
|
3551
3729
|
}
|
|
3730
|
+
let currentLine = line;
|
|
3731
|
+
if (items.length === 0) {
|
|
3732
|
+
const varMatch = currentLine.match(variantTagRegex);
|
|
3733
|
+
if (varMatch?.groups) {
|
|
3734
|
+
const isOptionalPrefix = varMatch.groups.variantOptionalPrefix === "?";
|
|
3735
|
+
const names = (varMatch.groups.variantNames ?? "").split(",").map((n2) => n2.trim()).filter((n2) => n2.length > 0);
|
|
3736
|
+
if (names.length > 0) {
|
|
3737
|
+
stepVariants = names;
|
|
3738
|
+
for (const v of names) discoveredVariants.add(v);
|
|
3739
|
+
}
|
|
3740
|
+
if (isOptionalPrefix) {
|
|
3741
|
+
stepOptional = true;
|
|
3742
|
+
}
|
|
3743
|
+
currentLine = currentLine.slice(varMatch[0].length);
|
|
3744
|
+
if (currentLine.trim().length === 0) {
|
|
3745
|
+
blankLineBefore = false;
|
|
3746
|
+
continue;
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3552
3750
|
let cursor = 0;
|
|
3553
|
-
for (const match of
|
|
3751
|
+
for (const match of currentLine.matchAll(tokensRegex)) {
|
|
3554
3752
|
const idx = match.index;
|
|
3555
3753
|
if (idx > cursor) {
|
|
3556
|
-
items.push(...parseMarkdownSegments(
|
|
3754
|
+
items.push(...parseMarkdownSegments(currentLine.slice(cursor, idx)));
|
|
3557
3755
|
}
|
|
3558
3756
|
const groups = match.groups;
|
|
3559
3757
|
if (groups.mIngredientName || groups.sIngredientName) {
|
|
@@ -3614,16 +3812,21 @@ var _Recipe = class _Recipe {
|
|
|
3614
3812
|
}
|
|
3615
3813
|
cursor = idx + match[0].length;
|
|
3616
3814
|
}
|
|
3617
|
-
if (cursor <
|
|
3618
|
-
items.push(...parseMarkdownSegments(
|
|
3815
|
+
if (cursor < currentLine.length) {
|
|
3816
|
+
items.push(...parseMarkdownSegments(currentLine.slice(cursor)));
|
|
3619
3817
|
}
|
|
3620
3818
|
blankLineBefore = false;
|
|
3621
3819
|
}
|
|
3622
|
-
flushPendingItems(section, items);
|
|
3820
|
+
flushPendingItems(section, items, stepVariants, stepOptional);
|
|
3623
3821
|
flushPendingNote(section, noteText ? this._parseNoteText(noteText) : []);
|
|
3624
3822
|
if (!section.isBlank()) {
|
|
3625
3823
|
this.sections.push(section);
|
|
3626
3824
|
}
|
|
3825
|
+
const metaVariants = this.metadata.variants ?? [];
|
|
3826
|
+
const allVariants = /* @__PURE__ */ new Set([...metaVariants, ...discoveredVariants]);
|
|
3827
|
+
if (allVariants.size > 0) {
|
|
3828
|
+
this.choices.variants = [...allVariants];
|
|
3829
|
+
}
|
|
3627
3830
|
this._populateIngredientQuantities();
|
|
3628
3831
|
}
|
|
3629
3832
|
/**
|
|
@@ -3934,7 +4137,11 @@ var _Recipe = class _Recipe {
|
|
|
3934
4137
|
newRecipe.metadata = deepClone(this.metadata);
|
|
3935
4138
|
newRecipe.ingredients = deepClone(this.ingredients);
|
|
3936
4139
|
newRecipe.sections = this.sections.map((section) => {
|
|
3937
|
-
const newSection = new Section(
|
|
4140
|
+
const newSection = new Section(
|
|
4141
|
+
section.name,
|
|
4142
|
+
section.variants,
|
|
4143
|
+
section.optional
|
|
4144
|
+
);
|
|
3938
4145
|
newSection.content = deepClone(section.content);
|
|
3939
4146
|
return newSection;
|
|
3940
4147
|
});
|
|
@@ -4807,13 +5014,53 @@ function isAlternativeSelected(recipe, choices, item, alternativeIndex) {
|
|
|
4807
5014
|
const selectedIndex = choices?.ingredientItems?.get(item.id);
|
|
4808
5015
|
return alternativeIndex === selectedIndex;
|
|
4809
5016
|
}
|
|
5017
|
+
function isSectionActive(section, variant) {
|
|
5018
|
+
if (!section.variants) return true;
|
|
5019
|
+
const isDefault = variant === void 0 || variant === "*";
|
|
5020
|
+
if (isDefault) {
|
|
5021
|
+
return section.variants.includes("*");
|
|
5022
|
+
}
|
|
5023
|
+
return section.variants.includes(variant);
|
|
5024
|
+
}
|
|
5025
|
+
function isStepActive(step, variant) {
|
|
5026
|
+
if (!step.variants) return true;
|
|
5027
|
+
const isDefault = variant === void 0 || variant === "*";
|
|
5028
|
+
if (isDefault) {
|
|
5029
|
+
return step.variants.includes("*");
|
|
5030
|
+
}
|
|
5031
|
+
return step.variants.includes(variant);
|
|
5032
|
+
}
|
|
5033
|
+
function getEffectiveChoices(recipe, variant) {
|
|
5034
|
+
const choices = { variant };
|
|
5035
|
+
if (variant === void 0 || variant === "*") return choices;
|
|
5036
|
+
const variantLower = variant.toLowerCase();
|
|
5037
|
+
for (const [itemId, alternatives] of recipe.choices.ingredientItems) {
|
|
5038
|
+
const matchIdx = alternatives.findIndex(
|
|
5039
|
+
(alt) => alt.note && alt.note.toLowerCase().includes(variantLower)
|
|
5040
|
+
);
|
|
5041
|
+
if (matchIdx >= 0) {
|
|
5042
|
+
if (!choices.ingredientItems) choices.ingredientItems = /* @__PURE__ */ new Map();
|
|
5043
|
+
choices.ingredientItems.set(itemId, matchIdx);
|
|
5044
|
+
}
|
|
5045
|
+
}
|
|
5046
|
+
for (const [groupId, subgroups] of recipe.choices.ingredientGroups) {
|
|
5047
|
+
const matchIdx = subgroups.findIndex(
|
|
5048
|
+
(sg) => sg.some(
|
|
5049
|
+
(alt) => alt.note && alt.note.toLowerCase().includes(variantLower)
|
|
5050
|
+
)
|
|
5051
|
+
);
|
|
5052
|
+
if (matchIdx >= 0) {
|
|
5053
|
+
if (!choices.ingredientGroups) choices.ingredientGroups = /* @__PURE__ */ new Map();
|
|
5054
|
+
choices.ingredientGroups.set(groupId, matchIdx);
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
return choices;
|
|
5058
|
+
}
|
|
4810
5059
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4811
5060
|
0 && (module.exports = {
|
|
4812
|
-
BadIndentationError,
|
|
4813
5061
|
CategoryConfig,
|
|
4814
5062
|
NoProductCatalogForCartError,
|
|
4815
5063
|
NoShoppingListForCartError,
|
|
4816
|
-
NoTabAsIndentError,
|
|
4817
5064
|
Pantry,
|
|
4818
5065
|
ProductCatalog,
|
|
4819
5066
|
Recipe,
|
|
@@ -4828,11 +5075,14 @@ function isAlternativeSelected(recipe, choices, item, alternativeIndex) {
|
|
|
4828
5075
|
formatQuantityWithUnit,
|
|
4829
5076
|
formatSingleValue,
|
|
4830
5077
|
formatUnit,
|
|
5078
|
+
getEffectiveChoices,
|
|
4831
5079
|
hasAlternatives,
|
|
4832
5080
|
isAlternativeSelected,
|
|
4833
5081
|
isAndGroup,
|
|
4834
5082
|
isGroupedItem,
|
|
5083
|
+
isSectionActive,
|
|
4835
5084
|
isSimpleGroup,
|
|
5085
|
+
isStepActive,
|
|
4836
5086
|
renderFractionAsVulgar
|
|
4837
5087
|
});
|
|
4838
5088
|
/* v8 ignore else -- @preserve */
|