@jclind/ingredient-parser 1.2.14 → 1.3.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/README.md
CHANGED
|
@@ -1,80 +1,136 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @jclind/ingredient-parser
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@jclind/ingredient-parser)
|
|
4
|
+
[](https://github.com/jclind/ingredient-parser/blob/main/LICENSE)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
This package is built with [recipe-ingredient-parser-v3](https://www.npmjs.com/package/recipe-ingredient-parser-v3) and also returns ingredient data along with parsing the ingredient. If you don't need the ingredient data, just the parsed ingredient, I recommend using [recipe-ingredient-parser-v3](https://www.npmjs.com/package/recipe-ingredient-parser-v3).
|
|
6
|
+
A TypeScript package for parsing ingredient strings and retrieving structured ingredient data from the Spoonacular API.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
Built on top of [recipe-ingredient-parser-v3](https://www.npmjs.com/package/recipe-ingredient-parser-v3). If you only need ingredient parsing without ingredient metadata, nutrition, or API lookups, you may prefer using that package directly.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install @jclind/ingredient-parser
|
|
12
14
|
```
|
|
13
|
-
import { parseIngredient } from '@jclind/ingredient-parser';
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
parseIngredient
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { parseIngredient } from '@jclind/ingredient-parser'
|
|
20
|
+
|
|
21
|
+
const ingredientString = '1 cup rice, washed'
|
|
22
|
+
const apiKey = 'YOUR_API_KEY'
|
|
23
|
+
|
|
24
|
+
const result = await parseIngredient(ingredientString, apiKey, {
|
|
25
|
+
returnNutritionData: true,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
console.log(result)
|
|
19
29
|
```
|
|
20
|
-
Returns an object `{id: (randomly generated id unique to every request), parsedIngredient, ingredientData}` with the following properties/values.
|
|
21
30
|
|
|
22
|
-
|
|
31
|
+
## API
|
|
32
|
+
|
|
33
|
+
### `parseIngredient(ingredientString, SPOONACULAR_API_KEY, options?)`
|
|
34
|
+
|
|
35
|
+
Parses an ingredient string and returns both the parsed ingredient data and ingredient metadata retrieved from Spoonacular.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { parseIngredient } from '@jclind/ingredient-parser'
|
|
39
|
+
|
|
40
|
+
parseIngredient(
|
|
41
|
+
ingredientString: string,
|
|
42
|
+
SPOONACULAR_API_KEY: string,
|
|
43
|
+
options?: {
|
|
44
|
+
returnNutritionData?: boolean
|
|
45
|
+
}
|
|
46
|
+
): Promise<IngredientResponseType>
|
|
23
47
|
```
|
|
48
|
+
|
|
49
|
+
| Parameter | Type | Required | Description |
|
|
50
|
+
| --------------------- | -------- | -------- | ------------------------------------------------------- |
|
|
51
|
+
| `ingredientString` | `string` | Yes | Ingredient string formatted like `2 cups onions, diced` |
|
|
52
|
+
| `SPOONACULAR_API_KEY` | `string` | Yes | Your Spoonacular API key |
|
|
53
|
+
| `options` | `object` | No | Additional parsing options |
|
|
54
|
+
|
|
55
|
+
### Options
|
|
56
|
+
|
|
57
|
+
| Option | Type | Default | Description |
|
|
58
|
+
| --------------------- | --------- | ------- | ------------------------------------------------------- |
|
|
59
|
+
| `returnNutritionData` | `boolean` | `false` | Includes Spoonacular nutrition data in `ingredientData` |
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Response Structure
|
|
64
|
+
|
|
65
|
+
Returns an object with the following shape:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
24
68
|
{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
minQty: 1,
|
|
32
|
-
maxQty: 1,
|
|
33
|
-
comment: 'washed'
|
|
69
|
+
id: string
|
|
70
|
+
parsedIngredient: ParsedIngredientType
|
|
71
|
+
ingredientData: IngredientDataType | null
|
|
72
|
+
error?: {
|
|
73
|
+
message: string
|
|
74
|
+
}
|
|
34
75
|
}
|
|
35
76
|
```
|
|
36
77
|
|
|
37
|
-
|
|
38
|
-
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### `parsedIngredient`
|
|
81
|
+
|
|
82
|
+
```ts
|
|
39
83
|
{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
image: 'uncooked-white-rice.png',
|
|
50
|
-
imagePath: 'https://spoonacular.com/cdn/ingredients_100x100/uncooked-white-rice.png',
|
|
51
|
-
nutrition?: {
|
|
52
|
-
nutrients: [Array],
|
|
53
|
-
properties: [Array],
|
|
54
|
-
flavonoids: [Array],
|
|
55
|
-
caloricBreakdown: [Object],
|
|
56
|
-
weightPerServing: [Object]
|
|
57
|
-
},
|
|
58
|
-
names: [ 'rice' ],
|
|
59
|
-
dateAdded: 1674242751000,
|
|
60
|
-
totalPriceUSACents: 75.71
|
|
84
|
+
quantity: 1,
|
|
85
|
+
unit: 'cup',
|
|
86
|
+
unitPlural: 'cups',
|
|
87
|
+
symbol: 'c',
|
|
88
|
+
ingredient: 'rice',
|
|
89
|
+
originalIngredientString: '1 cup rice, washed',
|
|
90
|
+
minQty: 1,
|
|
91
|
+
maxQty: 1,
|
|
92
|
+
comment: 'washed'
|
|
61
93
|
}
|
|
62
94
|
```
|
|
63
95
|
|
|
64
|
-
|
|
65
|
-
`parseIngredient(ingredientString: string, SPOONACULAR_API_KEY: string) => { parsedIngredient, IngredientData }` Takes an ingredient string and a spoonacular API key and returns the parsed ingredient and the ingredient data.
|
|
96
|
+
---
|
|
66
97
|
|
|
67
|
-
|
|
68
|
-
- `SPOONACULAR_API_KEY` (string) [required] : your unique [spoonacular](https://spoonacular.com/food-api) API key.
|
|
69
|
-
- `options` (object) [optional] : an object containing additional options. Currently, the only supported option is `returnNutritionData`, which, if set to `true`, will include nutrition data in the ingredientData object.
|
|
98
|
+
### `ingredientData`
|
|
70
99
|
|
|
71
|
-
|
|
100
|
+
```ts
|
|
101
|
+
{
|
|
102
|
+
_id: '63caeabf4762c87be39c3795',
|
|
103
|
+
ingredientId: 20444,
|
|
104
|
+
originalName: 'rice',
|
|
105
|
+
name: 'rice',
|
|
106
|
+
amount: 1,
|
|
107
|
+
possibleUnits: ['g', 'oz', 'cup'],
|
|
108
|
+
consistency: 'solid',
|
|
109
|
+
shoppingListUnits: ['ounces', 'pounds'],
|
|
110
|
+
aisle: 'Pasta and Rice',
|
|
111
|
+
image: 'uncooked-white-rice.png',
|
|
112
|
+
imagePath: 'https://spoonacular.com/cdn/ingredients_100x100/uncooked-white-rice.png',
|
|
113
|
+
nutrition?: {
|
|
114
|
+
nutrients: [Array],
|
|
115
|
+
properties: [Array],
|
|
116
|
+
flavonoids: [Array],
|
|
117
|
+
caloricBreakdown: [Object],
|
|
118
|
+
weightPerServing: [Object]
|
|
119
|
+
},
|
|
120
|
+
dateAdded: 1674242751000,
|
|
121
|
+
totalPriceUSACents: 75.71
|
|
122
|
+
}
|
|
123
|
+
```
|
|
72
124
|
|
|
73
125
|
## Error Handling
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
126
|
+
|
|
127
|
+
If the ingredient cannot be identified or the API key is invalid, the function returns an error object while still including the parsed ingredient structure.
|
|
128
|
+
|
|
129
|
+
### Unknown Ingredient
|
|
130
|
+
|
|
131
|
+
```ts
|
|
77
132
|
parseIngredient('Invalid Text', YOUR_API_KEY)
|
|
133
|
+
|
|
78
134
|
/*
|
|
79
135
|
{
|
|
80
136
|
error: { message: 'No Data Found, unknown ingredient: invalid text' },
|
|
@@ -93,9 +149,12 @@ parseIngredient('Invalid Text', YOUR_API_KEY)
|
|
|
93
149
|
}
|
|
94
150
|
*/
|
|
95
151
|
```
|
|
96
|
-
|
|
97
|
-
|
|
152
|
+
|
|
153
|
+
### Invalid API Key
|
|
154
|
+
|
|
155
|
+
```ts
|
|
98
156
|
parseIngredient('1 cup rice', INVALID_API_KEY)
|
|
157
|
+
|
|
99
158
|
/*
|
|
100
159
|
{
|
|
101
160
|
error: { message: 'API Key Not Valid' },
|
|
@@ -106,7 +165,7 @@ parseIngredient('1 cup rice', INVALID_API_KEY)
|
|
|
106
165
|
unitPlural: 'cups',
|
|
107
166
|
symbol: 'c',
|
|
108
167
|
ingredient: 'rice',
|
|
109
|
-
originalIngredientString: '1 cup rice'
|
|
168
|
+
originalIngredientString: '1 cup rice',
|
|
110
169
|
minQty: 1,
|
|
111
170
|
maxQty: 1,
|
|
112
171
|
comment: null
|
|
@@ -115,16 +174,38 @@ parseIngredient('1 cup rice', INVALID_API_KEY)
|
|
|
115
174
|
*/
|
|
116
175
|
```
|
|
117
176
|
|
|
118
|
-
##
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
import { parseIngredient, ParsedIngredientType, IngredientDataType, IngredientResponseType, } from '@jclind/ingredient-parser';
|
|
177
|
+
## TypeScript
|
|
178
|
+
|
|
179
|
+
This package ships with full TypeScript definitions. No additional `@types` package required.
|
|
122
180
|
|
|
123
|
-
|
|
124
|
-
|
|
181
|
+
```ts
|
|
182
|
+
import {
|
|
183
|
+
parseIngredient,
|
|
184
|
+
ParsedIngredientType,
|
|
185
|
+
IngredientDataType,
|
|
186
|
+
IngredientResponseType,
|
|
187
|
+
} from '@jclind/ingredient-parser'
|
|
125
188
|
|
|
126
|
-
const
|
|
189
|
+
const ingredientString: string = '1 cup rice, washed'
|
|
190
|
+
const apiKey: string = 'YOUR_API_KEY'
|
|
191
|
+
|
|
192
|
+
const parsed: IngredientResponseType = await parseIngredient(
|
|
193
|
+
ingredientString,
|
|
194
|
+
apiKey,
|
|
195
|
+
)
|
|
127
196
|
|
|
128
197
|
const parsedIngredient: ParsedIngredientType = parsed.parsedIngredient
|
|
129
|
-
|
|
198
|
+
|
|
199
|
+
const ingredientData: IngredientDataType | null = parsed.ingredientData
|
|
130
200
|
```
|
|
201
|
+
|
|
202
|
+
## Notes
|
|
203
|
+
|
|
204
|
+
- A valid Spoonacular API key is required for ingredient lookups.
|
|
205
|
+
- Ingredient parsing is powered by `recipe-ingredient-parser-v3`.
|
|
206
|
+
- Nutrition data is optional and disabled by default to reduce API usage.
|
|
207
|
+
- Parsed ingredient results are returned even when ingredient lookup fails.
|
|
208
|
+
|
|
209
|
+
## Issues & Contributing
|
|
210
|
+
|
|
211
|
+
Found a bug or have a feature request? [Open an issue](https://github.com/jclind/ingredient-parser/issues) on GitHub. PRs are welcome.
|
package/dist/src/api/http.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
1
|
+
import _axios from 'axios';
|
|
2
|
+
export declare const spoonacularHttp: _axios.AxiosInstance;
|
|
3
|
+
export declare const createIngredientServerHttp: (serverUrl?: string) => _axios.AxiosInstance;
|
|
@@ -4,7 +4,85 @@ exports.parseIngredientString = void 0;
|
|
|
4
4
|
const convertFractions_js_1 = require("./convertFractions.js");
|
|
5
5
|
const parseStringConsecutiveTs_js_1 = require("./parseStringConsecutiveTs.js");
|
|
6
6
|
const parseIngredientString = (ingrStr) => {
|
|
7
|
-
var _a, _b;
|
|
7
|
+
var _a, _b, _c;
|
|
8
|
+
// Input validation
|
|
9
|
+
if (typeof ingrStr !== 'string' || ingrStr === null || ingrStr === undefined) {
|
|
10
|
+
throw new TypeError('parseIngredientString expects a string input');
|
|
11
|
+
}
|
|
12
|
+
// Store original input before any modifications
|
|
13
|
+
const originalInput = ingrStr;
|
|
14
|
+
// Extract range quantities (e.g., "1-2 cups", "2 to 3 cups")
|
|
15
|
+
let minQty = null;
|
|
16
|
+
let maxQty = null;
|
|
17
|
+
const hyphenRangeMatch = ingrStr.match(/(\d+(?:\.\d+)?)\s*[-–]\s*(\d+(?:\.\d+)?)/);
|
|
18
|
+
const toRangeMatch = ingrStr.match(/(\d+(?:\.\d+)?)\s+to\s+(\d+(?:\.\d+)?)/i);
|
|
19
|
+
if (hyphenRangeMatch) {
|
|
20
|
+
minQty = parseFloat(hyphenRangeMatch[1]);
|
|
21
|
+
maxQty = parseFloat(hyphenRangeMatch[2]);
|
|
22
|
+
// Replace the range with minQty for parsing
|
|
23
|
+
ingrStr = ingrStr.replace(hyphenRangeMatch[0], String(minQty));
|
|
24
|
+
}
|
|
25
|
+
else if (toRangeMatch) {
|
|
26
|
+
minQty = parseFloat(toRangeMatch[1]);
|
|
27
|
+
maxQty = parseFloat(toRangeMatch[2]);
|
|
28
|
+
// Replace the range with minQty for parsing
|
|
29
|
+
ingrStr = ingrStr.replace(toRangeMatch[0], String(minQty));
|
|
30
|
+
}
|
|
31
|
+
// Store extracted ranges for later use
|
|
32
|
+
const extractedMinQty = minQty;
|
|
33
|
+
const extractedMaxQty = maxQty;
|
|
34
|
+
// Pre-processing: handle informal quantity patterns ("a pinch of", "handful", "dash")
|
|
35
|
+
// These patterns aren't well-handled by the upstream parser
|
|
36
|
+
const informalQtyPatterns = [
|
|
37
|
+
{
|
|
38
|
+
// "a pinch of salt"
|
|
39
|
+
regex: /^a\s+(pinch|dash|handful)\s+of\s+(.+)$/i,
|
|
40
|
+
extract: (match) => ({
|
|
41
|
+
quantity: 1,
|
|
42
|
+
unit: match[1].toLowerCase(),
|
|
43
|
+
ingredient: match[2].trim(),
|
|
44
|
+
})
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
// "pinch of salt", "dash of hot sauce"
|
|
48
|
+
regex: /^(pinch|dash|handful)\s+of\s+(.+)$/i,
|
|
49
|
+
extract: (match) => ({
|
|
50
|
+
quantity: 1,
|
|
51
|
+
unit: match[1].toLowerCase(),
|
|
52
|
+
ingredient: match[2].trim(),
|
|
53
|
+
})
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
// "handful spinach" (no "of")
|
|
57
|
+
regex: /^(pinch|dash|handful)\s+(.+)$/i,
|
|
58
|
+
extract: (match) => ({
|
|
59
|
+
quantity: 1,
|
|
60
|
+
unit: match[1].toLowerCase(),
|
|
61
|
+
ingredient: match[2].trim(),
|
|
62
|
+
})
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
for (const pattern of informalQtyPatterns) {
|
|
66
|
+
const match = ingrStr.match(pattern.regex);
|
|
67
|
+
if (match) {
|
|
68
|
+
const { quantity, unit, ingredient } = pattern.extract(match);
|
|
69
|
+
// Apply descriptor stripping to ingredient
|
|
70
|
+
const wordsToRemove = ['small', 'medium', 'large', 'fresh', 'canned', 'freshly', 'finely', 'roughly', 'coarsely', 'grated', 'chopped'];
|
|
71
|
+
const regex = new RegExp('\\b(' + wordsToRemove.join('|') + ')\\b', 'gi');
|
|
72
|
+
const cleanedIngredient = ingredient.replace(regex, '').trim();
|
|
73
|
+
return {
|
|
74
|
+
quantity,
|
|
75
|
+
unit,
|
|
76
|
+
unitPlural: unit + 's',
|
|
77
|
+
symbol: null,
|
|
78
|
+
ingredient: cleanedIngredient,
|
|
79
|
+
originalIngredientString: originalInput,
|
|
80
|
+
minQty: quantity,
|
|
81
|
+
maxQty: quantity,
|
|
82
|
+
comment: '',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
8
86
|
// Define regular expressions for text inside parentheses and text before the first comma
|
|
9
87
|
const parenRegex = /(\(.*?\))/;
|
|
10
88
|
const commaRegex = /^(.*?)(?=,)/;
|
|
@@ -49,14 +127,83 @@ const parseIngredientString = (ingrStr) => {
|
|
|
49
127
|
ml: 'milliliter',
|
|
50
128
|
lb: 'pound',
|
|
51
129
|
g: 'gram',
|
|
130
|
+
// Additional unit recognitions
|
|
131
|
+
sprigs: 'sprig',
|
|
132
|
+
sprig: 'sprig',
|
|
133
|
+
'bay leaves': 'bay leaf',
|
|
134
|
+
'bay leaf': 'bay leaf',
|
|
135
|
+
sheets: 'sheet',
|
|
136
|
+
sheet: 'sheet',
|
|
137
|
+
tblsp: 'tablespoon',
|
|
138
|
+
dessertspoon: 'dessertspoon',
|
|
139
|
+
dessertspoons: 'dessertspoon',
|
|
140
|
+
'fl oz': 'fluid ounce',
|
|
141
|
+
'fluid oz': 'fluid ounce',
|
|
52
142
|
};
|
|
53
143
|
const unitPattern = new RegExp('\\b(' + Object.keys(unitNormalizations).join('|') + ')\\b', 'gi');
|
|
54
144
|
const prepIngrText = ingrText.replace(unitPattern, match => { var _a; return (_a = unitNormalizations[match.toLowerCase()]) !== null && _a !== void 0 ? _a : match; });
|
|
55
|
-
|
|
145
|
+
let parsedIngrRes;
|
|
146
|
+
try {
|
|
147
|
+
parsedIngrRes = (0, parseStringConsecutiveTs_js_1.parseStringConsecutiveTs)(prepIngrText);
|
|
148
|
+
}
|
|
149
|
+
catch (_d) {
|
|
150
|
+
// Return degraded result for malformed input (e.g., division by zero)
|
|
151
|
+
return {
|
|
152
|
+
quantity: 0,
|
|
153
|
+
unit: null,
|
|
154
|
+
unitPlural: null,
|
|
155
|
+
symbol: null,
|
|
156
|
+
ingredient: ingrStr.replace(/[^a-zA-Z\s]/g, '').trim(),
|
|
157
|
+
minQty: null,
|
|
158
|
+
maxQty: null,
|
|
159
|
+
originalIngredientString: originalInput,
|
|
160
|
+
comment,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
// Post-processing: check if ingredient name starts with an unrecognized unit
|
|
164
|
+
// The upstream parser doesn't recognize some units, so we extract them manually
|
|
165
|
+
const unrecognizedUnits = [
|
|
166
|
+
'sprig', 'sprigs',
|
|
167
|
+
'strip', 'strips',
|
|
168
|
+
'sheet', 'sheets',
|
|
169
|
+
'dessertspoon', 'dessertspoons',
|
|
170
|
+
'handful', 'handfuls',
|
|
171
|
+
'dash', 'dashes',
|
|
172
|
+
'bay leaf', 'bay leaves',
|
|
173
|
+
];
|
|
174
|
+
const unrecognizedUnitPattern = new RegExp('^(' + unrecognizedUnits.join('|') + ')\\b', 'i');
|
|
175
|
+
if (parsedIngrRes.ingredient && parsedIngrRes.unit === null) {
|
|
176
|
+
const unitMatch = parsedIngrRes.ingredient.match(unrecognizedUnitPattern);
|
|
177
|
+
if (unitMatch) {
|
|
178
|
+
let unit = unitMatch[1].toLowerCase();
|
|
179
|
+
// Normalize plural forms to singular
|
|
180
|
+
const unitSingular = {
|
|
181
|
+
sprigs: 'sprig',
|
|
182
|
+
strips: 'strip',
|
|
183
|
+
sheets: 'sheet',
|
|
184
|
+
dessertspoons: 'dessertspoon',
|
|
185
|
+
handfuls: 'handful',
|
|
186
|
+
dashes: 'dash',
|
|
187
|
+
'bay leaves': 'bay leaf',
|
|
188
|
+
};
|
|
189
|
+
parsedIngrRes.unit = (_c = unitSingular[unit]) !== null && _c !== void 0 ? _c : unit;
|
|
190
|
+
parsedIngrRes.ingredient = parsedIngrRes.ingredient
|
|
191
|
+
.replace(unrecognizedUnitPattern, '')
|
|
192
|
+
.trim();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Post-processing: handle "fl oz" / "fluid ounce" which upstream parser normalizes to "ounce"
|
|
196
|
+
if (parsedIngrRes.unit === 'ounce' &&
|
|
197
|
+
(originalInput.toLowerCase().includes('fl oz') ||
|
|
198
|
+
originalInput.toLowerCase().includes('fluid ounce'))) {
|
|
199
|
+
parsedIngrRes.unit = 'fluid ounce';
|
|
200
|
+
// Also fix the symbol
|
|
201
|
+
parsedIngrRes.symbol = 'fl oz';
|
|
202
|
+
}
|
|
56
203
|
if (!parsedIngrRes.ingredient) {
|
|
57
|
-
return Object.assign(Object.assign({}, parsedIngrRes), { originalIngredientString:
|
|
204
|
+
return Object.assign(Object.assign({}, parsedIngrRes), { originalIngredientString: originalInput, comment });
|
|
58
205
|
}
|
|
59
|
-
const wordsToRemove = ['small', 'medium', 'large', 'fresh', 'canned'];
|
|
206
|
+
const wordsToRemove = ['small', 'medium', 'large', 'fresh', 'canned', 'freshly', 'finely', 'roughly', 'coarsely', 'grated', 'chopped'];
|
|
60
207
|
const regex = new RegExp('\\b(' + wordsToRemove.join('|') + ')\\b', 'gi');
|
|
61
208
|
const descriptorSet = new Set(wordsToRemove.map(w => w.toLowerCase()));
|
|
62
209
|
const unit = parsedIngrRes.unit && descriptorSet.has(parsedIngrRes.unit.toLowerCase())
|
|
@@ -69,6 +216,6 @@ const parseIngredientString = (ingrStr) => {
|
|
|
69
216
|
.replace(/^(fluid|fl|oz) /, '') // Remove "fluid ", "fl ", or "oz " at the beginning of the string
|
|
70
217
|
.replace(regex, '')
|
|
71
218
|
.trim();
|
|
72
|
-
return Object.assign(Object.assign({}, parsedIngrRes), { unit, ingredient: formattedIngrName, originalIngredientString:
|
|
219
|
+
return Object.assign(Object.assign({}, parsedIngrRes), { unit, ingredient: formattedIngrName, originalIngredientString: originalInput, comment, minQty: extractedMinQty !== null && extractedMinQty !== void 0 ? extractedMinQty : parsedIngrRes.minQty, maxQty: extractedMaxQty !== null && extractedMaxQty !== void 0 ? extractedMaxQty : parsedIngrRes.maxQty });
|
|
73
220
|
};
|
|
74
221
|
exports.parseIngredientString = parseIngredientString;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import { ParsedIngredient } from '../../types.js';
|
|
2
|
-
type ParsedIngredientOmitType = Omit<ParsedIngredient, 'originalIngredientString' | 'comment'>;
|
|
2
|
+
export type ParsedIngredientOmitType = Omit<ParsedIngredient, 'originalIngredientString' | 'comment'>;
|
|
3
3
|
export declare const parseStringConsecutiveTs: (ingrStr: string) => ParsedIngredientOmitType;
|
|
4
|
-
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jclind/ingredient-parser",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Parses given sentence including ingredient information and attempts to return quantity, measurement and ingredient data",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"recipe",
|
|
@@ -37,15 +37,15 @@
|
|
|
37
37
|
"homepage": "https://github.com/jclind/ingredient-parser#readme",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@jclind/ingredient-unit-converter": "^1.1.0",
|
|
40
|
-
"axios": "^1.
|
|
40
|
+
"axios": "^1.16.0",
|
|
41
41
|
"recipe-ingredient-parser-v3": "^1.5.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/jest": "^30.0.0",
|
|
45
|
-
"@types/node": "^
|
|
45
|
+
"@types/node": "^25.6.0",
|
|
46
46
|
"jest": "^30.3.0",
|
|
47
|
-
"rimraf": "^
|
|
47
|
+
"rimraf": "^6.1.3",
|
|
48
48
|
"ts-jest": "^29.4.9",
|
|
49
|
-
"typescript": "^
|
|
49
|
+
"typescript": "^6.0.3"
|
|
50
50
|
}
|
|
51
51
|
}
|