@gram-lang/analyzer
Enrichit un CompilationResult (issu de @gram-lang/kitchen) avec des propriétés physiques — masse standardisée, rendement d'achat, estimations nutritionnelles, et pourcentages boulanger — en croisant les données avec une base de données d'ingrédients. C'est la seule étape qui nécessite une base de données ; le parsing et la compilation fonctionnent sur n'importe quelle recette sans données externes.
analyze
function analyze(
result: CompilationResult,
database: Record<string, IngredientData>,
options?: AnalyzerOptions,
): AnalysisResult
interface AnalysisResult {
result: AnalyzedCompilationResult;
missingIngredients: string[]; // ids présents dans la recette mais absents de `database`
}import { compile } from '@gram-lang/kitchen';
import { analyze } from '@gram-lang/analyzer';
const compiled = compile(ast);
const { result: analyzed, missingIngredients } = analyze(compiled, myIngredientDatabase);La base de données d'ingrédients est le deuxième argument positionnel, pas un champ de
options—analyze(compiled, database, options?).
analyze() est pure et ne mute jamais result. AnalyzedCompilationResult étend CompilationResult — même forme, plus des champs normalizedMass/conversionMethod/isEstimate/purchasingMass/bakersPercentage par usage et un bloc metrics.nutrition. Voir Formats de Données pour un exemple entièrement annoté.
AnalyzerOptions
Tous les indicateurs sont activés par défaut (vérifications internes !== false) — passez false pour désactiver une passe d'enrichissement donnée.
| Option | Type | Description |
|---|---|---|
enableMassStandardization | boolean | Convertit les quantités d'ingrédients en grammes standardisés. |
enableYieldCalculation | boolean | Applique le physical.yield (facteur de perte) de la base de données lors de la standardisation de la masse. |
enableNutritionalEstimation | boolean | Calcule metrics.nutrition (calories, macros, éventuellement par portion). |
enableBakersMath | boolean | Calcule bakersPercentage par rapport à l'ingrédient marqué * (ou bakersReference). |
bakersReference | string | Id d'ingrédient explicite à utiliser comme base 100% du pourcentage boulanger, au lieu du modificateur *. |
portions | number | Nombre de portions utilisé pour calculer metrics.nutrition.perPortion. |
lang | string | Code de langue optionnel (ex : 'en', 'fr') pour la normalisation d'unités et le tri des catégories par langue. |
validateIngredientDatabase
function validateIngredientDatabase(rawDb: unknown): {
data: Record<string, IngredientData>;
rejected: { key: string; message: string }[];
}Valide entrée par entrée plutôt que tout-ou-rien : un ingrédient malformé n'empêche pas le chargement de tous les autres. Utilisez data pour compiler/analyser ; affichez rejected à l'utilisateur (ex : gram db validate). Voir Formats de Données pour le schéma YAML IngredientData.
Utilitaires de masse
function standardizeMass(
amount: number,
unit: string,
database: Record<string, IngredientData>,
ingredientName?: string,
overrides?: Record<string, number>, // slug -> densité (g/mL) ou poids unitaire (g), issu du frontmatter `densities:`
lang?: string,
): { mass: number; method: "physical" | "density" | "unit_weight" | "default" | "explicit"; isEstimate: boolean } | null
function convertUnit(value: number, fromUnit: string, toUnit: string, density?: number, lang?: string): number | null
function applyYield(
mass: number,
method: "physical" | "density" | "unit_weight" | "default" | "explicit",
yieldFactor?: number,
): { normalizedMass: number; purchasingMass?: number }standardizeMass résout une quantité en grammes via, dans l'ordre : (1) une unité de masse directe (g, kg, oz...), (2) une unité de volume convertie via la densité résolue de l'ingrédient, (3) une unité non reconnue (gousse, tranche...) traitée comme un décompte via le unit_weight de l'ingrédient. Retourne null — sans jamais deviner — quand une unité de volume n'a aucune densité résolvable.
convertUnit convertit entre deux chaînes d'unité arbitraires, faisant le pont masse↔volume via density (g/mL) si fournie. Retourne null pour une conversion inter-famille non résolvable sans densité.
Table de conversion masse & volume
Les facteurs de conversion de base utilisés par standardizeMass/convertUnit pour les conversions intra-famille (masse↔masse, volume↔volume), avant toute densité :
| Famille | Unité | Facteur (par rapport à la base) |
|---|---|---|
mass (base: g) | mg | 0.001 |
mass (base: g) | g | 1 |
mass (base: g) | kg | 1000 |
mass (base: g) | oz | 28.3495 |
mass (base: g) | lb | 453.592 |
mass (base: g) | livre | 500 |
volume (base: ml) | ml | 1 |
volume (base: ml) | cl | 10 |
volume (base: ml) | dl | 100 |
volume (base: ml) | l | 1000 |
volume (base: ml) | drop | 0.078 |
volume (base: ml) | smidgen | 0.156 |
volume (base: ml) | pinch | 0.3125 |
volume (base: ml) | dash | 0.625 |
volume (base: ml) | tad | 1.25 |
volume (base: ml) | tsp | 4.9289 |
volume (base: ml) | tbsp | 14.7868 |
volume (base: ml) | cup | 236.588 |
volume (base: ml) | tasse | 250 |
volume (base: ml) | pt | 473.176 |
volume (base: ml) | qt | 946.353 |
volume (base: ml) | gal | 3785.41 |
volume (base: ml) | fl oz | 29.5735 |
calculateNutrition
function calculateNutrition(
ingredients: AnalyzedUsage[],
database: Record<string, IngredientData>,
portions?: number,
): NutritionMetricsAplatit les composites et les alternatives (en prenant la première option), ignore les ingrédients marqués optional et ceux à quantité nulle, et somme les champs nutrition (déclarés pour 100g dans la base de données — voir Formats de Données) mis à l'échelle par la masse standardisée de chaque ingrédient. Retourne isEstimate: true si l'une des masses contributrices était elle-même une estimation, et coverage (0–1 : fraction des ingrédients avec des données nutritionnelles connues).
diffRecipes
function diffRecipes(a: CompilationResult, b: CompilationResult): DiffResultDiff structurel entre deux recettes compilées (ex : deux versions d'un même fichier, ou un résultat mis à l'échelle vs. non mis à l'échelle) — changements de quantité/unité d'ingrédients (avec percentChange quand comparable), changements de préparation, écarts de minutage, sections ajoutées/supprimées/modifiées, changements de température et de minuteur, et changements de frontmatter (meta). hasChanges vaut true si une catégorie au moins est non vide.