Skip to content

xray/ExpressiveMD

Repository files navigation

ExpressiveMD

A typed templating DSL for generating Markdown.


The .emd format is designed for LLM orchestration, agent workflows, and documentation generation.

The ExpressiveMD Family

VSCode Extension: GitHub | Extension

Features

  • Type-safe templates - Declare typed props with validation at render time
  • Template composition - Include and compose templates with prop passing
  • Control flow - Conditionals, loops, and expressions with proper operator precedence
  • Plugin system - Extend with custom formatters via git-hosted plugins
  • Type generation - Generate Python (Pydantic) or TypeScript types from templates

Installation

# Install
go install github.com/xray/ExpressiveMD/cmd/emd@latest

# Or clone and build
git clone https://github.com/xray/ExpressiveMD.git
cd ExpressiveMD
make build

Quick Start

Create a template (hello.emd):

---
inbound name: string
inbound greeting: string defaults to "Hello"
inbound items: list[string]?
---
# ${greeting}, ${name}!

${if items}
Your items:
${for item in items}
- ${item}
${end}
${end}

Create props (props.json):

{
  "name": "World",
  "items": ["apple", "banana", "cherry"]
}

Render:

emd build hello.emd --props props.json

Output:

# Hello, World!

Your items:
- apple
- banana
- cherry

CLI Commands

emd build <template.emd> --props <data.json> [--out <file>]
emd validate <template.emd> --props <data.json>
emd typegen --lang <python|typescript> --out <dir> <template.emd>
emd plugin install <git-url> [--ref <tag>]
emd plugin list
emd plugin remove <name>

Type System

Basic Types

inbound username: string
inbound age: int
inbound price: float
inbound active: bool

Optional and Defaults

inbound email: string?                    # optional (null if not provided)
inbound limit: int defaults to 10         # with default value

Complex Types

inbound tags: list[string]
inbound user: map{name: string, email: string?, active: bool}
inbound items: list[map{id: int, label: string}]

Template Syntax

Interpolation

${username}
${user.name}
${items[0].label}
${count + 1}
${name | uppercase()}

Control Flow

${if user.active && !user.banned}
Welcome, ${user.name}!
${else if user.pending}
Account pending approval.
${else}
Access denied.
${end}

${for item, idx in items}
${idx + 1}. ${item.name}
${end}

Built-in Formatters

Formatter Description
uppercase() Convert to uppercase
lowercase() Convert to lowercase
capitalize() Capitalize first letter
trim() Remove leading/trailing whitespace
length() Get length of string/list
default(val) Fallback if null

Formatters chain: ${name | trim() | uppercase()}

Template Includes

---
include header: ./partials/header.emd
include userCard: ./components/user-card.emd
---

${header(title="My Page")}

${for user in users}
${userCard(name=user.name, email=user.email)}
${end}

Type Generation

Generate typed bindings for your templates:

# Python (Pydantic v2)
emd typegen --lang python --out ./types template.emd

# TypeScript
emd typegen --lang typescript --out ./types template.emd

Plugins

Install formatters from git repositories:

emd plugin install https://github.com/expressivemd/date-formatter --ref v1.0.0

Use in templates:

---
plugin dateFormat: @expressivemd/date-formatter
---

Published: ${post.date | dateFormat("MMM D, YYYY")}

Development

make build    # Build binary to ./bin/emd
make test     # Run tests
make install  # Install to /usr/local/bin
make release  # Cross-compile for all platforms

License

This is free and unencumbered software released into the public domain. See UNLICENSE for details.

About

A typed, language agnostic, Markdown templating engine for the future.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

No packages published