2026-01-24 22:40:22 +00:00
<!DOCTYPE html>
<!-- retoor <retoor@molodetz.nl> -->
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > jinja - Wren-CLI Manual</ title >
< link rel = "stylesheet" href = "../css/style.css" >
</ head >
< body >
< button class = "mobile-menu-toggle" > Menu</ button >
< div class = "container" >
< aside class = "sidebar" >
< div class = "sidebar-header" >
< h1 >< a href = "../index.html" > Wren-CLI</ a ></ h1 >
< div class = "version" > v0.4.0</ div >
</ div >
< nav class = "sidebar-nav" >
< div class = "section" >
< span class = "section-title" > Getting Started</ span >
< ul >
< li >< a href = "../getting-started/index.html" > Overview</ a ></ li >
< li >< a href = "../getting-started/installation.html" > Installation</ a ></ li >
< li >< a href = "../getting-started/first-script.html" > First Script</ a ></ li >
< li >< a href = "../getting-started/repl.html" > Using the REPL</ a ></ li >
</ ul >
</ div >
< div class = "section" >
< span class = "section-title" > Language</ span >
< ul >
< li >< a href = "../language/index.html" > Syntax Overview</ a ></ li >
< li >< a href = "../language/classes.html" > Classes</ a ></ li >
< li >< a href = "../language/methods.html" > Methods</ a ></ li >
< li >< a href = "../language/control-flow.html" > Control Flow</ a ></ li >
< li >< a href = "../language/fibers.html" > Fibers</ a ></ li >
< li >< a href = "../language/modules.html" > Modules</ a ></ li >
</ ul >
</ div >
< div class = "section" >
< span class = "section-title" > API Reference</ span >
< ul >
< li >< a href = "index.html" > Overview</ a ></ li >
2026-01-25 03:58:39 +00:00
< li >< a href = "string.html" > String</ a ></ li >
< li >< a href = "number.html" > Num</ a ></ li >
2026-01-24 22:40:22 +00:00
< li >< a href = "http.html" > http</ a ></ li >
< li >< a href = "websocket.html" > websocket</ a ></ li >
< li >< a href = "tls.html" > tls</ a ></ li >
< li >< a href = "net.html" > net</ a ></ li >
< li >< a href = "dns.html" > dns</ a ></ li >
< li >< a href = "json.html" > json</ a ></ li >
< li >< a href = "base64.html" > base64</ a ></ li >
< li >< a href = "regex.html" > regex</ a ></ li >
< li >< a href = "jinja.html" class = "active" > jinja</ a ></ li >
< li >< a href = "crypto.html" > crypto</ a ></ li >
< li >< a href = "os.html" > os</ a ></ li >
< li >< a href = "env.html" > env</ a ></ li >
< li >< a href = "signal.html" > signal</ a ></ li >
< li >< a href = "subprocess.html" > subprocess</ a ></ li >
< li >< a href = "sqlite.html" > sqlite</ a ></ li >
< li >< a href = "datetime.html" > datetime</ a ></ li >
< li >< a href = "timer.html" > timer</ a ></ li >
< li >< a href = "io.html" > io</ a ></ li >
2026-01-25 03:58:39 +00:00
< li >< a href = "pathlib.html" > pathlib</ a ></ li >
2026-01-24 22:40:22 +00:00
< li >< a href = "scheduler.html" > scheduler</ a ></ li >
< li >< a href = "math.html" > math</ a ></ li >
</ ul >
</ div >
< div class = "section" >
< span class = "section-title" > Tutorials</ span >
< ul >
< li >< a href = "../tutorials/index.html" > Tutorial List</ a ></ li >
< li >< a href = "../tutorials/http-client.html" > HTTP Client</ a ></ li >
< li >< a href = "../tutorials/websocket-chat.html" > WebSocket Chat</ a ></ li >
< li >< a href = "../tutorials/database-app.html" > Database App</ a ></ li >
< li >< a href = "../tutorials/template-rendering.html" > Templates</ a ></ li >
< li >< a href = "../tutorials/cli-tool.html" > CLI Tool</ a ></ li >
</ ul >
</ div >
< div class = "section" >
< span class = "section-title" > How-To Guides</ span >
< ul >
< li >< a href = "../howto/index.html" > How-To List</ a ></ li >
< li >< a href = "../howto/http-requests.html" > HTTP Requests</ a ></ li >
< li >< a href = "../howto/json-parsing.html" > JSON Parsing</ a ></ li >
< li >< a href = "../howto/regex-patterns.html" > Regex Patterns</ a ></ li >
< li >< a href = "../howto/file-operations.html" > File Operations</ a ></ li >
< li >< a href = "../howto/async-operations.html" > Async Operations</ a ></ li >
< li >< a href = "../howto/error-handling.html" > Error Handling</ a ></ li >
</ ul >
</ div >
</ nav >
</ aside >
< main class = "content" >
< nav class = "breadcrumb" >
< a href = "../index.html" > Home</ a >
< span class = "separator" > /</ span >
< a href = "index.html" > API Reference</ a >
< span class = "separator" > /</ span >
< span > jinja</ span >
</ nav >
< article >
< h1 > jinja</ h1 >
< p > The < code > jinja</ code > module provides a Jinja2-compatible template engine for Wren. It supports variable interpolation, filters, control structures, template inheritance, macros, and more.</ p >
< pre >< code > import "jinja" for Environment, DictLoader, FileSystemLoader, Template</ code ></ pre >
< div class = "toc" >
< h4 > On This Page</ h4 >
< ul >
< li >< a href = "#environment-class" > Environment Class</ a ></ li >
< li >< a href = "#dictloader-class" > DictLoader Class</ a ></ li >
< li >< a href = "#filesystemloader-class" > FileSystemLoader Class</ a ></ li >
< li >< a href = "#template-syntax" > Template Syntax</ a >
< ul >
< li >< a href = "#variables" > Variables</ a ></ li >
< li >< a href = "#filters" > Filters</ a ></ li >
< li >< a href = "#control-structures" > Control Structures</ a ></ li >
< li >< a href = "#comments" > Comments</ a ></ li >
< li >< a href = "#whitespace-control" > Whitespace Control</ a ></ li >
< li >< a href = "#tests" > Tests</ a ></ li >
< li >< a href = "#macros" > Macros</ a ></ li >
< li >< a href = "#template-inheritance" > Template Inheritance</ a ></ li >
2026-01-25 03:58:39 +00:00
< li >< a href = "#markdown-tags" > Markdown Tags</ a ></ li >
2026-01-24 22:40:22 +00:00
</ ul >
</ li >
< li >< a href = "#filters-reference" > Filters Reference</ a ></ li >
< li >< a href = "#tests-reference" > Tests Reference</ a ></ li >
< li >< a href = "#built-in-functions" > Built-in Functions</ a ></ li >
< li >< a href = "#loop-variable" > Loop Variable</ a ></ li >
< li >< a href = "#examples" > Examples</ a ></ li >
</ ul >
</ div >
< h2 id = "environment-class" > Environment Class</ h2 >
< div class = "class-header" >
< h3 > Environment</ h3 >
< p > Template compilation environment with caching</ p >
</ div >
< p > The Environment class is the central object for template management. It handles template loading, compilation, and caching.</ p >
< h3 > Constructor</ h3 >
< div class = "method-signature" >
< span class = "method-name" > Environment.new</ span > (< span class = "param" > loader</ span > ) → < span class = "type" > Environment</ span >
</ div >
< p > Creates a new environment with the specified loader.</ p >
< ul class = "param-list" >
< li >< span class = "param-name" > loader</ span > < span class = "param-type" > (DictLoader|FileSystemLoader)</ span > - Template loader</ li >
</ ul >
< pre >< code > var loader = DictLoader.new({
"base.html": "< html> {% block content %}{% endblock %}< /html> ",
"page.html": "{% extends 'base.html' %}{% block content %}Hello{% endblock %}"
})
var env = Environment.new(loader)</ code ></ pre >
< h3 > Properties</ h3 >
< div class = "method-signature" >
< span class = "method-name" > autoEscape</ span > → < span class = "type" > Bool</ span >
</ div >
< p > Gets or sets whether HTML auto-escaping is enabled. Defaults to < code > true</ code > .</ p >
< pre >< code > env.autoEscape = false // Disable auto-escaping</ code ></ pre >
< h3 > Methods</ h3 >
< div class = "method-signature" >
< span class = "method-name" > getTemplate</ span > (< span class = "param" > name</ span > ) → < span class = "type" > Template</ span >
</ div >
< p > Loads and returns a template by name. Templates are cached after first load.</ p >
< ul class = "param-list" >
< li >< span class = "param-name" > name</ span > < span class = "param-type" > (String)</ span > - Template name</ li >
< li >< span class = "returns" > Returns:</ span > Compiled Template object</ li >
</ ul >
< pre >< code > var template = env.getTemplate("page.html")
var html = template.render({"title": "Welcome"})</ code ></ pre >
< div class = "method-signature" >
< span class = "method-name" > fromString</ span > (< span class = "param" > source</ span > ) → < span class = "type" > Template</ span >
</ div >
< p > Compiles a template directly from a source string without using the loader.</ p >
< ul class = "param-list" >
< li >< span class = "param-name" > source</ span > < span class = "param-type" > (String)</ span > - Template source code</ li >
< li >< span class = "returns" > Returns:</ span > Compiled Template object</ li >
</ ul >
< pre >< code > var template = env.fromString("Hello, {{ name }}!")
System.print(template.render({"name": "World"})) // Hello, World!</ code ></ pre >
< h2 id = "dictloader-class" > DictLoader Class</ h2 >
< div class = "class-header" >
< h3 > DictLoader</ h3 >
< p > Load templates from a map of template names to source strings</ p >
</ div >
< h3 > Constructor</ h3 >
< div class = "method-signature" >
< span class = "method-name" > DictLoader.new</ span > (< span class = "param" > templates</ span > ) → < span class = "type" > DictLoader</ span >
</ div >
< p > Creates a loader from a map of template name to template source.</ p >
< ul class = "param-list" >
< li >< span class = "param-name" > templates</ span > < span class = "param-type" > (Map)</ span > - Map of template names to source strings</ li >
</ ul >
< pre >< code > var loader = DictLoader.new({
"greeting.html": "Hello, {{ name }}!",
"list.html": "{% for item in items %}{{ item }}{% endfor %}"
})</ code ></ pre >
< h3 > Methods</ h3 >
< div class = "method-signature" >
< span class = "method-name" > getSource</ span > (< span class = "param" > name</ span > ) → < span class = "type" > String</ span >
</ div >
< p > Returns the template source for the given name. Raises an error if not found.</ p >
< div class = "method-signature" >
< span class = "method-name" > exists</ span > (< span class = "param" > name</ span > ) → < span class = "type" > Bool</ span >
</ div >
< p > Checks if a template with the given name exists.</ p >
< h2 id = "filesystemloader-class" > FileSystemLoader Class</ h2 >
< div class = "class-header" >
< h3 > FileSystemLoader</ h3 >
< p > Load templates from the filesystem</ p >
</ div >
< h3 > Constructor</ h3 >
< div class = "method-signature" >
< span class = "method-name" > FileSystemLoader.new</ span > (< span class = "param" > searchPath</ span > ) → < span class = "type" > FileSystemLoader</ span >
</ div >
< p > Creates a loader that loads templates from a directory.</ p >
< ul class = "param-list" >
< li >< span class = "param-name" > searchPath</ span > < span class = "param-type" > (String)</ span > - Base directory for templates</ li >
</ ul >
< pre >< code > var loader = FileSystemLoader.new("./templates")
var env = Environment.new(loader)
var template = env.getTemplate("page.html") // Loads ./templates/page.html</ code ></ pre >
< h3 > Methods</ h3 >
< div class = "method-signature" >
< span class = "method-name" > getSource</ span > (< span class = "param" > name</ span > ) → < span class = "type" > String</ span >
</ div >
< p > Reads and returns the template source from the filesystem.</ p >
< div class = "method-signature" >
< span class = "method-name" > exists</ span > (< span class = "param" > name</ span > ) → < span class = "type" > Bool</ span >
</ div >
< p > Checks if a template file exists.</ p >
< h2 id = "template-syntax" > Template Syntax</ h2 >
< p > The jinja module uses syntax compatible with Jinja2/Twig template engines.</ p >
< h3 id = "variables" > Variables</ h3 >
< p > Output variables using double curly braces:</ p >
< pre >< code > {{ variable }}
{{ user.name }}
{{ items[0] }}
{{ data["key"] }}</ code ></ pre >
< p > Variables are automatically HTML-escaped when < code > autoEscape</ code > is enabled.</ p >
< h4 > Attribute Access</ h4 >
< p > Access map values using dot notation or bracket notation:</ p >
< pre >< code > {{ user.name }} {# Same as user["name"] #}
{{ user["email"] }}
{{ items[0] }} {# List index access #}
{{ items[-1] }} {# Negative index: last item #}</ code ></ pre >
< h4 > Expressions</ h4 >
< p > Templates support arithmetic and comparison expressions:</ p >
< pre >< code > {{ a + b }}
{{ count * 2 }}
{{ total / items }}
{{ index % 2 }}
{{ "Hello " ~ name }} {# String concatenation #}</ code ></ pre >
< h4 > Conditional Expressions</ h4 >
< p > Inline if expressions (ternary-style):</ p >
< pre >< code > {{ "active" if is_active else "inactive" }}
{{ value if value else "default" }}</ code ></ pre >
< h3 id = "filters" > Filters</ h3 >
< p > Apply filters to transform values using the pipe operator:</ p >
< pre >< code > {{ name|upper }}
{{ text|truncate(100) }}
{{ items|join(", ") }}
{{ value|default("N/A") }}</ code ></ pre >
< p > Chain multiple filters:</ p >
< pre >< code > {{ name|lower|capitalize }}
{{ text|striptags|truncate(50) }}</ code ></ pre >
< p > Filter arguments can use parentheses or colon syntax:</ p >
< pre >< code > {{ items|join(", ") }}
{{ items|join:", " }} {# Alternative syntax #}</ code ></ pre >
< h3 id = "control-structures" > Control Structures</ h3 >
< h4 > If Statements</ h4 >
< pre >< code > {% if user %}
Hello, {{ user.name }}!
{% endif %}
{% if count > 10 %}
Many items
{% elif count > 0 %}
Some items
{% else %}
No items
{% endif %}</ code ></ pre >
< h4 > For Loops</ h4 >
< pre >< code > {% for item in items %}
{{ item }}
{% endfor %}
{% for key, value in mapping %}
{{ key }}: {{ value }}
{% endfor %}
{% for item in items %}
{{ item }}
{% else %}
No items found.
{% endfor %}</ code ></ pre >
< h4 > Set Variables</ h4 >
< pre >< code > {% set name = "Alice" %}
{% set items = [1, 2, 3] %}
{% set data = {"key": "value"} %}</ code ></ pre >
< h4 > Include Templates</ h4 >
< pre >< code > {% include "header.html" %}
{% include "sidebar.html" with context %}
{% include "footer.html" without context %}</ code ></ pre >
< h4 > Raw Blocks</ h4 >
< p > Output template syntax literally without processing:</ p >
< pre >< code > {% raw %}
This {{ will not }} be processed.
{% Neither will this %}
{% endraw %}</ code ></ pre >
< h4 > Filter Blocks</ h4 >
< p > Apply a filter to an entire block of content:</ p >
< pre >< code > {% filter upper %}
This text will be UPPERCASE.
{% endfilter %}
{% filter truncate(50) %}
This long text will be truncated to 50 characters...
{% endfilter %}</ code ></ pre >
2026-01-25 03:58:39 +00:00
< h3 id = "markdown-tags" > Markdown Tags</ h3 >
< p > Convert between Markdown and HTML within templates using block tags:</ p >
< h4 > markdowntohtml</ h4 >
< p > Converts Markdown content to HTML:</ p >
< pre >< code > {% markdowntohtml %}
# Heading
This is **bold** text.
{% endmarkdowntohtml %}</ code ></ pre >
< p > Variables are resolved before conversion:</ p >
< pre >< code > {% markdowntohtml %}
# {{ title }}
{{ description }}
{% endmarkdowntohtml %}</ code ></ pre >
< h4 > markdownfromhtml</ h4 >
< p > Converts HTML content back to Markdown:</ p >
< pre >< code > {% markdownfromhtml %}
< h1> Heading< /h1>
< p> This is < strong> bold< /strong> text.< /p>
{% endmarkdownfromhtml %}</ code ></ pre >
< p > Corresponding filters are also available for inline use:</ p >
< pre >< code > {{ markdown_text|markdown }}
{{ html_content|markdownfromhtml }}</ code ></ pre >
2026-01-24 22:40:22 +00:00
< h3 id = "comments" > Comments</ h3 >
< p > Comments are not included in the output:</ p >
< pre >< code > {# This is a comment #}
{#
Multi-line
comment
#}</ code ></ pre >
< h3 id = "whitespace-control" > Whitespace Control</ h3 >
< p > Use minus signs to strip whitespace before or after tags:</ p >
< pre >< code > {% for item in items -%}
{{ item }}
{%- endfor %}
{{- value -}} {# Strip whitespace on both sides #}</ code ></ pre >
< table >
< tr >< th > Syntax</ th >< th > Effect</ th ></ tr >
< tr >< td >< code > {%-</ code ></ td >< td > Strip whitespace before the tag</ td ></ tr >
< tr >< td >< code > -%}</ code ></ td >< td > Strip whitespace after the tag</ td ></ tr >
< tr >< td >< code > {{-</ code ></ td >< td > Strip whitespace before the variable</ td ></ tr >
< tr >< td >< code > -}}</ code ></ td >< td > Strip whitespace after the variable</ td ></ tr >
</ table >
< h3 id = "tests" > Tests</ h3 >
< p > Test values with the < code > is</ code > operator:</ p >
< pre >< code > {% if value is defined %}
Value exists: {{ value }}
{% endif %}
{% if num is even %}
Even number
{% endif %}
{% if value is not none %}
Has value
{% endif %}</ code ></ pre >
< h3 id = "macros" > Macros</ h3 >
< p > Define reusable template fragments:</ p >
< pre >< code > {% macro button(text, type="primary") %}
< button class="btn btn-{{ type }}"> {{ text }}< /button>
{% endmacro %}
{{ button("Click me") }}
{{ button("Delete", "danger") }}</ code ></ pre >
< p > Macros can have default parameter values and be called like functions.</ p >
< h3 id = "template-inheritance" > Template Inheritance</ h3 >
< p > Create base templates with blocks that child templates can override:</ p >
< h4 > Base Template (base.html)</ h4 >
< pre >< code > < !DOCTYPE html>
< html>
< head>
< title> {% block title %}Default Title{% endblock %}< /title>
< /head>
< body>
< header> {% block header %}{% endblock %}< /header>
< main> {% block content %}{% endblock %}< /main>
< footer> {% block footer %}& copy; 2024{% endblock %}< /footer>
< /body>
< /html> </ code ></ pre >
< h4 > Child Template (page.html)</ h4 >
< pre >< code > {% extends "base.html" %}
{% block title %}My Page{% endblock %}
{% block content %}
< h1> Welcome< /h1>
< p> This is my page content.< /p>
{% endblock %}</ code ></ pre >
< h4 > Using super()</ h4 >
< p > Call the parent block content using < code > super()</ code > :</ p >
< pre >< code > {% block footer %}
{{ super() }}
< p> Additional footer content< /p>
{% endblock %}</ code ></ pre >
< h2 id = "filters-reference" > Filters Reference</ h2 >
< h3 > String Filters</ h3 >
< table >
< tr >< th > Filter</ th >< th > Description</ th >< th > Example</ th ></ tr >
< tr >
< td >< code > upper</ code ></ td >
< td > Convert to uppercase</ td >
< td >< code > {{ "hello"|upper }}</ code > → HELLO</ td >
</ tr >
< tr >
< td >< code > lower</ code ></ td >
< td > Convert to lowercase</ td >
< td >< code > {{ "HELLO"|lower }}</ code > → hello</ td >
</ tr >
< tr >
< td >< code > capitalize</ code ></ td >
< td > Capitalize first letter, lowercase rest</ td >
< td >< code > {{ "hello WORLD"|capitalize }}</ code > → Hello world</ td >
</ tr >
< tr >
< td >< code > title</ code ></ td >
< td > Titlecase all words</ td >
< td >< code > {{ "hello world"|title }}</ code > → Hello World</ td >
</ tr >
< tr >
< td >< code > trim</ code ></ td >
< td > Remove leading/trailing whitespace</ td >
< td >< code > {{ " hello "|trim }}</ code > → hello</ td >
</ tr >
< tr >
< td >< code > striptags</ code ></ td >
< td > Remove HTML tags</ td >
< td >< code > {{ "< b> bold< /b> "|striptags }}</ code > → bold</ td >
</ tr >
< tr >
< td >< code > truncate(len, killwords, end)</ code ></ td >
< td > Truncate string to length</ td >
< td >< code > {{ text|truncate(20) }}</ code ></ td >
</ tr >
< tr >
< td >< code > replace(old, new)</ code ></ td >
< td > Replace substring</ td >
< td >< code > {{ "hello"|replace("l", "L") }}</ code > → heLLo</ td >
</ tr >
< tr >
< td >< code > center(width)</ code ></ td >
< td > Center string in width</ td >
< td >< code > {{ "hi"|center(10) }}</ code > → " hi "</ td >
</ tr >
< tr >
< td >< code > indent(width, first)</ code ></ td >
< td > Indent lines</ td >
< td >< code > {{ text|indent(4) }}</ code ></ td >
</ tr >
< tr >
< td >< code > wordcount</ code ></ td >
< td > Count words</ td >
< td >< code > {{ "hello world"|wordcount }}</ code > → 2</ td >
</ tr >
</ table >
< h3 > HTML Filters</ h3 >
< table >
< tr >< th > Filter</ th >< th > Description</ th >< th > Example</ th ></ tr >
< tr >
< td >< code > escape</ code > or < code > e</ code ></ td >
< td > HTML-escape special characters</ td >
< td >< code > {{ "< script> "|escape }}</ code > → & lt;script& gt;</ td >
</ tr >
< tr >
< td >< code > safe</ code ></ td >
< td > Mark as safe (no escaping)</ td >
< td >< code > {{ html_content|safe }}</ code ></ td >
</ tr >
< tr >
< td >< code > urlencode</ code ></ td >
< td > URL-encode string</ td >
< td >< code > {{ "hello world"|urlencode }}</ code > → hello+world</ td >
</ tr >
2026-01-25 03:58:39 +00:00
< tr >
< td >< code > markdown</ code ></ td >
< td > Convert Markdown to HTML</ td >
< td >< code > {{ md_text|markdown }}</ code ></ td >
</ tr >
< tr >
< td >< code > markdownfromhtml</ code ></ td >
< td > Convert HTML to Markdown</ td >
< td >< code > {{ html_content|markdownfromhtml }}</ code ></ td >
</ tr >
2026-01-24 22:40:22 +00:00
</ table >
< h3 > List/Collection Filters</ h3 >
< table >
< tr >< th > Filter</ th >< th > Description</ th >< th > Example</ th ></ tr >
< tr >
< td >< code > length</ code ></ td >
< td > Get length of string/list/map</ td >
< td >< code > {{ items|length }}</ code ></ td >
</ tr >
< tr >
< td >< code > first</ code ></ td >
< td > Get first element</ td >
< td >< code > {{ items|first }}</ code ></ td >
</ tr >
< tr >
< td >< code > last</ code ></ td >
< td > Get last element</ td >
< td >< code > {{ items|last }}</ code ></ td >
</ tr >
< tr >
< td >< code > reverse</ code ></ td >
< td > Reverse list or string</ td >
< td >< code > {{ items|reverse }}</ code ></ td >
</ tr >
< tr >
< td >< code > sort</ code ></ td >
< td > Sort list</ td >
< td >< code > {{ items|sort }}</ code ></ td >
</ tr >
< tr >
< td >< code > unique</ code ></ td >
< td > Remove duplicates</ td >
< td >< code > {{ [1,1,2,2]|unique }}</ code > → [1, 2]</ td >
</ tr >
< tr >
< td >< code > join(separator)</ code ></ td >
< td > Join list into string</ td >
< td >< code > {{ items|join(", ") }}</ code ></ td >
</ tr >
< tr >
< td >< code > batch(count, fill)</ code ></ td >
< td > Split into batches</ td >
< td >< code > {{ items|batch(3) }}</ code ></ td >
</ tr >
< tr >
< td >< code > map(attr)</ code ></ td >
< td > Extract attribute from each item</ td >
< td >< code > {{ users|map("name") }}</ code ></ td >
</ tr >
< tr >
< td >< code > sum</ code ></ td >
< td > Sum of numeric values</ td >
< td >< code > {{ [1, 2, 3]|sum }}</ code > → 6</ td >
</ tr >
< tr >
< td >< code > min</ code ></ td >
< td > Minimum value</ td >
< td >< code > {{ [3, 1, 2]|min }}</ code > → 1</ td >
</ tr >
< tr >
< td >< code > max</ code ></ td >
< td > Maximum value</ td >
< td >< code > {{ [3, 1, 2]|max }}</ code > → 3</ td >
</ tr >
</ table >
< h3 > Number Filters</ h3 >
< table >
< tr >< th > Filter</ th >< th > Description</ th >< th > Example</ th ></ tr >
< tr >
< td >< code > abs</ code ></ td >
< td > Absolute value</ td >
< td >< code > {{ -5|abs }}</ code > → 5</ td >
</ tr >
< tr >
< td >< code > round(precision)</ code ></ td >
< td > Round to precision</ td >
< td >< code > {{ 3.14159|round(2) }}</ code > → 3.14</ td >
</ tr >
< tr >
< td >< code > int</ code ></ td >
< td > Convert to integer</ td >
< td >< code > {{ "42"|int }}</ code > → 42</ td >
</ tr >
< tr >
< td >< code > float</ code ></ td >
< td > Convert to float</ td >
< td >< code > {{ "3.14"|float }}</ code > → 3.14</ td >
</ tr >
</ table >
< h3 > Type Conversion Filters</ h3 >
< table >
< tr >< th > Filter</ th >< th > Description</ th >< th > Example</ th ></ tr >
< tr >
< td >< code > string</ code ></ td >
< td > Convert to string</ td >
< td >< code > {{ 42|string }}</ code > → "42"</ td >
</ tr >
< tr >
< td >< code > list</ code ></ td >
< td > Convert to list</ td >
< td >< code > {{ "abc"|list }}</ code > → ["a", "b", "c"]</ td >
</ tr >
< tr >
< td >< code > tojson</ code ></ td >
< td > Convert to JSON string</ td >
< td >< code > {{ data|tojson }}</ code ></ td >
</ tr >
</ table >
< h3 > Default/Conditional Filters</ h3 >
< table >
< tr >< th > Filter</ th >< th > Description</ th >< th > Example</ th ></ tr >
< tr >
< td >< code > default(value)</ code > or < code > d(value)</ code ></ td >
< td > Default value if undefined/empty</ td >
< td >< code > {{ name|default("Anonymous") }}</ code ></ td >
</ tr >
</ table >
< h3 > Debug Filter</ h3 >
< table >
< tr >< th > Filter</ th >< th > Description</ th >< th > Example</ th ></ tr >
< tr >
< td >< code > debug</ code ></ td >
< td > Display type and value for debugging</ td >
< td >< code > {{ value|debug }}</ code > → String: "hello"</ td >
</ tr >
</ table >
< h2 id = "tests-reference" > Tests Reference</ h2 >
< p > Tests check values and return boolean results. Use with < code > is</ code > and < code > is not</ code > :</ p >
< table >
< tr >< th > Test</ th >< th > Description</ th >< th > Example</ th ></ tr >
< tr >
< td >< code > defined</ code ></ td >
< td > Value is not null</ td >
< td >< code > {% if var is defined %}</ code ></ td >
</ tr >
< tr >
< td >< code > undefined</ code ></ td >
< td > Value is null</ td >
< td >< code > {% if var is undefined %}</ code ></ td >
</ tr >
< tr >
< td >< code > none</ code ></ td >
< td > Value is null</ td >
< td >< code > {% if var is none %}</ code ></ td >
</ tr >
< tr >
< td >< code > even</ code ></ td >
< td > Number is even</ td >
< td >< code > {% if num is even %}</ code ></ td >
</ tr >
< tr >
< td >< code > odd</ code ></ td >
< td > Number is odd</ td >
< td >< code > {% if num is odd %}</ code ></ td >
</ tr >
< tr >
< td >< code > divisibleby(n)</ code ></ td >
< td > Number is divisible by n</ td >
< td >< code > {% if num is divisibleby 3 %}</ code ></ td >
</ tr >
< tr >
< td >< code > iterable</ code ></ td >
< td > Value is list, map, or string</ td >
< td >< code > {% if items is iterable %}</ code ></ td >
</ tr >
< tr >
< td >< code > mapping</ code ></ td >
< td > Value is a map</ td >
< td >< code > {% if data is mapping %}</ code ></ td >
</ tr >
< tr >
< td >< code > sequence</ code ></ td >
< td > Value is a list</ td >
< td >< code > {% if items is sequence %}</ code ></ td >
</ tr >
< tr >
< td >< code > string</ code ></ td >
< td > Value is a string</ td >
< td >< code > {% if name is string %}</ code ></ td >
</ tr >
< tr >
< td >< code > number</ code ></ td >
< td > Value is a number</ td >
< td >< code > {% if count is number %}</ code ></ td >
</ tr >
< tr >
< td >< code > upper</ code ></ td >
< td > String is all uppercase</ td >
< td >< code > {% if text is upper %}</ code ></ td >
</ tr >
< tr >
< td >< code > lower</ code ></ td >
< td > String is all lowercase</ td >
< td >< code > {% if text is lower %}</ code ></ td >
</ tr >
</ table >
< h2 id = "built-in-functions" > Built-in Functions</ h2 >
< table >
< tr >< th > Function</ th >< th > Description</ th >< th > Example</ th ></ tr >
< tr >
< td >< code > range(n)</ code ></ td >
< td > Generate list [0, 1, ..., n-1]</ td >
< td >< code > {% for i in range(5) %}</ code ></ td >
</ tr >
< tr >
< td >< code > range(start, end)</ code ></ td >
< td > Generate list [start, ..., end-1]</ td >
< td >< code > {% for i in range(1, 10) %}</ code ></ td >
</ tr >
< tr >
< td >< code > range(start, end, step)</ code ></ td >
< td > Generate with step</ td >
< td >< code > {% for i in range(0, 10, 2) %}</ code ></ td >
</ tr >
< tr >
< td >< code > dict()</ code ></ td >
< td > Create empty map</ td >
< td >< code > {% set data = dict() %}</ code ></ td >
</ tr >
< tr >
< td >< code > super()</ code ></ td >
< td > Call parent block content</ td >
< td >< code > {{ super() }}</ code ></ td >
</ tr >
</ table >
< h2 id = "loop-variable" > Loop Variable</ h2 >
< p > Inside < code > for</ code > loops, a special < code > loop</ code > variable is available:</ p >
< table >
< tr >< th > Property</ th >< th > Description</ th ></ tr >
< tr >
< td >< code > loop.index</ code ></ td >
< td > Current iteration (1-indexed)</ td >
</ tr >
< tr >
< td >< code > loop.index0</ code ></ td >
< td > Current iteration (0-indexed)</ td >
</ tr >
< tr >
< td >< code > loop.first</ code ></ td >
< td > True if first iteration</ td >
</ tr >
< tr >
< td >< code > loop.last</ code ></ td >
< td > True if last iteration</ td >
</ tr >
< tr >
< td >< code > loop.length</ code ></ td >
< td > Total number of items</ td >
</ tr >
< tr >
< td >< code > loop.revindex</ code ></ td >
< td > Iterations remaining (1-indexed)</ td >
</ tr >
< tr >
< td >< code > loop.revindex0</ code ></ td >
< td > Iterations remaining (0-indexed)</ td >
</ tr >
</ table >
< pre >< code > {% for item in items %}
{% if loop.first %}< ul> {% endif %}
< li class="{% if loop.last %}last{% endif %}">
{{ loop.index }}. {{ item }}
< /li>
{% if loop.last %}< /ul> {% endif %}
{% endfor %}</ code ></ pre >
< h2 id = "examples" > Examples</ h2 >
< h3 > Basic Template Rendering</ h3 >
< pre >< code > import "jinja" for Environment, DictLoader
var loader = DictLoader.new({
"greeting.html": "Hello, {{ name }}! You have {{ count }} messages."
})
var env = Environment.new(loader)
var template = env.getTemplate("greeting.html")
var html = template.render({
"name": "Alice",
"count": 5
})
System.print(html) // Hello, Alice! You have 5 messages.</ code ></ pre >
< h3 > Using Filters</ h3 >
< pre >< code > import "jinja" for Environment, DictLoader
var loader = DictLoader.new({
"filters.html": """
Name: {{ name|upper }}
Items: {{ items|join(", ") }}
Total: {{ prices|sum|round(2) }}
Default: {{ missing|default("N/A") }}
"""
})
var env = Environment.new(loader)
var template = env.getTemplate("filters.html")
System.print(template.render({
"name": "alice",
"items": ["apple", "banana", "cherry"],
"prices": [1.5, 2.75, 3.25]
}))</ code ></ pre >
< h3 > Conditional Rendering</ h3 >
< pre >< code > import "jinja" for Environment, DictLoader
var loader = DictLoader.new({
"status.html": """
{% if user %}
{% if user.admin %}
< span class="admin"> Admin: {{ user.name }}< /span>
{% else %}
< span> User: {{ user.name }}< /span>
{% endif %}
{% else %}
< span> Guest< /span>
{% endif %}
"""
})
var env = Environment.new(loader)
var template = env.getTemplate("status.html")
System.print(template.render({
"user": {"name": "Alice", "admin": true}
}))</ code ></ pre >
< h3 > Looping with Loop Variable</ h3 >
< pre >< code > import "jinja" for Environment, DictLoader
var loader = DictLoader.new({
"list.html": """
< table>
{% for user in users %}
< tr class="{{ 'even' if loop.index is even else 'odd' }}">
< td> {{ loop.index }}< /td>
< td> {{ user.name }}< /td>
< td> {{ user.email }}< /td>
< /tr>
{% else %}
< tr>< td colspan="3"> No users found< /td>< /tr>
{% endfor %}
< /table>
"""
})
var env = Environment.new(loader)
var template = env.getTemplate("list.html")
System.print(template.render({
"users": [
{"name": "Alice", "email": "alice@example.com"},
{"name": "Bob", "email": "bob@example.com"}
]
}))</ code ></ pre >
< h3 > Template Inheritance</ h3 >
< pre >< code > import "jinja" for Environment, DictLoader
var loader = DictLoader.new({
"base.html": """
< !DOCTYPE html>
< html>
< head>< title> {% block title %}Site{% endblock %}< /title>< /head>
< body>
< nav> {% block nav %}Home | About{% endblock %}< /nav>
< main> {% block content %}{% endblock %}< /main>
< footer> {% block footer %}Copyright 2024{% endblock %}< /footer>
< /body>
< /html>
""",
"page.html": """
{% extends "base.html" %}
{% block title %}My Page - {{ super() }}{% endblock %}
{% block content %}
< h1> {{ title }}< /h1>
< p> {{ content }}< /p>
{% endblock %}
"""
})
var env = Environment.new(loader)
var template = env.getTemplate("page.html")
System.print(template.render({
"title": "Welcome",
"content": "This is the page content."
}))</ code ></ pre >
< h3 > Macros for Reusable Components</ h3 >
< pre >< code > import "jinja" for Environment, DictLoader
var loader = DictLoader.new({
"components.html": """
{% macro input(name, value="", type="text") %}
< input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
{% macro button(text, style="primary") %}
< button class="btn-{{ style }}"> {{ text }}< /button>
{% endmacro %}
< form>
{{ input("username") }}
{{ input("password", "", "password") }}
{{ input("email", user.email) }}
{{ button("Submit") }}
{{ button("Cancel", "secondary") }}
< /form>
"""
})
var env = Environment.new(loader)
var template = env.getTemplate("components.html")
System.print(template.render({
"user": {"email": "alice@example.com"}
}))</ code ></ pre >
< h3 > Loading Templates from Filesystem</ h3 >
< pre >< code > import "jinja" for Environment, FileSystemLoader
var loader = FileSystemLoader.new("./templates")
var env = Environment.new(loader)
var template = env.getTemplate("email.html")
var html = template.render({
"recipient": "Alice",
"subject": "Welcome",
"body": "Thank you for signing up!"
})</ code ></ pre >
< h3 > Iterating Over Maps</ h3 >
< pre >< code > import "jinja" for Environment, DictLoader
var loader = DictLoader.new({
"map.html": """
< dl>
{% for key, value in data %}
< dt> {{ key }}< /dt>
< dd> {{ value }}< /dd>
{% endfor %}
< /dl>
"""
})
var env = Environment.new(loader)
var template = env.getTemplate("map.html")
System.print(template.render({
"data": {
"name": "Alice",
"age": 30,
"city": "Boston"
}
}))</ code ></ pre >
< h3 > HTML Escaping</ h3 >
< pre >< code > import "jinja" for Environment, DictLoader
var loader = DictLoader.new({
"escape.html": """
Auto-escaped: {{ user_input }}
Raw HTML: {{ html_content|safe }}
Manual escape: {{ data|escape }}
"""
})
var env = Environment.new(loader)
var template = env.getTemplate("escape.html")
System.print(template.render({
"user_input": "< script> alert('xss')< /script> ",
"html_content": "< strong> Bold< /strong> ",
"data": "A < B & C > D"
}))</ code ></ pre >
< div class = "admonition note" >
< div class = "admonition-title" > Note</ div >
< p > The jinja module implements a subset of Jinja2 functionality. Some advanced features like custom filters, extensions, and async support are not available.</ p >
</ div >
< div class = "admonition tip" >
< div class = "admonition-title" > Tip</ div >
< p > Use < code > DictLoader</ code > for embedded templates and < code > FileSystemLoader</ code > for file-based templates. The environment caches compiled templates, so repeated calls to < code > getTemplate</ code > are efficient.</ p >
</ div >
< div class = "admonition warning" >
< div class = "admonition-title" > Warning</ div >
< p > When auto-escaping is disabled, be careful with user-provided content to prevent XSS vulnerabilities. Use the < code > |escape</ code > filter explicitly when needed.</ p >
</ div >
</ article >
< footer class = "page-footer" >
< a href = "regex.html" class = "prev" > regex</ a >
< a href = "crypto.html" class = "next" > crypto</ a >
</ footer >
</ main >
</ div >
< script src = "../js/main.js" ></ script >
</ body >
</ html >