/** * Ingredient density table for automatic weight/volume conversion. * * Each entry has keywords (ingredient names) and a density in g/ml. * Conversion: * Weight -> Volume: ml = grams / density * Volume -> Weight: grams = ml * density * * Entries are sorted longest-keyword-first so that "brown sugar" matches * before "sugar", "olive oil" before "oil", etc. */ const DENSITIES = [ { keywords: ['all-purpose flour', 'all purpose flour', 'ap flour'], density: 0.53 }, { keywords: ['powdered sugar', 'confectioners sugar', 'icing sugar'], density: 0.50 }, { keywords: ['vanilla extract'], density: 1.03 }, { keywords: ['baking powder'], density: 0.90 }, { keywords: ['cocoa powder'], density: 0.43 }, { keywords: ['brown sugar'], density: 0.93 }, { keywords: ['maple syrup'], density: 1.32 }, { keywords: ['baking soda', 'bicarbonate'], density: 1.10 }, { keywords: ['bread flour'], density: 0.55 }, { keywords: ['sour cream'], density: 1.05 }, { keywords: ['olive oil'], density: 0.92 }, { keywords: ['cornstarch', 'corn starch'], density: 0.54 }, { keywords: ['butter'], density: 0.96 }, { keywords: ['cream'], density: 1.01 }, { keywords: ['flour'], density: 0.53 }, { keywords: ['sugar'], density: 0.85 }, { keywords: ['honey'], density: 1.42 }, { keywords: ['milk'], density: 1.03 }, { keywords: ['water'], density: 1.0 }, { keywords: ['salt'], density: 1.22 }, { keywords: ['rice'], density: 0.80 }, { keywords: ['oil'], density: 0.92 }, ]; /** * Check if text contains a known ingredient and return its density. * Lowercases the text and checks keywords longest-first. * * @param {string} text - text preceding the measurement (ingredient context) * @returns {number|null} density in g/ml, or null if no match */ function matchDensity(text) { if (!text) return null; const lower = text.toLowerCase().trim(); for (const entry of DENSITIES) { for (const keyword of entry.keywords) { if (lower.includes(keyword)) { return entry.density; } } } return null; } module.exports = { DENSITIES, matchDensity };