@tmlmt/cooklang-parser 3.0.0-alpha.21 → 3.0.0-alpha.23

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 CHANGED
@@ -1833,6 +1833,10 @@ function parseNestedMetaVar(content, varName) {
1833
1833
  const match = content.match(nestedMetaVarRegex(varName));
1834
1834
  if (!match) return void 0;
1835
1835
  const nestedContent = match[1];
1836
+ const lines = nestedContent.split(/\r?\n/).filter((line) => line.trim() !== "");
1837
+ if (lines.length > 0 && lines[0].trim().startsWith("- ")) {
1838
+ return parseListItems(lines);
1839
+ }
1836
1840
  return parseNestedBlock(nestedContent);
1837
1841
  }
1838
1842
  function parseNestedBlock(content) {
@@ -1859,7 +1863,7 @@ function parseNestedBlock(content) {
1859
1863
  if (currentIndent !== baseIndent) {
1860
1864
  throw new BadIndentationError();
1861
1865
  }
1862
- const keyValueMatch = line.match(/^[ ]*([^:\n]+?):\s*(.*)$/);
1866
+ const keyValueMatch = line.match(/^ *([^:\n]+?):\s*(.*)$/);
1863
1867
  if (!keyValueMatch) {
1864
1868
  i2++;
1865
1869
  continue;
@@ -1871,7 +1875,7 @@ function parseNestedBlock(content) {
1871
1875
  let j = i2 + 1;
1872
1876
  while (j < lines.length) {
1873
1877
  const childLine = lines[j];
1874
- const childIndent = childLine.match(/^([ ]*)/)?.[1]?.length;
1878
+ const childIndent = childLine.match(/^( *)/)?.[1]?.length;
1875
1879
  if (childIndent && childIndent > baseIndent) {
1876
1880
  childLines.push(childLine);
1877
1881
  j++;
@@ -1882,14 +1886,7 @@ function parseNestedBlock(content) {
1882
1886
  if (childLines.length > 0) {
1883
1887
  const firstChildTrimmed = childLines[0].trim();
1884
1888
  if (firstChildTrimmed.startsWith("- ")) {
1885
- const reconstructedContent = `${key}:
1886
- ${childLines.join("\n")}`;
1887
- const listResult = parseListMetaVar(reconstructedContent, key);
1888
- if (listResult) {
1889
- result[key] = listResult.map(
1890
- (item) => parseMetadataValue(item)
1891
- );
1892
- }
1889
+ result[key] = parseListItems(childLines);
1893
1890
  } else {
1894
1891
  const childContent = childLines.join("\n");
1895
1892
  const nested = parseNestedBlock(childContent);
@@ -1897,16 +1894,67 @@ ${childLines.join("\n")}`;
1897
1894
  result[key] = nested;
1898
1895
  }
1899
1896
  }
1897
+ } else {
1898
+ result[key] = "";
1900
1899
  }
1901
1900
  i2 = j;
1902
1901
  } else {
1903
- result[key] = parseMetadataValue(rawValue);
1902
+ result[key] = parseSingleLineMetadataValue(rawValue);
1904
1903
  i2++;
1905
1904
  }
1906
1905
  }
1907
1906
  return result;
1908
1907
  }
1909
- function parseMetadataValue(rawValue) {
1908
+ function parseListItems(childLines) {
1909
+ const listIndent = childLines[0].match(/^( *)/)?.[1]?.length;
1910
+ const groups = [];
1911
+ let currentGroup = [];
1912
+ for (const line of childLines) {
1913
+ const indent = line.match(/^( *)/)?.[1]?.length;
1914
+ if (indent === listIndent && line.trim().startsWith("- ")) {
1915
+ if (currentGroup.length > 0) {
1916
+ groups.push(currentGroup);
1917
+ }
1918
+ currentGroup = [line];
1919
+ } else {
1920
+ currentGroup.push(line);
1921
+ }
1922
+ }
1923
+ groups.push(currentGroup);
1924
+ const isObjectItem = (group) => {
1925
+ if (group.length > 1) return true;
1926
+ const value = group[0].replace(/^\s*-\s*/, "").trim();
1927
+ return /^[^:\n]+:\s/.test(value);
1928
+ };
1929
+ const hasObjectItems = groups.some(isObjectItem);
1930
+ if (!hasObjectItems) {
1931
+ return groups.map((group) => {
1932
+ const value = group[0].replace(/^\s*-\s*/, "").trim();
1933
+ return parseSingleLineMetadataValue(value);
1934
+ });
1935
+ }
1936
+ const items = [];
1937
+ for (const group of groups) {
1938
+ const firstLine = group[0];
1939
+ const afterDash = firstLine.replace(/^\s*-\s*/, "");
1940
+ const dashPrefixMatch = firstLine.match(/^( *-\s*)/);
1941
+ const contentIndent = dashPrefixMatch?.[1]?.length;
1942
+ const objectLines = [" ".repeat(contentIndent) + afterDash];
1943
+ for (let k = 1; k < group.length; k++) {
1944
+ objectLines.push(group[k]);
1945
+ }
1946
+ const parsed = parseNestedBlock(objectLines.join("\n"));
1947
+ if (parsed) {
1948
+ items.push(parsed);
1949
+ } else {
1950
+ items.push(
1951
+ parseSingleLineMetadataValue(afterDash.trim())
1952
+ );
1953
+ }
1954
+ }
1955
+ return items;
1956
+ }
1957
+ function parseSingleLineMetadataValue(rawValue) {
1910
1958
  if (rawValue.startsWith("[") && rawValue.endsWith("]")) {
1911
1959
  return rawValue.slice(1, -1).split(",").map((item) => item.trim());
1912
1960
  }
@@ -1921,7 +1969,7 @@ function parseAnyMetaVar(content, varName) {
1921
1969
  const list = parseListMetaVar(content, varName);
1922
1970
  if (list) return list;
1923
1971
  const simple = parseSimpleMetaVar(content, varName);
1924
- if (simple) return parseMetadataValue(simple);
1972
+ if (simple) return parseSingleLineMetadataValue(simple);
1925
1973
  return void 0;
1926
1974
  }
1927
1975
  function getNumericValueFromYield(v) {
@@ -2005,7 +2053,7 @@ function extractMetadata(content) {
2005
2053
  const sourceName = parseSimpleMetaVar(metadataContent, "source.name");
2006
2054
  const sourceUrl = parseSimpleMetaVar(metadataContent, "source.url");
2007
2055
  const sourceAuthor = parseSimpleMetaVar(metadataContent, "source.author");
2008
- if (sourceNested) {
2056
+ if (sourceNested && !Array.isArray(sourceNested)) {
2009
2057
  const source = {};
2010
2058
  if (typeof sourceNested.name === "string") source.name = sourceNested.name;
2011
2059
  if (typeof sourceNested.url === "string") source.url = sourceNested.url;
@@ -2025,7 +2073,7 @@ function extractMetadata(content) {
2025
2073
  const prepTime = parseSimpleMetaVar(metadataContent, "prep time") ?? parseSimpleMetaVar(metadataContent, "time.prep");
2026
2074
  const cookTime = parseSimpleMetaVar(metadataContent, "cook time") ?? parseSimpleMetaVar(metadataContent, "time.cook");
2027
2075
  const totalTime = parseSimpleMetaVar(metadataContent, "time required") ?? parseSimpleMetaVar(metadataContent, "time") ?? parseSimpleMetaVar(metadataContent, "duration");
2028
- if (timeNested) {
2076
+ if (timeNested && !Array.isArray(timeNested)) {
2029
2077
  const time = {};
2030
2078
  if (typeof timeNested.prep === "string") time.prep = timeNested.prep;
2031
2079
  if (typeof timeNested.cook === "string") time.cook = timeNested.cook;
@@ -2092,7 +2140,7 @@ function unionOfSets(s1, s2) {
2092
2140
  }
2093
2141
  function getAlternativeSignature(alternatives) {
2094
2142
  if (!alternatives || alternatives.length === 0) return null;
2095
- return alternatives.map((a2) => a2.index).sort((a2, b) => a2 - b).join(",");
2143
+ return alternatives.flat().map((a2) => a2.index).sort((a2, b) => a2 - b).join(",");
2096
2144
  }
2097
2145
 
2098
2146
  // src/classes/pantry.ts
@@ -3405,7 +3453,7 @@ var _Recipe = class _Recipe {
3405
3453
  const currentSubgroupIdx = groupSubgroups.findIndex(
3406
3454
  (sg) => sg.some((alt) => alt.itemId === item.id)
3407
3455
  );
3408
- alternativeRefs = groupSubgroups.filter((_, idx) => idx !== currentSubgroupIdx).flatMap(
3456
+ alternativeRefs = groupSubgroups.filter((_, idx) => idx !== currentSubgroupIdx).map(
3409
3457
  (subgroup) => subgroup.map((otherAlt) => {
3410
3458
  const ref = {
3411
3459
  index: otherAlt.index
@@ -3444,7 +3492,7 @@ var _Recipe = class _Recipe {
3444
3492
  };
3445
3493
  ref.quantities = [altQty];
3446
3494
  }
3447
- return ref;
3495
+ return [ref];
3448
3496
  });
3449
3497
  }
3450
3498
  const altIndices = getAlternativeSignature(alternativeRefs) ?? "";
@@ -3465,28 +3513,36 @@ var _Recipe = class _Recipe {
3465
3513
  if (!groupsForIng.has(signature)) {
3466
3514
  groupsForIng.set(signature, {
3467
3515
  quantities: [],
3468
- alternativeQuantities: /* @__PURE__ */ new Map()
3516
+ alternativeQuantities: /* @__PURE__ */ new Map(),
3517
+ alternativeSubgroups: []
3469
3518
  });
3470
3519
  }
3471
3520
  const group = groupsForIng.get(signature);
3472
3521
  group.quantities.push(quantityEntry);
3473
- for (const ref of alternativeRefs ?? []) {
3474
- if (!group.alternativeQuantities.has(ref.index)) {
3475
- group.alternativeQuantities.set(ref.index, []);
3476
- }
3477
- for (const altQty of ref.quantities ?? []) {
3478
- const extended = toExtendedUnit({
3479
- quantity: altQty.quantity,
3480
- unit: altQty.unit
3481
- });
3482
- if (altQty.equivalents?.length) {
3483
- const eqEntries = [
3484
- extended,
3485
- ...altQty.equivalents.map((eq) => toExtendedUnit(eq))
3486
- ];
3487
- group.alternativeQuantities.get(ref.index).push({ or: eqEntries });
3488
- } else {
3489
- group.alternativeQuantities.get(ref.index).push(extended);
3522
+ if (alternativeRefs && alternativeRefs.length > 0 && group.alternativeSubgroups.length === 0) {
3523
+ group.alternativeSubgroups = alternativeRefs.map(
3524
+ (subgroup) => subgroup.map((ref) => ref.index)
3525
+ );
3526
+ }
3527
+ for (const subgroup of alternativeRefs ?? []) {
3528
+ for (const ref of subgroup) {
3529
+ if (!group.alternativeQuantities.has(ref.index)) {
3530
+ group.alternativeQuantities.set(ref.index, []);
3531
+ }
3532
+ for (const altQty of ref.quantities ?? []) {
3533
+ const extended = toExtendedUnit({
3534
+ quantity: altQty.quantity,
3535
+ unit: altQty.unit
3536
+ });
3537
+ if (altQty.equivalents?.length) {
3538
+ const eqEntries = [
3539
+ extended,
3540
+ ...altQty.equivalents.map((eq) => toExtendedUnit(eq))
3541
+ ];
3542
+ group.alternativeQuantities.get(ref.index).push({ or: eqEntries });
3543
+ } else {
3544
+ group.alternativeQuantities.get(ref.index).push(extended);
3545
+ }
3490
3546
  }
3491
3547
  }
3492
3548
  }
@@ -3609,17 +3665,25 @@ var _Recipe = class _Recipe {
3609
3665
  this.unitSystem
3610
3666
  );
3611
3667
  const flattened = flattenPlainUnitGroup(summed);
3612
- const alternatives = group.alternativeQuantities.size > 0 ? [...group.alternativeQuantities].map(([altIdx, altQtys]) => ({
3613
- index: altIdx,
3614
- ...altQtys.length > 0 && {
3615
- quantities: flattenPlainUnitGroup(
3616
- addEquivalentsAndSimplify(altQtys, this.unitSystem)
3617
- ).flatMap(
3618
- /* v8 ignore next -- item.and branch requires complex nested AND-with-equivalents structure */
3619
- (item) => "quantity" in item ? [item] : item.and
3620
- )
3621
- }
3622
- })) : void 0;
3668
+ let alternatives;
3669
+ if (group.alternativeSubgroups.length > 0) {
3670
+ alternatives = group.alternativeSubgroups.map(
3671
+ (subgroupIndices) => subgroupIndices.map((altIdx) => {
3672
+ const altQtys = group.alternativeQuantities.get(altIdx);
3673
+ return {
3674
+ index: altIdx,
3675
+ ...altQtys.length > 0 && {
3676
+ quantities: flattenPlainUnitGroup(
3677
+ addEquivalentsAndSimplify(altQtys, this.unitSystem)
3678
+ ).flatMap(
3679
+ /* v8 ignore next -- item.and branch requires complex nested AND-with-equivalents structure */
3680
+ (item) => "quantity" in item ? [item] : item.and
3681
+ )
3682
+ }
3683
+ };
3684
+ })
3685
+ );
3686
+ }
3623
3687
  for (const gq of flattened) {
3624
3688
  if ("and" in gq) {
3625
3689
  quantityGroups.push({
@@ -5144,6 +5208,7 @@ function getEffectiveChoices(recipe, variant) {
5144
5208
  // v8 ignore if -- @preserve
5145
5209
  /* v8 ignore else -- expliciting error type -- @preserve */
5146
5210
  /* v8 ignore next 4 -- @preserve: defensive guard; regex always matches */
5211
+ /* v8 ignore else -- @preserve: empty non nested block will in practice be detected earlier */
5147
5212
  // v8 ignore if -- @preserve: defensive type guard
5148
5213
  /* v8 ignore if -- @preserve */
5149
5214
  // v8 ignore next -- @preserve