Module imports
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.
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.
Syntax
Section titled “Syntax”@use directives must be declared immediately after the frontmatter, before any cooking step — see Document Structure.
Specifier rules
Section titled “Specifier rules”- 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.
Project-root & aliased paths
Section titled “Project-root & aliased paths”./ 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:
Configure path aliases in .gram/config.yaml:
Then import directly using @alias/:
What a module exports
Section titled “What a module exports”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.
Scaling
Section titled “Scaling”Gram measures a module’s own output from its ingredients — nothing to declare:
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.
Scaling destructured exports
Section titled “Scaling destructured exports”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:
&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.
Timers & batches
Section titled “Timers & batches”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.
Chained imports & transitivity
Section titled “Chained imports & transitivity”Modules can import other modules, creating a dependency chain:
- Encapsulation:
recipe.graminteracts only with&sauce. Private steps and local variables ofstock.gramremain isolated. - Unified Schedule & Shopping List: Raw ingredients (carrots, aromatics, bones) and simmering steps from
stock.gramautomatically bubble up into the main recipe.
Diamond dependencies & cycle prevention
Section titled “Diamond dependencies & cycle prevention”Diamond dependencies
Section titled “Diamond dependencies”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.
Cycle prevention
Section titled “Cycle prevention”Encapsulation
Section titled “Encapsulation”- 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.
Frontmatter inheritance
Section titled “Frontmatter inheritance”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.
Stocking an import (--stock)
Section titled “Stocking an import (--stock)”When a component is already on hand (bought ready-made or prepared ahead of time), pass --stock to the CLI:
- 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).
Anchoring an import in time (~{-2d})
Section titled “Anchoring an import in time (~{-2d})”When a fresh preparation must be started in advance (e.g. a sourdough starter or marinade), anchor it directly on the @use line:
Editor & language server support
Section titled “Editor & language server support”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
&pateopens the source module at the exact exported section. - Auto-completion: Typing
@use "completes./,../,@/, and configuredpaths:aliases.