Aller au contenu

Référence API

Gram n’est pas qu’un simple format de fichier : c’est un pipeline de petites bibliothèques composables. Chaque package accomplit une seule tâche et transmet un objet JSON au suivant. Vous pouvez utiliser le pipeline complet, ou piocher uniquement la brique dont vous avez besoin (ex : utiliser juste le parser pour construire un linter).

npm install @gram-lang/parser @gram-lang/modules @gram-lang/kitchen @gram-lang/analyzer @gram-lang/renderer @gram-lang/format

Tous ces packages sont ESM only, side-effect free, et tournent partout où du JavaScript s’exécute : Node.js, Deno, Bun, environnements Edge, ou directement dans le navigateur (voir le Playground pour une démo 100 % côté client).

PaquetRôlePoint d’entrée principal
@gram-lang/parserTransforme le texte source .gram en Arbre Syntaxique Abstrait (AST)getAST(source)
@gram-lang/modulesRésout les imports @use, suit les dépendances, ajuste les sous-bases et compose un AST unifiéloadModuleGraph(entry, host), composeRecipe(graph, opts)
@gram-lang/kitchenCompile l’AST en une charge utile structurée, prête au rendu (liste de courses, minutages, registre)compile(ast, options?)
@gram-lang/analyzerEnrichit une recette compilée avec des propriétés physiques (masse, rendement, nutrition, pourcentages boulanger) via une base de données d’ingrédientsanalyze(compiled, database, options?)
@gram-lang/rendererEffectue le rendu d’une recette (compilée ou analysée) en Markdown ou HTMLtoMarkdown / toHTML / toPrintHTML
@gram-lang/formatFormateur de code canonique pour fichiers .gram (13 règles unifiées)formatGram(source, options?)
@gram-lang/i18nNormalisation partagée des unités/du temps et dictionnaires de chaînes UI utilisés en interne par les paquets ci-dessusnormalizeUnit, getDictionary

L’étape d’analyse est purement optionnelle : compile() fournit à lui seul une recette structurée et prête au rendu (bien qu’avec des quantités brutes). @gram-lang/analyzer n’est requis que si vous avez besoin de la conversion de masse, des estimations nutritionnelles, ou du % du boulanger — opérations qui exigent une base de données d’ingrédients.

Raccourci optionnel Node uniquement (@gram-lang/cli)

Section intitulée « Raccourci optionnel Node uniquement (@gram-lang/cli) »

Si vous évoluez sous Node.js et n’avez pas besoin d’un contrôle granulaire, le package @gram-lang/cli (celui-là même qui propulse le binaire gram) expose également une petite API très pratique :

import { runPipeline, GramCLIError } from '@gram-lang/cli';

const { content, compiled, analyzed } = await runPipeline('recette.gram', {
  db: myDatabase,             // optionnel — omettez pour sauter l'étape d'analyse
  scaleFactor: 2,              // optionnel
  bakersReference: 'farine',   // optionnel
});

runPipeline(filePath, options?) lit le fichier, puis enchaîne automatiquement getASTcompileanalyze (si la db est fournie) et vous renvoie un objet { content, compiled, analyzed }. Elle throw une GramCLIError (sous-classe d’Error portant un .exitCode via l’énumération exportée ExitCode) au lieu d’une erreur standard, ce qui facilite le mapping avec vos propres codes de retour CLI. Attention, cette fonction est réservée à Node.js (elle tape sur le disque via node:fs) : pour les navigateurs ou les environnements Edge, vous devrez brancher les packages manuellement comme illustré ci-dessous.

import { getAST, GramParseError } from '@gram-lang/parser';
import { compile } from '@gram-lang/kitchen';
import { analyze, validateIngredientDatabase } from '@gram-lang/analyzer';
import { toHTML } from '@gram-lang/renderer';

function renderRecipe(source: string, rawIngredientDb: unknown) {
  // 1. Parser le texte .gram en AST
  let ast;
  try {
    ast = getAST(source);
  } catch (err) {
    if (err instanceof GramParseError) {
      console.error(`Erreur de syntaxe à l'offset ${err.offset} : attendu ${err.expected}`);
    }
    throw err;
  }

  // 2. Compiler l'AST en une recette structurée (liste de courses, minutages, registre)
  const compiled = compile(ast);

  // 3. Charger et valider la base de données d'ingrédients, puis enrichir avec les données physiques/nutritionnelles
  const { data: database, rejected } = validateIngredientDatabase(rawIngredientDb);
  if (rejected.length > 0) {
    console.warn('Ingrédients invalides ignorés :', rejected);
  }
  const { result: analyzed, missingIngredients } = analyze(compiled, database);

  // 4. Rendu en HTML
  return toHTML(analyzed);
}

Lorsque vous manipulez des recettes composées de directives @use réparties sur plusieurs fichiers, insérez @gram-lang/modules entre l’analyseur syntaxique et le compilateur :

import { loadModuleGraph, composeRecipe, finalizeComposed, createMemoryHost } from '@gram-lang/modules';
import { compile } from '@gram-lang/kitchen';
import { analyze } from '@gram-lang/analyzer';
import { toHTML } from '@gram-lang/renderer';

async function renderModularRecipe(entryUri: string, host: ModuleHost, rawIngredientDb: unknown) {
  // 1. Résoudre et charger le graphe complet d'importation (DFS, détection de cycles)
  const graph = await loadModuleGraph(entryUri, host);

  // 2. Composer en un AST unique avec calcul des proportions et renommage hygiénique
  const composed = composeRecipe(graph, { db: database });

  // 3. Compiler l'AST composé
  const compiled = compile(composed.ast);
  const result = finalizeComposed(compiled, composed);

  // 4. Enrichir avec la base physique + ingrédients synthétiques pour le stock
  const enrichedDb = { ...database, ...composed.syntheticIngredients };
  const { result: analyzed } = analyze(result, enrichedDb);

  // 5. Rendu en HTML
  return toHTML(analyzed);
}

Gérer les erreurs de parsing et les avertissements

Section intitulée « Gérer les erreurs de parsing et les avertissements »

getAST() est la seule fonction du pipeline qui throw une exception : s’il y a une erreur de syntaxe, l’AST ne peut tout simplement pas être généré. Toutes les étapes ultérieures se contentent de collecter des avertissements (warnings) : une recette bancale finira toujours de compiler, pour vous laisser l’opportunité de l’afficher et de remonter visuellement le problème à l’utilisateur.

  • GramParseError (levée par getAST) : transporte .message (le log lisible renvoyé par ohm-js, extrait de source inclus), .offset (la position du curseur dans la chaîne d’entrée), et .expected (ce que le parser attendait à cet endroit).
  • CompilationResult.warnings / AnalyzedCompilationResult.warnings (retournés par compile() et propagés par analyze()) : un tableau d’objets Warning — jamais de simples chaînes. Voir la référence des avertissements pour la liste complète des codes et comment les traiter comme des erreurs façon --strict.