mold

A lightweight template engine for MoonBit.

moonbit
template
templating
moon add robinfang/mold@0.3.0
Download zip
Author
Version
0.3.0
License
Apache-2.0
Last updated
2 months ago
Downloads
24
README

#mold

mold 是一个面向 MoonBit 生态的轻量模板引擎。

mold is a lightweight template engine for the MoonBit ecosystem.

#总体概况 / Overview

mold 当前聚焦在一个明确范围内:把模板稳定解析成 AST,再基于统一的 Value 模型完成渲染。它优先服务于报告生成、邮件模板、配置文件生成、文档模板这类文本生成场景,同时也支持通过显式配置进入 HTML 输出场景。

mold currently focuses on a clear scope: parse templates into ASTs and render them against a unified Value model. It primarily targets text generation scenarios such as reports, email templates, config files, and document generation, while also supporting HTML output through explicit configuration.

#阅读框架 / Reading Guide

第一次接触 mold 时,建议按下面顺序阅读:

  1. 先看本页,了解项目定位、安装方式和三种推荐使用路径。
  2. 再看 docs/getting-started.md,把最小示例和运行方式跑通。
  3. 需要写模板时,看 docs/template-syntax.md
  4. 需要多模板组合、HTML 输出或错误排查时,看对应进阶文档和 examples。

When reading mold for the first time, start with this page for project scope, installation, and the three recommended workflows, then move to getting-started, and finally to template-syntax when you begin writing templates.

#安装 / Installation

moon add robinfang/mold

#30 秒上手 / 30-Second Quick Start

let output = @mold.render(
"Hello, {{ name }}!",
@mold.object({ "name": @mold.string("World") }),
)

这三条路径对应 mold 的总体框架:

  • 顶层函数:更轻,适合快速渲染
  • Template:更稳,适合 parse once, render many
  • Engine:更可扩展,适合 include、autoescape、自定义 filter

These three workflows map directly to the overall structure of mold: top-level rendering for quick use, Template for repeated rendering, and Engine for extensibility.

  • 小模板或一次性渲染:@mold.render(...)
  • 重复渲染同一模板:Template::parse(...).render(...)
  • 需要 include、autoescape 或自定义 filter:Engine

  • Use @mold.render(...) for small templates or one-off rendering.
  • Use Template::parse(...).render(...) when the same template is rendered repeatedly.
  • Use Engine when you need include, autoescape, or custom filters.

#文档导航 / Documentation

#示例 / Examples

  • src/examples/hello/
    • 最小变量插值与 filter
    • Minimal interpolation and filters
  • src/examples/report/
    • 循环、条件分支与嵌套对象
    • Loops, conditionals, and nested objects
  • src/examples/email/
    • 更接近真实业务的文本模板
    • A more realistic text template
  • src/examples/include_loader/
    • Engine + Loader + include 的模板组合
    • Template composition with Engine + Loader + include
  • src/examples/html_safe/
    • with_autoescape(true)| safe
    • with_autoescape(true) and | safe
  • src/examples/custom_filter/
    • Engine::register_filter(...) 自定义 filter
    • Custom filters via Engine::register_filter(...)
  • src/examples/from_json/
    • from_json(...) 把 JSON 转成模板上下文
    • Convert JSON into template context with from_json(...)
  • src/examples/site/
    • 静态网站批量渲染
    • Static site batch rendering

运行示例 / Run an example:

moon run src/examples/hello moon run src/examples/include_loader moon run src/examples/html_safe moon run src/examples/custom_filter moon run src/examples/from_json

#能力摘要 / Feature Summary

  • 纯文本渲染 / plain text rendering
  • {{ expr }} 插值与点路径访问 / interpolation with dotted lookup
  • {% if %} / {% else %} / {% endif %} 条件分支 / conditional blocks
  • {% for %} / {% endfor %} 循环,支持嵌套控制块 / loops with nested control blocks
  • 内置 filters / built-in filters:
    • upper
    • lower
    • trim
    • default(...)
    • join(...)
    • escape
    • length
    • safe
  • 比较与布尔表达式 / comparison and boolean expressions:
    • == != < <= > >=
    • and or not
    • parentheses grouping
  • {% include "template_name" %} 模板包含 / template inclusion
  • whitespace control / 空白控制:{%- / -%}{{- / -}}
  • {# ... #} 模板注释 / template comments
  • Engine 级 autoescape / engine-level autoescape
  • Template::ast() 调试访问 / AST debug accessor
  • Template::inspect() / inspect(...) 模板依赖诊断 / template dependency inspection
  • from_json / from_map 上下文转换 / context conversion helpers
  • 结构化错误类型与源码位置 / structured errors with source spans

#HTML 安全 / HTML Safety

默认情况下,mold 不自动转义 HTML,适合通用文本生成场景。输出 HTML 时,建议显式使用 Engine::with_autoescape(true)

By default, mold does not autoescape HTML, which keeps it suitable for general text generation. For HTML output, explicitly enable Engine::with_autoescape(true).

let engine = @mold.Engine::new().with_autoescape(true)

let output = engine.render(
"{{ user_input }} | {{ trusted_html | safe }}",
@mold.object({
"user_input": @mold.string("<strong>escaped</strong>"),
"trusted_html": @mold.string("<em>kept</em>"),
}),
)

#API 摘要 / API Snapshot

pub fn render(source : String, ctx : Value) -> String raise MoldError

pub fn Template::parse(source : String) -> Template raise MoldError
pub fn Template::render(self : Template, ctx : Value) -> String raise MoldError
pub fn Template::source(self : Template) -> String
pub fn Template::ast(self : Template) -> Array[Node]
pub fn Template::inspect(self : Template) -> TemplateInspection

pub fn Engine::new() -> Engine
pub fn Engine::with_loader(self : Engine, loader : Loader) -> Engine
pub fn Engine::with_autoescape(self : Engine, autoescape : Bool) -> Engine
pub fn Engine::register_filter(self : Engine, name : String, filter : Filter) -> Unit raise MoldError
pub fn Engine::parse(self : Engine, source : String) -> Template raise MoldError
pub fn Engine::render(self : Engine, source : String, ctx : Value) -> String raise MoldError
pub fn Engine::inspect(self : Engine, source : String) -> TemplateInspection raise MoldError

pub fn inspect(source : String) -> TemplateInspection raise MoldError

#当前限制 / Current Limits

  • 不支持模板继承 / no template inheritance
  • 不支持宏系统 / no macro system
  • 不支持异步模板 / no async templates
  • 不支持自动模板目录扫描 / no automatic template discovery

#在线体验 / Playground

MoldLive 是一个在线模板游乐场,mold 编译为 WASM 在浏览器中直接运行:

  • 三栏编辑器(模板 / JSON 数据 / 实时输出)
  • 4 个内置示例(Hello / Email / SVG Card / Offline Report)
  • mold 语法高亮
  • 零后端,模板不离开你的浏览器

MoldLive is an online playground where mold runs as WASM directly in your browser:

  • Three-panel editor (template / JSON data / live output)
  • 4 built-in examples
  • mold syntax highlighting
  • Zero backend — templates never leave your browser

#发布状态 / Release Status

mold 已发布到 mooncakes.io,当前版本为 0.3.0

mold is now published on mooncakes.io, and the current version is 0.3.0.

#开源协议 / License

Apache-2.0

#
Filter

type Filter = (Value, Array[Value]) -> Value

#
Loader

type Loader = (String) -> String?

#
MoldError

pub suberror MoldError {
LexerError((String, SourceSpan))
ParserError((String, SourceSpan))
UnknownFilter(String)
DuplicateFilter(String)
MissingVariable(String)
MissingInclude(String)
IncludeDepthExceeded
TypeMismatch((String, String))
} derive(Eq,
Debug
)

#
BinaryOp

pub enum BinaryOp {
Eq
Ne
Lt
Le
Gt
Ge
And
Or
}

#
CompiledInclude

type CompiledInclude

#
Engine

pub struct Engine {
filters : Array[FilterEntry]
loader : (String) -> String??
autoescape : Bool
}

#
Engine::inspect

fn Engine::inspect(self : Engine, source : String) -> TemplateInspection raise MoldError

#
Engine::new

fn Engine::new() -> Engine

#
Engine::parse

fn Engine::parse(self : Engine, source : String) -> Template raise MoldError

#
Engine::register_filter

fn Engine::register_filter(self : Engine, name : String, filter : (Value, Array[Value]) -> Value) -> Unit raise MoldError

#
Engine::render

fn Engine::render(self : Engine, source : String, ctx : Value) -> String raise MoldError

#
Engine::with_autoescape

fn Engine::with_autoescape(self : Engine, autoescape : Bool) -> Engine

#
Engine::with_loader

fn Engine::with_loader(self : Engine, loader : (String) -> String?) -> Engine

#
Expr

pub enum Expr {
Path(Array[String])
StringLiteral(String)
IntLiteral(Int)
FloatLiteral(Double)
BoolLiteral(Bool)
NullLiteral
FilterCall(Expr, String, Array[Expr])
Binary(Expr, BinaryOp, Expr)
Unary(UnaryOp, Expr)
}

#
FilterEntry

type FilterEntry

#
Node

pub enum Node {
Text(String)
Interpolation(Expr)
If(Expr, Array[Node], Array[Node])
For(String, Expr, Array[Node])
Include(String)
}

#
SourceSpan

pub struct SourceSpan {
start : Int
end : Int
line : Int
column : Int
} derive(Eq,
Debug
)

#
Template

pub struct Template {
source : String
ast : Array[Node]
filters : Array[FilterEntry]
loader : (String) -> String??
includes : Array[CompiledInclude]
autoescape : Bool
}

#
Template::ast

fn Template::ast(self : Template) -> Array[Node]

#
Template::inspect

fn Template::inspect(self : Template) -> TemplateInspection

#
Template::parse

fn Template::parse(source : String) -> Template raise MoldError

#
Template::render

fn Template::render(self : Template, ctx : Value) -> String raise MoldError

#
Template::source

fn Template::source(self : Template) -> String

#
TemplateInspection

pub struct TemplateInspection {
variables : Array[String]
filters : Array[String]
includes : Array[String]
}

#
UnaryOp

pub enum UnaryOp {
Not
}

#
Value

pub enum Value {
Null
Bool(Bool)
Int(Int)
Float(Double)
String(String)
Array(Array[Value])
Object(Map[String, Value])
Safe(Value)
}

#
array

fn array(values : Array[Value]) -> Value

#
bool

fn bool(value : Bool) -> Value

#
float

fn float(value : Double) -> Value

#
from_json

fn from_json(json : Json) -> Value

#
from_map

fn from_map(map : Map[String, Value]) -> Value

#
inspect

fn inspect(source : String) -> TemplateInspection raise MoldError

#
int

fn int(value : Int) -> Value

#
null

fn null() -> Value

#
object

fn object(values : Map[String, Value]) -> Value

#
render

fn render(source : String, ctx : Value) -> String raise MoldError

#
string

fn string(value : String) -> Value