@recipe-book/core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -0
- package/README.md +1 -1
- package/dist/{chunk-VWVIBCTG.cjs → chunk-D5CV3UF7.cjs} +188 -173
- package/dist/chunk-D5CV3UF7.cjs.map +1 -0
- package/dist/errors.js.map +1 -1
- package/dist/format/number.js +1 -1
- package/dist/format/number.js.map +1 -1
- package/dist/format/options.js.map +1 -1
- package/dist/index.cjs +83 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -19
- package/dist/index.d.ts +35 -19
- package/dist/ingredient/parse.js +27 -22
- package/dist/ingredient/parse.js.map +1 -1
- package/dist/issues.js.map +1 -1
- package/dist/quantity/amount.js +11 -0
- package/dist/quantity/amount.js.map +1 -1
- package/dist/quantity/parse.js +58 -81
- package/dist/quantity/parse.js.map +1 -1
- package/dist/quantity/scale.js +5 -2
- package/dist/quantity/scale.js.map +1 -1
- package/dist/recipe/parse.js +70 -55
- package/dist/recipe/parse.js.map +1 -1
- package/dist/recipe/schema.js +8 -6
- package/dist/recipe/schema.js.map +1 -1
- package/dist/resolve.js +6 -1
- package/dist/resolve.js.map +1 -1
- package/dist/schema-org/find.js +46 -13
- package/dist/schema-org/find.js.map +1 -1
- package/dist/schema-org/from-json-ld.js +127 -57
- package/dist/schema-org/from-json-ld.js.map +1 -1
- package/dist/schema-org/text.js.map +1 -1
- package/dist/schema-org/to-json-ld.js +12 -15
- package/dist/schema-org/to-json-ld.js.map +1 -1
- package/dist/schema-org.cjs +231 -132
- package/dist/schema-org.cjs.map +1 -1
- package/dist/schema-org.d.cts +32 -19
- package/dist/schema-org.d.ts +32 -19
- package/dist/steps/temperature.js +13 -13
- package/dist/steps/temperature.js.map +1 -1
- package/dist/{types-DSMf45H5.d.cts → types-BknM8KOd.d.cts} +4 -3
- package/dist/{types-DSMf45H5.d.ts → types-BknM8KOd.d.ts} +4 -3
- package/dist/units/convert.js +16 -8
- package/dist/units/convert.js.map +1 -1
- package/dist/units/temperature.js +7 -1
- package/dist/units/temperature.js.map +1 -1
- package/dist/validation.js +1 -1
- package/dist/validation.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-VWVIBCTG.cjs.map +0 -1
|
@@ -392,7 +392,7 @@ var RecipeValidationError = class extends RecipeBookError {
|
|
|
392
392
|
* @param subject - What was validated, shown as given at the start of the
|
|
393
393
|
* message, e.g. `'"Buttermilk pancakes"'`. Recipe Book's own errors quote
|
|
394
394
|
* the recipe's name or the input text like a JSON string, cut with "…"
|
|
395
|
-
* when longer than 60
|
|
395
|
+
* when longer than 60 UTF-16 code units.
|
|
396
396
|
*/
|
|
397
397
|
constructor(code, issues, subject = code === "invalid_recipe" ? "Recipe" : code === "invalid_quantity" ? "Quantity" : "Ingredient") {
|
|
398
398
|
super(code, summarize(subject, issues));
|
|
@@ -507,6 +507,16 @@ function checkQuantity(fn, quantity) {
|
|
|
507
507
|
`${fn}: amount must be a number or a { min, max } range, received ${describeValue(amount)}.`
|
|
508
508
|
);
|
|
509
509
|
}
|
|
510
|
+
function checkResult(fn, amount) {
|
|
511
|
+
for (const value of amountEnds(amount)) {
|
|
512
|
+
if (!isPositive(value)) {
|
|
513
|
+
throw invalidArgument(
|
|
514
|
+
`${fn}: the result must be a finite number greater than 0, but it is ${describeValue(value)}.`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return amount;
|
|
519
|
+
}
|
|
510
520
|
function checkFactor(fn, name, value) {
|
|
511
521
|
if (!isPositive(value)) {
|
|
512
522
|
throw invalidArgument(
|
|
@@ -522,45 +532,9 @@ function invalidArgument(message) {
|
|
|
522
532
|
}
|
|
523
533
|
|
|
524
534
|
// src/quantity/parse.ts
|
|
525
|
-
var
|
|
526
|
-
"\xBC": [1, 4],
|
|
527
|
-
"\xBD": [1, 2],
|
|
528
|
-
"\xBE": [3, 4],
|
|
529
|
-
"\u2150": [1, 7],
|
|
530
|
-
"\u2151": [1, 9],
|
|
531
|
-
"\u2152": [1, 10],
|
|
532
|
-
"\u2153": [1, 3],
|
|
533
|
-
"\u2154": [2, 3],
|
|
534
|
-
"\u2155": [1, 5],
|
|
535
|
-
"\u2156": [2, 5],
|
|
536
|
-
"\u2157": [3, 5],
|
|
537
|
-
"\u2158": [4, 5],
|
|
538
|
-
"\u2159": [1, 6],
|
|
539
|
-
"\u215A": [5, 6],
|
|
540
|
-
"\u215B": [1, 8],
|
|
541
|
-
"\u215C": [3, 8],
|
|
542
|
-
"\u215D": [5, 8],
|
|
543
|
-
"\u215E": [7, 8]
|
|
544
|
-
};
|
|
535
|
+
var VULGAR = "[\xBC-\xBE\u2150-\u215E]";
|
|
545
536
|
var ARTICLE = /^an?\s+(?=\D)/i;
|
|
546
|
-
var
|
|
547
|
-
function getPatterns() {
|
|
548
|
-
return patterns ?? (patterns = compilePatterns());
|
|
549
|
-
}
|
|
550
|
-
function compilePatterns() {
|
|
551
|
-
const vulgar = `[${Object.keys(VULGAR_FRACTIONS).join("")}]`;
|
|
552
|
-
const slash = "[/\u2044]";
|
|
553
|
-
const number = String.raw`(?:\d+\s+)?\d+\s*${slash}\s*\d+|(?:\d+\s*)?${vulgar}|\d+(?:\.\d+)?|\.\d+`;
|
|
554
|
-
return {
|
|
555
|
-
amount: new RegExp(
|
|
556
|
-
String.raw`^(${number})(?:\s*[-–—]\s*(${number})|\s+to\s+(${number}))?`,
|
|
557
|
-
"i"
|
|
558
|
-
),
|
|
559
|
-
asciiFraction: new RegExp(
|
|
560
|
-
String.raw`^(?:(\d+)\s+)?(\d+)\s*${slash}\s*(\d+)$`
|
|
561
|
-
)
|
|
562
|
-
};
|
|
563
|
-
}
|
|
537
|
+
var amountPattern;
|
|
564
538
|
function parseQuantity(text) {
|
|
565
539
|
const issues = [];
|
|
566
540
|
if (typeof text !== "string") {
|
|
@@ -577,7 +551,8 @@ function parseQuantity(text) {
|
|
|
577
551
|
}
|
|
578
552
|
function readQuantity(issues, value, path) {
|
|
579
553
|
if (typeof value === "string") {
|
|
580
|
-
|
|
554
|
+
const quantity = readQuantityText(issues, value, path);
|
|
555
|
+
return quantity && readTextAmount(issues, quantity.amount, path) ? quantity : void 0;
|
|
581
556
|
}
|
|
582
557
|
if (typeof value === "number") {
|
|
583
558
|
const amount = readPositive(issues, value, path);
|
|
@@ -585,7 +560,35 @@ function readQuantity(issues, value, path) {
|
|
|
585
560
|
}
|
|
586
561
|
const record = asRecord(value);
|
|
587
562
|
if (record) {
|
|
588
|
-
|
|
563
|
+
checkFields(issues, record, { amount: true, unit: true }, path);
|
|
564
|
+
const amount = readAmount(issues, record.amount, [...path, "amount"]);
|
|
565
|
+
const { unit } = record;
|
|
566
|
+
if (unit === void 0) {
|
|
567
|
+
return amount === void 0 ? void 0 : { amount };
|
|
568
|
+
}
|
|
569
|
+
if (typeof unit !== "string") {
|
|
570
|
+
return error(
|
|
571
|
+
issues,
|
|
572
|
+
"invalid_type",
|
|
573
|
+
[...path, "unit"],
|
|
574
|
+
`Expected a unit such as "g" or "cup", received ${describeValue(unit)}.`
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
const id = findUnit(unit);
|
|
578
|
+
if (!id) {
|
|
579
|
+
const problem = unknownUnit(
|
|
580
|
+
unit,
|
|
581
|
+
amount === void 0 ? void 0 : typeof amount === "number" ? String(amount) : `{ amount: { min: ${amount.min}, max: ${amount.max} } }`
|
|
582
|
+
);
|
|
583
|
+
return error(
|
|
584
|
+
issues,
|
|
585
|
+
problem.code,
|
|
586
|
+
[...path, "unit"],
|
|
587
|
+
problem.message,
|
|
588
|
+
problem.suggestion
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
return amount === void 0 ? void 0 : { amount, unit: id };
|
|
589
592
|
}
|
|
590
593
|
return error(
|
|
591
594
|
issues,
|
|
@@ -640,12 +643,18 @@ function matchAmount(text, allowZero) {
|
|
|
640
643
|
if (article) {
|
|
641
644
|
return { amount: 1, amountText: "1", rest: text.slice(article[0].length) };
|
|
642
645
|
}
|
|
643
|
-
|
|
646
|
+
if (!amountPattern) {
|
|
647
|
+
const number = String.raw`(?:\d+\s+)?\d+\s*[/⁄]\s*\d+|(?:\d+\s*)?${VULGAR}|\d+(?:\.\d+)?|\.\d+`;
|
|
648
|
+
amountPattern = new RegExp(
|
|
649
|
+
String.raw`^(${number})(?:(?:\s*[-–—]\s*|\s+to\s+)(${number}))?`,
|
|
650
|
+
"i"
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
const match = amountPattern.exec(text);
|
|
644
654
|
if (!match) {
|
|
645
655
|
return void 0;
|
|
646
656
|
}
|
|
647
|
-
const [matched, lowText = "",
|
|
648
|
-
const highText = highTextDash ?? highTextTo;
|
|
657
|
+
const [matched, lowText = "", highText] = match;
|
|
649
658
|
const low = toNumber(lowText);
|
|
650
659
|
const high = highText === void 0 ? low : toNumber(highText);
|
|
651
660
|
if (low === Infinity || high === Infinity) {
|
|
@@ -691,50 +700,25 @@ function unreadable(text) {
|
|
|
691
700
|
message: text === "" ? 'Expected a quantity such as "1 \xBD cups", received an empty string.' : `Couldn't read ${quote(text)}. Use a number or fraction, e.g. "1 \xBD cups".`
|
|
692
701
|
};
|
|
693
702
|
}
|
|
703
|
+
var fractions = /* @__PURE__ */ new Map();
|
|
694
704
|
function toNumber(text) {
|
|
695
|
-
const ascii =
|
|
705
|
+
const ascii = /^(?:(\d+)\s+)?(\d+)\s*[/⁄]\s*(\d+)$/.exec(text);
|
|
696
706
|
if (ascii) {
|
|
697
707
|
const [, whole = "0", numerator, denominator] = ascii;
|
|
698
708
|
return Number(whole) + Number(numerator) / (Number(denominator) || NaN);
|
|
699
709
|
}
|
|
700
|
-
const
|
|
701
|
-
if (
|
|
702
|
-
|
|
703
|
-
|
|
710
|
+
const last = text.slice(-1);
|
|
711
|
+
if (last > "9") {
|
|
712
|
+
let fraction = fractions.get(last);
|
|
713
|
+
if (fraction === void 0) {
|
|
714
|
+
const [numerator, denominator] = last.normalize("NFKD").split("\u2044");
|
|
715
|
+
fraction = Number(numerator) / Number(denominator);
|
|
716
|
+
fractions.set(last, fraction);
|
|
717
|
+
}
|
|
718
|
+
return Number(text.slice(0, -1)) + fraction;
|
|
704
719
|
}
|
|
705
720
|
return Number(text);
|
|
706
721
|
}
|
|
707
|
-
function readQuantityObject(issues, record, path) {
|
|
708
|
-
checkFields(issues, record, { amount: true, unit: true }, path);
|
|
709
|
-
const amount = readAmount(issues, record.amount, [...path, "amount"]);
|
|
710
|
-
const { unit } = record;
|
|
711
|
-
if (unit === void 0) {
|
|
712
|
-
return amount === void 0 ? void 0 : { amount };
|
|
713
|
-
}
|
|
714
|
-
if (typeof unit !== "string") {
|
|
715
|
-
return error(
|
|
716
|
-
issues,
|
|
717
|
-
"invalid_type",
|
|
718
|
-
[...path, "unit"],
|
|
719
|
-
`Expected a unit such as "g" or "cup", received ${describeValue(unit)}.`
|
|
720
|
-
);
|
|
721
|
-
}
|
|
722
|
-
const id = findUnit(unit);
|
|
723
|
-
if (!id) {
|
|
724
|
-
const problem = unknownUnit(
|
|
725
|
-
unit,
|
|
726
|
-
amount === void 0 ? void 0 : typeof amount === "number" ? String(amount) : `{ amount: { min: ${amount.min}, max: ${amount.max} } }`
|
|
727
|
-
);
|
|
728
|
-
return error(
|
|
729
|
-
issues,
|
|
730
|
-
problem.code,
|
|
731
|
-
[...path, "unit"],
|
|
732
|
-
problem.message,
|
|
733
|
-
problem.suggestion
|
|
734
|
-
);
|
|
735
|
-
}
|
|
736
|
-
return amount === void 0 ? void 0 : { amount, unit: id };
|
|
737
|
-
}
|
|
738
722
|
function readAmount(issues, value, path) {
|
|
739
723
|
if (value === void 0) {
|
|
740
724
|
return error(
|
|
@@ -773,16 +757,19 @@ function readAmount(issues, value, path) {
|
|
|
773
757
|
);
|
|
774
758
|
}
|
|
775
759
|
function readPositive(issues, value, path) {
|
|
776
|
-
if (isPositive(value)) {
|
|
760
|
+
if (isPositive(value) && value >= 1e-300 && value <= 1e290) {
|
|
777
761
|
return value;
|
|
778
762
|
}
|
|
779
763
|
return error(
|
|
780
764
|
issues,
|
|
781
765
|
typeof value === "number" ? "invalid_amount" : "invalid_type",
|
|
782
766
|
path,
|
|
783
|
-
`Expected a positive number, received ${describeValue(value)}.`
|
|
767
|
+
isPositive(value) ? `Expected a number from 1e-300 to 1e290, received ${describeValue(value)}.` : `Expected a positive number, received ${describeValue(value)}.`
|
|
784
768
|
);
|
|
785
769
|
}
|
|
770
|
+
function readTextAmount(issues, amount, path) {
|
|
771
|
+
return (typeof amount === "number" ? [amount] : [amount.min, amount.max]).every((end) => readPositive(issues, end, path) !== void 0);
|
|
772
|
+
}
|
|
786
773
|
|
|
787
774
|
// src/recipe/duration.ts
|
|
788
775
|
var ISO_DURATION = /^P(?!$)(?:(\d+(?:[.,]\d+)?)W)?(?:(\d+(?:[.,]\d+)?)D)?(?:T(?=\d)(?:(\d+(?:[.,]\d+)?)H)?(?:(\d+(?:[.,]\d+)?)M)?(?:(\d+(?:[.,]\d+)?)S)?)?$/i;
|
|
@@ -1008,9 +995,14 @@ var NUTRIENT_EXAMPLES = {
|
|
|
1008
995
|
mg: 'milligrams, e.g. 300, "300 mg", or "0.3 g"'
|
|
1009
996
|
};
|
|
1010
997
|
function parseRecipe(input, options) {
|
|
998
|
+
return parseRecipeWith(
|
|
999
|
+
input,
|
|
1000
|
+
readLineOption("parseRecipe", options) ?? "pass parseIngredientLine: parseRecipe(input, { parseIngredientLine })"
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
function parseRecipeWith(input, lines) {
|
|
1011
1004
|
const issues = [];
|
|
1012
|
-
const
|
|
1013
|
-
const recipe = readRecipe(issues, input, readLine2);
|
|
1005
|
+
const recipe = readRecipe(issues, input, lines);
|
|
1014
1006
|
const label2 = recipe && quote(recipe.name);
|
|
1015
1007
|
return toResult("invalid_recipe", recipe, issues, label2);
|
|
1016
1008
|
}
|
|
@@ -1024,7 +1016,7 @@ function readLineOption(fn, options) {
|
|
|
1024
1016
|
}
|
|
1025
1017
|
return reader;
|
|
1026
1018
|
}
|
|
1027
|
-
function readRecipe(issues, value,
|
|
1019
|
+
function readRecipe(issues, value, lines) {
|
|
1028
1020
|
const input = asRecord(value);
|
|
1029
1021
|
if (!input) {
|
|
1030
1022
|
return error(
|
|
@@ -1054,7 +1046,7 @@ function readRecipe(issues, value, readLine2) {
|
|
|
1054
1046
|
input.ingredients,
|
|
1055
1047
|
["ingredients"],
|
|
1056
1048
|
"ingredients",
|
|
1057
|
-
(issues2, value2, path) => readIngredient(issues2, value2, path,
|
|
1049
|
+
(issues2, value2, path) => readIngredient(issues2, value2, path, lines)
|
|
1058
1050
|
);
|
|
1059
1051
|
const steps = readList(
|
|
1060
1052
|
issues,
|
|
@@ -1104,15 +1096,11 @@ function readRecipe(issues, value, readLine2) {
|
|
|
1104
1096
|
});
|
|
1105
1097
|
}
|
|
1106
1098
|
function readList(issues, pending, value, path, noun, readItem) {
|
|
1107
|
-
const entries = value === void 0 ? [] : readArray(issues, value, path, noun);
|
|
1108
|
-
if (!entries) {
|
|
1109
|
-
return [];
|
|
1110
|
-
}
|
|
1111
1099
|
const sections = [];
|
|
1112
1100
|
let loose;
|
|
1113
1101
|
let count2 = 0;
|
|
1114
1102
|
const read = (entry, entryPath) => readItem(issues, entry, entryPath, ++count2);
|
|
1115
|
-
|
|
1103
|
+
const visit = (entry, i) => {
|
|
1116
1104
|
const entryPath = [...path, i];
|
|
1117
1105
|
const section = asSection(entry);
|
|
1118
1106
|
if (section) {
|
|
@@ -1135,7 +1123,10 @@ function readList(issues, pending, value, path, noun, readItem) {
|
|
|
1135
1123
|
if (item) {
|
|
1136
1124
|
loose.items.push(item);
|
|
1137
1125
|
}
|
|
1138
|
-
}
|
|
1126
|
+
};
|
|
1127
|
+
if (value !== void 0 && !readArray(issues, value, path, noun, visit)) {
|
|
1128
|
+
return [];
|
|
1129
|
+
}
|
|
1139
1130
|
if (sections.length === 0) {
|
|
1140
1131
|
warning(issues, "empty_list", path, `Recipe has no ${noun}.`);
|
|
1141
1132
|
return [];
|
|
@@ -1164,10 +1155,13 @@ function readSection(issues, record, path, noun, read) {
|
|
|
1164
1155
|
const title = readText(issues, record.title, [...path, "title"]);
|
|
1165
1156
|
const items = [];
|
|
1166
1157
|
const itemsPath = [...path, "items"];
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1158
|
+
let visited = 0;
|
|
1159
|
+
const entries = readArray(
|
|
1160
|
+
issues,
|
|
1161
|
+
record.items,
|
|
1162
|
+
itemsPath,
|
|
1163
|
+
noun,
|
|
1164
|
+
(entry, j) => {
|
|
1171
1165
|
visited++;
|
|
1172
1166
|
const itemPath = [...itemsPath, j];
|
|
1173
1167
|
if (asSection(entry)) {
|
|
@@ -1183,10 +1177,10 @@ function readSection(issues, record, path, noun, read) {
|
|
|
1183
1177
|
if (item) {
|
|
1184
1178
|
items.push(item);
|
|
1185
1179
|
}
|
|
1186
|
-
});
|
|
1187
|
-
if (visited === 0) {
|
|
1188
|
-
warning(issues, "empty_list", itemsPath, `Section has no ${noun}.`);
|
|
1189
1180
|
}
|
|
1181
|
+
);
|
|
1182
|
+
if (entries && visited === 0) {
|
|
1183
|
+
warning(issues, "empty_list", itemsPath, `Section has no ${noun}.`);
|
|
1190
1184
|
}
|
|
1191
1185
|
checkFields(issues, record, SECTION_FIELDS, path);
|
|
1192
1186
|
return {
|
|
@@ -1199,29 +1193,44 @@ function readSection(issues, record, path, noun, read) {
|
|
|
1199
1193
|
};
|
|
1200
1194
|
}
|
|
1201
1195
|
function readItems(issues, pending, value, path, noun, readItem) {
|
|
1202
|
-
|
|
1203
|
-
|
|
1196
|
+
let n = 0;
|
|
1197
|
+
const drafts = value === void 0 ? [] : readArray(
|
|
1198
|
+
issues,
|
|
1199
|
+
value,
|
|
1200
|
+
path,
|
|
1201
|
+
noun,
|
|
1202
|
+
(entry, i) => readItem(issues, entry, [...path, i], ++n)
|
|
1203
|
+
);
|
|
1204
|
+
if (!drafts) {
|
|
1204
1205
|
return [];
|
|
1205
1206
|
}
|
|
1206
|
-
let n = 0;
|
|
1207
|
-
const drafts = entries.flatMap((entry, i) => {
|
|
1208
|
-
const draft = readItem(issues, entry, [...path, i], ++n);
|
|
1209
|
-
return draft ? [draft] : [];
|
|
1210
|
-
});
|
|
1211
1207
|
const ids = assignIds(drafts, pending);
|
|
1212
1208
|
return drafts.map((draft, i) => draft.build(ids[i]));
|
|
1213
1209
|
}
|
|
1214
|
-
function readArray(issues, value, path, noun) {
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1210
|
+
function readArray(issues, value, path, noun, read) {
|
|
1211
|
+
if (!Array.isArray(value)) {
|
|
1212
|
+
return error(
|
|
1213
|
+
issues,
|
|
1214
|
+
"invalid_type",
|
|
1215
|
+
path,
|
|
1216
|
+
`Expected a list of ${noun}, received ${describeValue(value)}.`
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
const kept = [];
|
|
1220
|
+
for (let i = 0, n = value.length; i < n; i++) {
|
|
1221
|
+
if (i in value) {
|
|
1222
|
+
const entry = value[i];
|
|
1223
|
+
const result = hasOwn(value, i) && read(entry, i);
|
|
1224
|
+
if (result) {
|
|
1225
|
+
kept.push(result);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
return kept;
|
|
1221
1230
|
}
|
|
1222
|
-
function readIngredient(issues, value, path,
|
|
1223
|
-
if (typeof value === "string" &&
|
|
1224
|
-
const result =
|
|
1231
|
+
function readIngredient(issues, value, path, lines) {
|
|
1232
|
+
if (typeof value === "string" && typeof lines === "function") {
|
|
1233
|
+
const result = lines(value);
|
|
1225
1234
|
for (const issue2 of result.success ? result.warnings : result.error.issues) {
|
|
1226
1235
|
issues.push({ ...issue2, path: [...path, ...issue2.path] });
|
|
1227
1236
|
}
|
|
@@ -1233,7 +1242,7 @@ function readIngredient(issues, value, path, readLine2) {
|
|
|
1233
1242
|
issues,
|
|
1234
1243
|
"invalid_type",
|
|
1235
1244
|
path,
|
|
1236
|
-
`Expected an ingredient such as ${
|
|
1245
|
+
`Expected an ingredient such as ${typeof lines === "function" ? '"2 cups flour" or ' : ""}{ name: "flour", quantity: "2 cups" }, received ${describeValue(value)}.${typeof value === "string" && typeof lines === "string" ? ` To read ingredient lines, ${lines}.` : ""}`
|
|
1237
1246
|
);
|
|
1238
1247
|
}
|
|
1239
1248
|
const position = issues.length;
|
|
@@ -1274,22 +1283,13 @@ function readIngredient(issues, value, path, readLine2) {
|
|
|
1274
1283
|
};
|
|
1275
1284
|
}
|
|
1276
1285
|
function readEquivalents(issues, value, path) {
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
path,
|
|
1285
|
-
`Expected a list of quantities, e.g. ["190 g"], received ${describeValue(value)}.`
|
|
1286
|
-
);
|
|
1287
|
-
return [];
|
|
1288
|
-
}
|
|
1289
|
-
return value.flatMap((entry, i) => {
|
|
1290
|
-
const quantity = readQuantity(issues, entry, [...path, i]);
|
|
1291
|
-
return quantity === void 0 ? [] : [quantity];
|
|
1292
|
-
});
|
|
1286
|
+
return value !== void 0 && readArray(
|
|
1287
|
+
issues,
|
|
1288
|
+
value,
|
|
1289
|
+
path,
|
|
1290
|
+
'quantities, e.g. ["190 g"]',
|
|
1291
|
+
(entry, i) => readQuantity(issues, entry, [...path, i])
|
|
1292
|
+
) || [];
|
|
1293
1293
|
}
|
|
1294
1294
|
function readStep(issues, value, path, n) {
|
|
1295
1295
|
const isText = typeof value === "string";
|
|
@@ -1524,10 +1524,11 @@ function readRangeText(issues, text, path, expected) {
|
|
|
1524
1524
|
return error(issues, match.code, path, match.message);
|
|
1525
1525
|
}
|
|
1526
1526
|
const { amount, rest } = match;
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
}
|
|
1527
|
+
const range = typeof amount === "number" ? { amount } : { amount: amount.min, max: amount.max };
|
|
1528
|
+
if (readPositive(issues, range.amount, path) === void 0 || range.max !== void 0 && readPositive(issues, range.max, path) === void 0) {
|
|
1529
|
+
return void 0;
|
|
1530
|
+
}
|
|
1531
|
+
return { range, rest };
|
|
1531
1532
|
}
|
|
1532
1533
|
function readRange(issues, record, path, missingAmount) {
|
|
1533
1534
|
const value = record.amount;
|
|
@@ -1750,26 +1751,26 @@ var ABOUT = /(about|approx(?:imately|\.)?) /iy;
|
|
|
1750
1751
|
var PHRASE = /((?:an? )?(?:handful|few|couple|little|splash|drizzle|knob|squeeze|sprinkle|dollop))(?: of)? /iy;
|
|
1751
1752
|
var HYPHENATED_MIXED = /^(\d+)-(?=(\d+)[/⁄](\d+)(?![\d/⁄]))/;
|
|
1752
1753
|
var AMOUNT_CHARS = /(?:an? )?(?:[\d .,/⁄¼-¾⅐-⅞–—-]|to )*/iy;
|
|
1753
|
-
var UNIT_OF = /([a-z]+) of /iy;
|
|
1754
1754
|
var TIMES = / ?[x×] ?(?=\d)/iy;
|
|
1755
|
-
var UNREAD = / ?(?:(?:or|to|and|plus|half)\
|
|
1755
|
+
var UNREAD = / ?(?:(?:or|to|and|plus|half)(?![\p{L}\p{M}\d_])|[-–—/+&]|\d+(?:\.\d+)?(?= |$))/iuy;
|
|
1756
1756
|
var UNREAD_AFTER_UNIT = / (?:or|to|[-–—&])(?= )|[-–—+&](?=\d)/iy;
|
|
1757
1757
|
var PAREN = / ?\(([^()]*)\)/y;
|
|
1758
1758
|
var PARENS = / ?\(([^()]*)\)/g;
|
|
1759
1759
|
var MODIFIER = / ?(heaping|heaped|level|scant|rounded|generous|good|small|medium|large|big)(?= )/iy;
|
|
1760
|
-
var JOIN = /(?: (plus|and|\+)(?![
|
|
1760
|
+
var JOIN = /(?: (plus|and|\+)(?![\p{L}\p{M}]))? ?/iuy;
|
|
1761
1761
|
var SEPARATOR = / ?[;/] ?/y;
|
|
1762
1762
|
var OF = / ?of(?: (?:an?|the))?(?= |$)/iy;
|
|
1763
|
+
var NAMED = /(?!(?:for|to|an?|yields?|total)(?![\p{L}\p{M}\d_])|(?:makes|serv(?:es|ings?)(?: size)?|protein|carbs|carbohydrates?|fiber|fibre|sodium|cholesterol|saturated fat|fat) ?:)([^\d:]+):/iuy;
|
|
1763
1764
|
var PHRASE_NOTE = / (?=to taste|as needed|as required|plus more|plus extra|or more|or to taste|for (?:serving|garnish|dusting|greasing|frying|drizzling|decoration))/i;
|
|
1764
1765
|
var OPTIONAL_NOTE = /(?:^|,) ?optional(?= ?(?:,|$))/gi;
|
|
1765
1766
|
var OPTIONAL_PAREN = /^optional\b[,;:]? ?/i;
|
|
1766
1767
|
var CONTAINER = /^(?:stalk|rib|ear|sheet|loa(?:f|ves)|knob|strip|bag|box|jar|bottle|packet|envelope|tin|block|ball|handful)(?:e?s)?$/i;
|
|
1767
1768
|
var BONELESS = /(?:^|[^a-z])(?:bone|skin)less$/i;
|
|
1768
|
-
var
|
|
1769
|
-
function
|
|
1770
|
-
return
|
|
1769
|
+
var patterns;
|
|
1770
|
+
function getPatterns() {
|
|
1771
|
+
return patterns ?? (patterns = compilePatterns());
|
|
1771
1772
|
}
|
|
1772
|
-
function
|
|
1773
|
+
function compilePatterns() {
|
|
1773
1774
|
const word = String.raw`([a-z]+(?:-[a-z]+)?)\.?`;
|
|
1774
1775
|
const end = String.raw`(?=[ ,;()/]|[-–—+&]\d|$)`;
|
|
1775
1776
|
return {
|
|
@@ -1817,7 +1818,7 @@ function parseIngredientLine(text) {
|
|
|
1817
1818
|
return toResult("invalid_ingredient", ingredient, issues);
|
|
1818
1819
|
}
|
|
1819
1820
|
function readLine(line) {
|
|
1820
|
-
const p =
|
|
1821
|
+
const p = getPatterns();
|
|
1821
1822
|
const notes = [];
|
|
1822
1823
|
const equivalents = [];
|
|
1823
1824
|
let optional = false;
|
|
@@ -1877,11 +1878,22 @@ function readLine(line) {
|
|
|
1877
1878
|
}
|
|
1878
1879
|
if (found) {
|
|
1879
1880
|
[amount, i] = found;
|
|
1880
|
-
} else
|
|
1881
|
-
const
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1881
|
+
} else {
|
|
1882
|
+
const modifier = exec(MODIFIER, line, i);
|
|
1883
|
+
const unit = readUnit(line, modifier ? MODIFIER.lastIndex : i);
|
|
1884
|
+
if (unit && exec(OF, line, unit[1])) {
|
|
1885
|
+
if (modifier) {
|
|
1886
|
+
addNote(modifier[1]);
|
|
1887
|
+
}
|
|
1888
|
+
quantity = { amount: 1, unit: unit[0] };
|
|
1889
|
+
i = unit[1];
|
|
1890
|
+
} else if (match = exec(NAMED, line, i)) {
|
|
1891
|
+
const measure = readMeasure(line, NAMED.lastIndex, false);
|
|
1892
|
+
if (measure && (line[measure[1]] ?? ",") === ",") {
|
|
1893
|
+
quantity = measure[0];
|
|
1894
|
+
line = match[1] + line.slice(measure[1]);
|
|
1895
|
+
i = 0;
|
|
1896
|
+
}
|
|
1885
1897
|
}
|
|
1886
1898
|
}
|
|
1887
1899
|
}
|
|
@@ -2012,10 +2024,10 @@ function isProblem(value) {
|
|
|
2012
2024
|
return typeof value === "object" && value !== null && "code" in value;
|
|
2013
2025
|
}
|
|
2014
2026
|
function readUnit(text, start) {
|
|
2015
|
-
const { twoWordUnit, unit } =
|
|
2027
|
+
const { twoWordUnit, unit } = getPatterns();
|
|
2016
2028
|
for (const pattern of [twoWordUnit, unit]) {
|
|
2017
2029
|
const match = exec(pattern, text, start);
|
|
2018
|
-
const id = match && findUnit(match
|
|
2030
|
+
const id = match && findUnit(match.slice(1).join(" "));
|
|
2019
2031
|
if (id) {
|
|
2020
2032
|
return [id, pattern.lastIndex];
|
|
2021
2033
|
}
|
|
@@ -2113,7 +2125,13 @@ function countForms(name, many) {
|
|
|
2113
2125
|
k = CONTAINER.test(words[0]) ? 0 : words.length - 1;
|
|
2114
2126
|
}
|
|
2115
2127
|
const word = words[k];
|
|
2116
|
-
const other = many ?
|
|
2128
|
+
const other = many ? word.replace(
|
|
2129
|
+
/(?:(lea|hal|loa)ves|([rtdv])ies|(o|ch|sh|x|ss|chill?i)es|([^su])s)$/i,
|
|
2130
|
+
(_, f, y, es, s) => f ? `${f}f` : y ? `${y}y` : es ?? s
|
|
2131
|
+
) : word.replace(
|
|
2132
|
+
/(?:(lea|hal|loa)f|([^aeiou])y|(ch|sh|x|z|tomato|potato|mango|chill?i)|[a-rt-z])$/i,
|
|
2133
|
+
(match, f, y, es) => f ? `${f}ves` : y ? `${y}ies` : es ? `${es}es` : `${match}s`
|
|
2134
|
+
);
|
|
2117
2135
|
if (other === word) {
|
|
2118
2136
|
return [name, void 0];
|
|
2119
2137
|
}
|
|
@@ -2121,24 +2139,14 @@ function countForms(name, many) {
|
|
|
2121
2139
|
const swapped = words.join(" ");
|
|
2122
2140
|
return many ? [swapped, name] : [name, swapped];
|
|
2123
2141
|
}
|
|
2124
|
-
function singular(word) {
|
|
2125
|
-
return word.replace(
|
|
2126
|
-
/(?:(lea|hal|loa)ves|([rtdv])ies|(o|ch|sh|x|ss|chill?i)es|([^su])s)$/i,
|
|
2127
|
-
(_, f, y, es, s) => f ? `${f}f` : y ? `${y}y` : es ?? s
|
|
2128
|
-
);
|
|
2129
|
-
}
|
|
2130
|
-
function pluralize(word) {
|
|
2131
|
-
return word.replace(
|
|
2132
|
-
/(?:(lea|hal|loa)f|([^aeiou])y|(ch|sh|x|z|tomato|potato|mango|chill?i)|[a-rt-z])$/i,
|
|
2133
|
-
(match, f, y, es) => f ? `${f}ves` : y ? `${y}ies` : es ? `${es}es` : `${match}s`
|
|
2134
|
-
);
|
|
2135
|
-
}
|
|
2136
2142
|
|
|
2137
2143
|
// src/quantity/scale.ts
|
|
2138
2144
|
function scaleQuantity(quantity, factor) {
|
|
2139
|
-
checkQuantity("scaleQuantity", quantity);
|
|
2145
|
+
const amount = checkQuantity("scaleQuantity", quantity);
|
|
2140
2146
|
checkFactor("scaleQuantity", "factor", factor);
|
|
2141
|
-
|
|
2147
|
+
const result = multiply({ amount, unit: quantity.unit }, factor);
|
|
2148
|
+
checkResult("scaleQuantity", result.amount);
|
|
2149
|
+
return result;
|
|
2142
2150
|
}
|
|
2143
2151
|
function multiply(quantity, factor) {
|
|
2144
2152
|
const amount = mapAmount(quantity.amount, (value) => value * factor);
|
|
@@ -2235,7 +2243,7 @@ function resolveRecipe(recipe, options) {
|
|
|
2235
2243
|
} = options ?? {};
|
|
2236
2244
|
const servingsIsBase = isPositive(recipe.servings?.amount);
|
|
2237
2245
|
const base = [recipe.servings?.amount, recipe.yield?.amount].find(isPositive) ?? 1;
|
|
2238
|
-
const ratio =
|
|
2246
|
+
const ratio = isServingsFor(servings, base) ? Math.min(Math.max(servings / base, 1e-6), 1e6) : Number.NaN;
|
|
2239
2247
|
const exactServings = isScale(requestedScale) || !isScale(ratio) || isUnity(ratio) ? void 0 : servings;
|
|
2240
2248
|
const scale = isScale(requestedScale) ? requestedScale : isScale(ratio) ? ratio : 1;
|
|
2241
2249
|
const system = [requestedSystem, recipe.system].find(isSystem) ?? "metric";
|
|
@@ -2320,6 +2328,11 @@ function withEnds(quantity) {
|
|
|
2320
2328
|
const { amount } = quantity;
|
|
2321
2329
|
return typeof amount === "number" || amount.min < amount.max ? quantity : { ...quantity, amount: amount.min };
|
|
2322
2330
|
}
|
|
2331
|
+
function isServingsFor(servings, base) {
|
|
2332
|
+
const [mantissa, exponent] = base.toExponential().split("e");
|
|
2333
|
+
const shift = (places) => Number(`${mantissa}e${Number(exponent) + places}`);
|
|
2334
|
+
return isPositive(servings) && servings >= shift(-6) && servings <= shift(6);
|
|
2335
|
+
}
|
|
2323
2336
|
function isScale(value) {
|
|
2324
2337
|
return typeof value === "number" && value >= 1e-6 && value <= 1e6;
|
|
2325
2338
|
}
|
|
@@ -2439,7 +2452,7 @@ function integerText(value) {
|
|
|
2439
2452
|
function invalidLocale(fn, locale) {
|
|
2440
2453
|
return new RecipeBookError(
|
|
2441
2454
|
"invalid_argument",
|
|
2442
|
-
`${fn}: locale must be a
|
|
2455
|
+
`${fn}: locale must be a language tag that Intl accepts, such as "en-US", received ${describeValue(locale)}.`
|
|
2443
2456
|
);
|
|
2444
2457
|
}
|
|
2445
2458
|
|
|
@@ -2541,7 +2554,7 @@ function joinParts(parts) {
|
|
|
2541
2554
|
return parts.map((part) => part.value).join("");
|
|
2542
2555
|
}
|
|
2543
2556
|
function showAmount(value, settings, steps = QUARTERS) {
|
|
2544
|
-
const { rounding, fractions } = settings;
|
|
2557
|
+
const { rounding, fractions: fractions2 } = settings;
|
|
2545
2558
|
if (rounding === "none") {
|
|
2546
2559
|
return decimal(value, 3, settings);
|
|
2547
2560
|
}
|
|
@@ -2553,7 +2566,7 @@ function showAmount(value, settings, steps = QUARTERS) {
|
|
|
2553
2566
|
return decimal(value, 1, settings);
|
|
2554
2567
|
}
|
|
2555
2568
|
const [whole, part, glyph] = snap(value, steps);
|
|
2556
|
-
if (!
|
|
2569
|
+
if (!fractions2) {
|
|
2557
2570
|
return decimal(whole + part, 2, settings);
|
|
2558
2571
|
}
|
|
2559
2572
|
const wholeText = settings.number(whole, 0);
|
|
@@ -2698,6 +2711,7 @@ exports.RecipeBookError = RecipeBookError;
|
|
|
2698
2711
|
exports.RecipeValidationError = RecipeValidationError;
|
|
2699
2712
|
exports.asRecord = asRecord;
|
|
2700
2713
|
exports.checkQuantity = checkQuantity;
|
|
2714
|
+
exports.checkResult = checkResult;
|
|
2701
2715
|
exports.checkSystem = checkSystem;
|
|
2702
2716
|
exports.describeValue = describeValue;
|
|
2703
2717
|
exports.disallowedScheme = disallowedScheme;
|
|
@@ -2723,6 +2737,7 @@ exports.parseIngredientLine = parseIngredientLine;
|
|
|
2723
2737
|
exports.parseIsoDuration = parseIsoDuration;
|
|
2724
2738
|
exports.parseQuantity = parseQuantity;
|
|
2725
2739
|
exports.parseRecipe = parseRecipe;
|
|
2740
|
+
exports.parseRecipeWith = parseRecipeWith;
|
|
2726
2741
|
exports.pluralForm = pluralForm;
|
|
2727
2742
|
exports.quote = quote;
|
|
2728
2743
|
exports.readLineOption = readLineOption;
|
|
@@ -2735,5 +2750,5 @@ exports.toResult = toResult;
|
|
|
2735
2750
|
exports.units = units;
|
|
2736
2751
|
exports.unknownUnitError = unknownUnitError;
|
|
2737
2752
|
exports.warning = warning;
|
|
2738
|
-
//# sourceMappingURL=chunk-
|
|
2739
|
-
//# sourceMappingURL=chunk-
|
|
2753
|
+
//# sourceMappingURL=chunk-D5CV3UF7.cjs.map
|
|
2754
|
+
//# sourceMappingURL=chunk-D5CV3UF7.cjs.map
|