Skip to content

Module imports

v1.2.0

A recipe can @use another .gram file as a reusable sub-component — a shortcrust pastry, a stock, a batch of egg whites — instead of copy-pasting its steps into every recipe that needs it.

---
title: 'Lemon Meringue Tart'
---

@use "./bases/shortcrust-pastry.gram" as &shortcrust

## Assembly

[Line] The #tart ring{} with &shortcrust{250g}.

[Fill] With @lemon curd{300g}.

The imported recipe’s steps are seamlessly inlined into the timeline: a base’s resting time interleaves with the host’s steps, and its raw ingredients merge directly onto the unified shopping list.


@use directives must be declared immediately after the frontmatter, before any cooking step — see Document Structure.

# 1. Default import (binds the default exported component)
@use "./bases/shortcrust.gram" as &crust

# 2. Destructuring (imports multiple distinct components)
@use "./bases/tart-elements.gram" as { &crust, &frangipane }

# 3. Renaming (resolves naming conflicts in the host recipe)
@use "./bases/cream.gram" as { &cream as &pastry-cream }

# 4. Multi-word identifiers (wrapped in braces)
@use "./bases/tart-elements.gram" as &sweet pastry{}

# 5. Time-anchoring (schedules preparation 2 days ahead)
@use "./bases/starter.gram" as &starter ~{-2d}
  • The specifier must end in .gram.
  • Supported formats: relative path (./, ../), project root (@/), or custom alias (@alias/).
  • Paths are strictly confined to the project root containing .gram/ — absolute paths or URLs are rejected.

./ and ../ resolve relative to the importing file’s directory. For deeply nested recipes, Gram provides two clean alternatives:

@/ always points to the project root (the directory containing .gram/), regardless of file depth:

@use "@/bases/shortcrust-pastry.gram" as &pate

Only section-level intermediate variables (->& on a ## Section header) are exported. Step-level intermediates (->& inside a step) remain private to the module. See Intermediate Variables.

---
title: 'Tart Elements'
---

## Sweet Pastry ->&crust

[Mix] The @flour{200g}, @butter{100g}, @sugar{50g}, and @water{50ml}. Chill for ~_{1h}.

## Frangipane Cream ->&frangipane

[Cream] The @almond flour{100g}, @butter{100g}, @sugar{100g}, and @eggs{100g}.

Gram measures a module’s own output from its ingredients — nothing to declare:

---
title: 'Shortcrust Pastry'
---

## Pastry

[Mix] @flour{300g} with @butter{200g}.

shortcrust.gram sums its own ingredients to 500g. When an importing recipe requests &pate{250g}, Gram scales every ingredient in it by 0.5 automatically — the ratio between what was asked for and what the module actually measures out to.

When importing multiple components from the same module, each component is measured on its own — from its own section’s ingredients, plus anything that section itself references. If different ratios are requested, the largest ratio applies to the entire module, and Gram reports the extra production:

---
title: 'Tart Elements'
---

## Sweet Pastry ->&crust

[Mix] The @flour{200g}, @butter{100g}, @sugar{50g}, and @water{50ml}. Chill for ~_{1h}.

## Frangipane Cream ->&frangipane

[Cream] The @almond flour{100g}, @butter{100g}, @sugar{100g}, and @eggs{100g}.
@use "./bases/tart-elements.gram" as { &crust, &frangipane }

## Pear Tart

[Line] The #tart ring{} with the &crust{800g}.

[Spread] The &frangipane{400g} over the base and top with sliced @pears{3}.

&crust{800g} against a measured 400g of sweet pastry requires a ×2 ratio; &frangipane{400g} against 400g of frangipane cream requires ×1. The larger ratio (×2) applies to the whole module, so frangipane comes out to 800g produced against the 400g actually used.

Cooking and resting times are never scaled. Doubling a cookie dough does not double or halve the baking duration.

When a discrete count is requested against a module’s measured mass (&cookies{2} against a module that measures out to 500g), Gram interprets the request as 2 full batches and emits MODULE_BATCH_INTERPRETATION.


Modules can import other modules, creating a dependency chain:

recipe.gram ──▶ @use "./sauce.gram" ──▶ @use "./stock.gram"
  • Encapsulation: recipe.gram interacts only with &sauce. Private steps and local variables of stock.gram remain isolated.
  • Unified Schedule & Shopping List: Raw ingredients (carrots, aromatics, bones) and simmering steps from stock.gram automatically bubble up into the main recipe.

When a menu imports multiple dishes sharing a common base (e.g. an entrée and a dessert both importing pate-sablee.gram):

  • The shared base file is parsed only once.
  • The compiler creates independent instances scaled to each dish’s requirements.
  • Raw ingredients (flour, butter) merge cleanly on the shopping list.

  • Section Scoping: Relative quantities (@water{70% @&flour}) and ingredient checks resolve only within the module’s own sections. Host recipes using identical ingredient names never cause conflicts.
  • Headless Modules: A module without section headers (##) is isolated into its own dedicated section upon import — it never merges into the host’s untitled section.

Informational metadata (title, description, author, tags, category) belongs strictly to the host recipe — importing a vegan base does not make an egg-based host tart vegan.

Exceptions:

  • densities: merges into the host (host values take precedence on conflict).
  • Baker’s percentage references (*) inside imported modules are stripped on import.

When a component is already on hand (bought ready-made or prepared ahead of time), pass --stock to the CLI:

gram shop dinner.gram --stock @bases/bouillon.gram
gram cook dinner.gram --stock @bases/bouillon.gram
  • Timeline: Cooking and prep steps for stocked modules are omitted.
  • Shopping List: Appears as a single purchasable item (e.g. 1 bouillon) instead of raw ingredients.
  • Nutrition: Exact caloric and macronutrient values are preserved.

Supported by all CLI commands resolving @use (build, check, view, export, cook, print, watch, shop, scale).


When a fresh preparation must be started in advance (e.g. a sourdough starter or marinade), anchor it directly on the @use line:

@use "./bases/starter.gram" as &starter ~{-2d}

## Bread

[Knead] With &starter{150g}.

The Gram Language Server resolves @use directives in real time:

  • Instant Diagnostics: Cross-file validation updates live as you type.
  • Gantt & Live Preview: Composed schedule reflects scaled times and dependencies.
  • Go to Definition: Jumping to &pate opens the source module at the exact exported section.
  • Auto-completion: Typing @use " completes ./, ../, @/, and configured paths: aliases.