MoonTemplate is a MoonBit-native text template engine for generating HTML, emails, configuration files, prompts, and code snippets from simple declarative templates.
Why This Project
MoonTemplate generates structured text from data for practical MoonBit tooling
and application workflows.
MoonTemplate targets the common “generate structured text from data” problem in the MoonBit ecosystem:
static page generators
CLI report generation
config and manifest synthesis
email and notification rendering
code scaffolding and boilerplate generation
The implementation is intentionally small, readable, and reviewable, but it already covers the core engineering surface expected from an OSC2026 acceptance repository: public source, reproducible checks, real tests, CI, a CLI entry point, API snapshots, and source-attribution documentation.
Feature Set
Variable interpolation: {{ name }}
Non-nesting comments: {# internal note #}
ASCII whitespace control: {{- name -}} and {%- if ok -%}
Hello, {{ user | trim }}!
{% if is_admin %}
Role: ADMIN
{% else %}
Role: USER
{% endif %}
Members:
{% for member in users %}
- {{ member | trim }}
{% endfor %}
Loop variables are read from comma-separated strings in the input context. Empty items are ignored.
import "Project2026-creator/moontemplate/src/moontemplate" @moontemplate
let template =
#|Hello, {{ user | trim }}!
#|{% if admin %}Welcome back, admin.{% else %}Welcome back.{% endif %}
let engine = @moontemplate.Engine::new(template).unwrap()
let ctx = Map([], capacity=2)
ctx.set("user", " MoonBit ")
ctx.set("admin", "true")
let output = engine.render(ctx)
println(output)
Register a custom filter
import "Project2026-creator/moontemplate/src/moontemplate" @moontemplate
let engine = @moontemplate.Engine::new("{{ name | suffix }}").unwrap()
engine.register_filter("suffix", fn(value) { value + "!" })
Safe output helpers
Use escape_html when interpolating untrusted text into HTML, escape_json
when producing a JSON string value, and slugify when deriving a stable
ASCII-oriented identifier:
<h1 id="{{ title | slugify }}">{{ title | escape_html }}</h1>
{{ payload | escape_json }}
{# comments are removed before rendering #}
{{ title | replace("Moon", "Star") | truncate(20) | default("Untitled") }}
{%- if enabled -%}
enabled
{%- endif -%}
Arguments are limited to double-quoted strings (with \\ and \" escapes)
and signed decimal integers. Calls remain encoded in the compatible
Node::Variable(String, Array[String]) representation.
Inspection and bounded rendering
Use inspect_template before accepting templates from users or CI. It reports
node counts, literal character counts, referenced context keys, filter
dependencies, and lint issues. For untrusted input, use
RenderLimits::new(output_chars, iterations, depth) and inspect the returned
RenderReport.metrics() counters.
CLI Usage
The CLI package targets native, so it needs a system C compiler when you run it locally.
moon run src/cli --target native -- --file examples/welcome.txt --var name=MoonBit
moon run src/cli --target native -- --template "Hello {{ name | uppercase }}" --var name=moonbit
moon run src/cli --target native -- --file examples/secure-output.txt --var "title=MoonBit & Templates" --var "body=<strong>safe</strong>"
moon run src/cli --target native -- --diagnostics json --template "{{ name | truncate(\"five\") }}"
moon run src/cli --target native -- --check --template "Hello {{ name | trim }}"
moon run src/cli --target native -- --stats --template "{% for item in items %}{{ item }}{% endfor %}"
moon run src/cli --target native -- --max-output 1000 --max-iterations 50 --max-depth 8 --template "{{ name }}" --var name=MoonBit
The first command reads the tracked example file examples/welcome.txt, so it can be copied and run immediately after cloning.
Supported options:
--file <path> or positional file path
--template <inline-template>
--var key=value (repeatable)
--vars-file <path> for key=value entries; blank lines and # comments are ignored
--diagnostics text|json (text is the default)
--check, --stats, and --max-output/--max-iterations/--max-depth
--help
--file and --template are mutually exclusive. A missing file, malformed
variable assignment, parser error, or unknown option is reported as a CLI error.
JSON diagnostics use the stable fields code, kind, message, line,
column, and source_line; runtime-only filter failures use line and column
zero.
For a larger, repeatable native workload, run
scripts/benchmark.ps1 on Windows or
scripts/benchmark.sh on Linux/macOS. The same
10-iteration benchmark runs in both CI workflows and reports total and average
milliseconds in the job log; it is an end-to-end CLI baseline, not a hardware
independent performance promise.
The library also contains a 14-case deterministic workload catalog and a
machine-readable report API (run_benchmark_suite and
benchmark_report_json) for CI evidence.
Quality Gates
CI installs the current stable MoonBit toolchain from the official installer.
The organizer feedback originally cited MoonBit 0.10.3; current stable
toolchains use pkgtype(kind: "executable") in moon.pkg. CI prints the exact
installed version and checks the committed API snapshot.
Local verification:
moon fmt --check
moon info
moon build
moon check --deny-warn
moon test --deny-warn
moon check --target wasm-gc --deny-warn
moon test --target wasm-gc --deny-warn
moon check --target js --deny-warn
moon test --target js --deny-warn
moon check --target native --deny-warn
moon test --target native --deny-warn
moon test --deny-warn --enable-coverage
moon coverage report -f summary
The library is checked and tested on wasm-gc, JavaScript, and native. The CLI
is intentionally native-only because it uses process arguments and filesystem
I/O. Native commands require a C compiler (build-essential on the CI runner).
MoonTemplate
MoonTemplate is a MoonBit-native text template engine for generating HTML, emails, configuration files, prompts, and code snippets from simple declarative templates.
Why This Project
MoonTemplate generates structured text from data for practical MoonBit tooling and application workflows.
MoonTemplate targets the common “generate structured text from data” problem in the MoonBit ecosystem:
The implementation is intentionally small, readable, and reviewable, but it already covers the core engineering surface expected from an OSC2026 acceptance repository: public source, reproducible checks, real tests, CI, a CLI entry point, API snapshots, and source-attribution documentation.
Feature Set
{{ name }}{# internal note #}{{- name -}}and{%- if ok -%}{{ name | trim | uppercase }}replace("old", "new"),truncate(20),default("fallback"),prefix,suffix,pad_left,pad_right, and Unicodeslicetrim,uppercase,lowercase,escape_html,escape_json,slugify,length,collapse_whitespace,capitalize, andnewline_to_brEngine::register_filterif/else{% for item in list %}src/moontemplate/pkg.generated.mbtiTemplate Syntax
Loop variables are read from comma-separated strings in the input context. Empty items are ignored.
More syntax examples live in
docs/syntax.md.Quick Start
Add the library
Render from MoonBit
Register a custom filter
Safe output helpers
Use
escape_htmlwhen interpolating untrusted text into HTML,escape_jsonwhen producing a JSON string value, andslugifywhen deriving a stable ASCII-oriented identifier:The tracked
examples/secure-output.txtfile demonstrates the HTML use case end to end.Parameterized filters and whitespace control
Arguments are limited to double-quoted strings (with
\\and\"escapes) and signed decimal integers. Calls remain encoded in the compatibleNode::Variable(String, Array[String])representation.Inspection and bounded rendering
Use
inspect_templatebefore accepting templates from users or CI. It reports node counts, literal character counts, referenced context keys, filter dependencies, and lint issues. For untrusted input, useRenderLimits::new(output_chars, iterations, depth)and inspect the returnedRenderReport.metrics()counters.CLI Usage
The CLI package targets
native, so it needs a system C compiler when you run it locally.The first command reads the tracked example file
examples/welcome.txt, so it can be copied and run immediately after cloning.Supported options:
--file <path>or positional file path--template <inline-template>--var key=value(repeatable)--vars-file <path>forkey=valueentries; blank lines and#comments are ignored--diagnostics text|json(text is the default)--check,--stats, and--max-output/--max-iterations/--max-depth--help--fileand--templateare mutually exclusive. A missing file, malformed variable assignment, parser error, or unknown option is reported as a CLI error. JSON diagnostics use the stable fieldscode,kind,message,line,column, andsource_line; runtime-only filter failures use line and column zero.For a larger, repeatable native workload, run
scripts/benchmark.ps1on Windows orscripts/benchmark.shon Linux/macOS. The same 10-iteration benchmark runs in both CI workflows and reports total and average milliseconds in the job log; it is an end-to-end CLI baseline, not a hardware independent performance promise.The library also contains a 14-case deterministic workload catalog and a machine-readable report API (
run_benchmark_suiteandbenchmark_report_json) for CI evidence.Quality Gates
CI installs the current stable MoonBit toolchain from the official installer. The organizer feedback originally cited MoonBit
0.10.3; current stable toolchains usepkgtype(kind: "executable")inmoon.pkg. CI prints the exact installed version and checks the committed API snapshot.Local verification:
The library is checked and tested on wasm-gc, JavaScript, and native. The CLI is intentionally native-only because it uses process arguments and filesystem I/O. Native commands require a C compiler (
build-essentialon the CI runner).Repository acceptance helpers:
docs/official-requirements.mddocs/acceptance-checklist.mddocs/source-attribution.mddocs/performance.mddocs/benchmark-evidence.mdscripts/verify_acceptance.ps1scripts/check_repo_compliance.pyRepository Links
Project2026-creator/moontemplateLicense
MoonTemplate is released under the Apache 2.0 License. See
LICENSE.