Aller au contenu

Avertissements

Une recette malformée ou incomplète finit quand même de compiler : le compilateur et l’Analyseur collectent des objets Warning structurés au lieu de lever des exceptions. Cela permet d’afficher la recette tout en signalant visuellement les avertissements. L’énumération WarningCode et ses utilitaires sont exportés par @gram-lang/kitchen.

interface Warning {
  code: WarningCode;
  message: string;       // lisible par un humain, prêt à être affiché tel quel
  item?: string;
  loc?: { start: number; end: number }; // décalages en caractères dans la source, quand disponible
  section?: string | null;
}

compile() les retourne dans le tableau CompilationResult.warnings. De son côté, analyze() les propage (et en ajoute potentiellement de nouveaux) dans AnalyzedCompilationResult.warnings. Ce ne sont jamais de simples chaînes de caractères : le champ .message est toujours garanti.

type WarningSeverity = "error" | "warning" | "info";
const warningSeverity: Record<WarningCode, WarningSeverity>;

Les problèmes d’intégrité structurelle (une référence vers un élément fantôme, une collision de noms, un module introuvable, etc.) sont de sévérité error. Les lacunes récupérables (estimations, annotations incomplètes) sont catégorisées en warning, et les simples notifications contextuelles (surplus, calcul par fournées, contention de ressources) sont en info. Ainsi, une unité de minuteur manquante ne fera pas crasher un build au même titre qu’une référence indéfinie. C’est exactement sur cette distinction que repose l’option --strict de la commande CLI gram check : sans --strict, seuls les codes de sévérité error font échouer la commande ; avec, chaque warning et info est promu en error. Vous pouvez facilement coder votre propre logique de mode strict en vous appuyant sur le dictionnaire warningSeverity[code].

CodeSévéritéModèle de message
VARIABLE_NOT_FOUNDwarningCannot resolve relative quantity: target intermediate '&{targetName}' is not defined.
RELATIVE_QUANTITY_UNRESOLVEDwarningCannot resolve relative quantity: target ingredient '@{targetName}' was not found in the current section.
RELATIVE_QUANTITY_UNKNOWN_MASSwarningCannot compute relative quantity for '{item}': mass of target '{targetName}' is unknown.
CIRCULAR_REFERENCEerrorCircular reference detected: '{name}' depends on itself.
UNDEFINED_REFERENCEerrorUndefined reference '{prefix}{name}' — no prior step or section produces this item.
MISSING_UNITwarning{type} requires an explicit unit (e.g. min, s, °C).
INVALID_UNITwarningInvalid unit "{value}" for {type}.
SCOPE_CONFLICTerrorIntermediate variable '&{varName}' is redefined; variable names must be unique across the recipe.
MISSING_INGREDIENTwarningIngredient "{id}" not found in database — nutritional metrics and density conversions unavailable.
MISSING_MACROSinfoIngredient "{id}" has no macronutrient data in database — nutritional totals are partial.
UNKNOWN_MASSinfoCannot calculate mass for "{id}" — omitted from nutritional totals.
INVALID_MODIFIER_COMBINATIONwarningIncompatible modifiers on "{item}": {combination}.
COMPOSITE_PARENT_CONFLICTerrorComposite child "{childName}" was already linked to parent "{previousParent}" — using it with a different parent "{newParent}" here means both will share the same database entry, which is very likely wrong.
INVALID_BAKERS_REFERENCEwarning'{item}' cannot be used as the Baker's percentage reference (*).
NO_BAKERS_REFERENCEwarningBaker's percentages (%) are used but no base flour (*) was designated.
TIME_PARADOXwarningTimeline conflict: {cause} is pulled earlier than recipe start to satisfy {conflict}.
TRACK_CONTENTIONinfoResource contention on track '{trackName}': delayed by {delay} min for '{item}'.
MODULE_NOT_FOUNDerrorModule "{specifier}" could not be found or resolved.
MODULE_PARSE_ERRORerrorSyntax error in imported module "{specifier}": {parseMessage}
MODULE_CYCLEerrorCircular module import detected: {chain}.
MODULE_DEPTH_EXCEEDEDerrorImport depth limit exceeded ({depth}) while importing "{specifier}".
MODULE_EXPORT_NOT_FOUNDerrorModule "{specifier}" does not export '&{exported}' — it exists in the module but isn't re-exported. Add '-> &{exported}' to the section that produces it.
UNUSED_IMPORTwarningUnused import '&{local}' from "{specifier}".
UNRESOLVED_MODULE_YIELDerrorCannot compute yield for '&{binding}' from "{specifier}": missing physical mass data for one or more ingredients.
ESTIMATED_MODULE_YIELDwarningYield of '&{binding}' from "{specifier}" is estimated using standard ingredient densities or unit weights. Scale factor is approximate.
MODULE_UNIT_MISMATCHerrorUnit mismatch for '&{binding}' from "{specifier}": requested in '{requestedUnit}' but yields in '{yieldUnit}' without a conversion density.
MODULE_BATCH_INTERPRETATIONinfo'&{binding}' from "{specifier}" requested without unit — scaled as {batches} batch(es) of the module.
IMPORTED_BAKERS_REFERENCE_DROPPEDinfoBaker's percentage base (*) from "{specifier}" is scoped to its own module and was not imported.
DENSITY_OVERRIDE_SHADOWEDinfoDensity for "{ingredient}" in host recipe ({hostValue}) overrides module "{specifier}" ({moduleValue}).
MODULE_SURPLUSinfoScaling "{specifier}" for '&{binding}' yields a surplus: {surplus}.
MODULE_SPECIFIER_INVALIDerrorInvalid module path "{specifier}": {reason}
MODULE_SCHEME_UNSUPPORTEDerrorUnsupported URL scheme in module specifier: "{specifier}".
STOCKED_RETRO_PLANNING_IGNOREDwarningStocked module "{specifier}" has a retro-planning offset "~{...}", which is ignored because stocked items require no prep time.
RETRO_PLANNING_OVERRIDE_SHADOWEDinfoHost retro-planning offset on "@use {specifier}" overrides the module's internal offset.
MODULE_BINDING_SHADOWS_INGREDIENTwarningImported binding '&{binding}' from "{specifier}" shares name with a database ingredient.
STOCKED_DESTRUCTURED_NUTRITION_BLENDEDinfoStocked module "{specifier}" uses destructured imports — nutrition profile is averaged across the entire module.

Une quantité relative référence une variable intermédiaire (50% of &nom) qui n’a été déclarée nulle part dans la recette comme sortie intermédiaire (>> nom). Correction : déclarez la variable avant de la référencer, ou corrigez la faute de frappe.

Une quantité relative cible un ingrédient (50% of @nom) qui n’est pas apparu plus tôt dans la même section. Attention, contrairement aux variables, les cibles relatives aux ingrédients sont scopées par section. Correction : déplacez l’ingrédient cible plus haut dans la même section, ou passez par une variable intermédiaire (&nom) si la portée doit être globale.

Émis pendant l’analyse : la cible d’une quantité relative a bien été trouvée, mais sa propre masse est incalculable (unité/densité introuvable). Le pourcentage ne peut donc pas s’appliquer. Correction : attribuez à l’ingrédient cible une unité standardisable, ou renseignez sa densité / son unit_weight dans la base de données.

La quantité relative d’un ingrédient se cible elle-même (@farine{50% of @farine}). Correction : supprimez l’auto-référence (une quantité relative doit pointer vers un ingrédient ou une variable différente).

Une référence pure (&nom) ou un ingrédient référençable (@&nom) pointe vers un élément qui n’a jamais été enregistré en amont de la recette. Correction : introduisez l’ingrédient (sans &) avant de le référencer, ou corrigez la faute de frappe.

Un Timer (minuteur) ou une Temperature a été écrit sans unité explicite (ex. ~{10} au lieu de ~{10 min}). Correction : ajoutez l’unité manquante.

Soit un Timer a reçu une quantité non numérique (du texte), soit une Temperature a reçu une unité autre que Celsius ou Fahrenheit. Correction : utilisez une valeur numérique couplée à une unité temporelle reconnue pour les minuteurs, et limitez-vous à °C ou °F pour les températures.

Deux sections déclarent un même nom de variable (>> nom). Les noms de variables doivent être uniques à l’échelle de toute la recette, pas juste de la section. Correction : renommez l’une des deux variables.

Lors de l’estimation nutritionnelle, un ingrédient (dont on connaît la masse) n’a trouvé aucune correspondance (par ID ou alias) dans la base de données. Correction : ajoutez l’ingrédient ou son alias dans votre base.

L’ingrédient a bien été trouvé en base, mais ne possède aucun bloc nutrition. Correction : documentez le bloc nutrition dans l’entrée YAML correspondante.

L’Analyseur n’a pu résoudre aucune masse pour cet ingrédient (unité inexploitable, aucune densité/unit_weight), il est donc exclu des totaux nutritionnels. Correction : comme pour RELATIVE_QUANTITY_UNKNOWN_MASS, fournissez une unité standardisable ou des données physiques en base.

Modificateurs en conflit ou dupliqués sur un même ingrédient/matériel. Par exemple : optional (?) couplé à important (*), ou hidden (-) avec referenceable (&). La collision exacte est détaillée dans le .message. Correction : retirez le modificateur incriminé.

L’ingrédient marqué comme ancre du % boulanger (via le modificateur * ou l’option bakersReference) possède lui-même une masse dérivée (quantité relative par rapport à un autre ingrédient). Il ne peut donc pas servir de base à 100 % (dépendance circulaire). Correction : choisissez un ingrédient doté d’une quantité absolue pour servir de point de référence.

Le mode Baker’s Math a été explicitement requis (via enableBakersMath avec un modificateur * nu, ou via un identifiant bakersReference), mais aucun ingrédient correspondant n’a été trouvé. Correction : ajoutez le modificateur * sur un ingrédient, ou corrigez l’ID passé à bakersReference.

v1.2.0

Un nom court d’ingrédient composite (ex : @jus) est extrait de deux parents différents dans la même recette (ex : <@citron dans une étape et <@orange dans une autre). Correction : utilisez le nom complet de l’enfant (ex : @jus de citron et @jus d'orange) pour éviter les collisions d’identité dans la base de données.

v1.2.0

Un module importé via une directive @use n’a pas pu être résolu ou trouvé sur le système de fichiers. Correction : vérifiez le chemin du fichier, l’extension (.gram) ou les alias configurés dans config.yaml.