A complete implementation of the Jinja template engine in Xojo, providing powerful template rendering capabilities for Xojo applications.
- Full Jinja Compatibility: Lexer, parser, and renderer implementing Jinja specification
- Template Inheritance:
extendsblock support for template hierarchies - Template Includes:
includestatement with context passing - Macros: Define and call reusable template macros
- Control Flow:
if/elif/elseconditionals andforloops with full iteration support - Variable Filters: 10+ built-in filters (upper, lower, title, capitalize, trim, length, default, replace, first, last)
- HTML Escaping: Automatic HTML entity escaping with MarkupSafe integration
- Error Handling: Comprehensive exception types for syntax and runtime errors
- Multiple Loaders: DictLoader for templates in memory, FileSystemLoader for disk-based templates
- Comprehensive Testing: 55 template examples covering all features
// Create an environment
var env = new JinjaEnvironment()
// Create a context with variables
var context = new JinjaContext()
context.set("name", "World")
// Render a template
var result = env.fromString("Hello {{ name }}!").render(context)
// Output: "Hello World!"// Create a file system loader
var loader = new FileSystemLoader("/path/to/templates")
var env = new JinjaEnvironment(loader)
// Get and render a template
var template = env.getTemplate("mytemplate.html")
var context = new JinjaContext()
context.set("items", array("apple", "banana", "cherry"))
var result = template.render(context)base.html:
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Default Title{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>child.html:
{% extends "base.html" %}
{% block title %}My Page{% endblock %}
{% block content %}
<h1>Welcome!</h1>
<p>This is the child template content.</p>
{% endblock %}var context = new JinjaContext()
context.set("text", "hello world")
var result = env.fromString("{{ text | upper }}").render(context)
// Output: "HELLO WORLD"
result = env.fromString("{{ text | title }}").render(context)
// Output: "Hello World"
result = env.fromString("{{ items | length }}").render(context)
// Output: "3"For Loops:
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}Conditionals:
{% if user.is_admin %}
<p>Welcome, admin!</p>
{% elif user.is_moderator %}
<p>Welcome, moderator!</p>
{% else %}
<p>Welcome, user!</p>
{% endif %}JinjaEnvironment - Main entry point for template compilation and rendering
- Manages template loading
- Caches compiled templates
- Provides filter and function registration
JinjaLexer - Tokenizes template source code
- Identifies variables, tags, filters, and text nodes
- Handles delimiters and whitespace
JinjaParser - Builds Abstract Syntax Tree (AST)
- Converts tokens to AST nodes
- Validates syntax and structure
JinjaRenderer - Executes the AST
- Evaluates expressions and variables
- Applies filters and functions
- Manages template context
JinjaContext - Runtime variable scope
- Stores template variables
- Manages variable resolution
- Supports nested scopes for loops and includes
FileSystemLoader - Load templates from the file system
var loader = new FileSystemLoader(basePath)DictLoader - Load templates from a dictionary
var templates = new Dictionary()
templates.Value("template1") = "Hello {{ name }}"
var loader = new DictLoader(templates)The parser creates a hierarchy of AST nodes representing the template structure:
- TemplateNode - Root node
- TextNode - Plain text content
- OutputNode -
{{ expression }} - IfNode -
{% if %}...{% endif %} - ForNode -
{% for %}...{% endfor %} - BlockNode -
{% block %}...{% endblock %} - ExtendsNode -
{% extends %} - IncludeNode -
{% include %} - MacroNode -
{% macro %}...{% endmacro %} - SetNode -
{% set %} - VariableNode - Variable references
- FilterNode - Filter applications
- And many more...
JinjaX_Implementation/
├── JinjaX/ # Xojo desktop application
│ ├── JinjaXLib/ # Core library
│ │ ├── JinjaX/ # Main engine classes
│ │ │ ├── JinjaEnvironment.xojo_code
│ │ │ ├── JinjaLexer.xojo_code
│ │ │ ├── JinjaParser.xojo_code
│ │ │ ├── JinjaRenderer.xojo_code
│ │ │ ├── JinjaContext.xojo_code
│ │ │ ├── *Node.xojo_code # AST nodes
│ │ │ └── ...
│ │ └── MarkupSafe/ # HTML escaping
│ ├── AppSrc/ # Desktop test application
│ │ ├── MainTestWindow.xojo_window
│ │ └── Tests/ # Test suite
│ ├── templates/ # 55 test templates
│ └── JinjaX.xojo_project
├── Shared Resources/
│ └── XojoUnit/ # Unit testing framework
├── development docs/
│ ├── spec_jinjax/ # JinjaX specifications
│ └── spec_markupsafe/ # MarkupSafe specifications
└── README.md
The project includes a comprehensive test suite with 55 template examples covering:
- Basic Variables: Simple variable substitution
- Filters: All built-in filter operations
- Control Flow: if/elif/else, for loops with edge cases
- Template Inheritance: Multi-level extends with block overrides
- Template Includes: Include with and without context
- Macros: Macro definition and invocation
- HTML Escaping: Safe markup handling and auto-escaping
- Edge Cases: Missing variables, nested access, complex expressions
Run the test suite from the Xojo desktop application's main window.
| Filter | Description | Example |
|---|---|---|
upper |
Convert to uppercase | {{ "hello" | upper }} → "HELLO" |
lower |
Convert to lowercase | {{ "Hello" | lower }} → "hello" |
title |
Title case | {{ "hello world" | title }} → "Hello World" |
capitalize |
Capitalize first letter | {{ "hello" | capitalize }} → "Hello" |
trim |
Remove whitespace | {{ " hello " | trim }} → "hello" |
length |
Get length | {{ items | length }} → 3 |
default |
Default value | {{ missing | default("N/A") }} → "N/A" |
replace |
String replacement | {{ "hello" | replace("l", "r") }} → "herro" |
first |
Get first item | {{ items | first }} → apple |
last |
Get last item | {{ items | last }} → cherry |
The engine provides specific exception types for error handling:
try
var template = env.getTemplate("template.html")
var result = template.render(context)
catch e as TemplateSyntaxException
// Syntax error in template
System.DebugLog("Syntax error: " + e.Message)
catch e as UndefinedVariableException
// Variable not found in context
System.DebugLog("Undefined variable: " + e.Message)
catch e as TemplateRenderException
// Runtime error during rendering
System.DebugLog("Render error: " + e.Message)
catch e as TemplateException
// General template error
System.DebugLog("Template error: " + e.Message)
end tryHTML content is automatically escaped unless marked as safe:
var context = new JinjaContext()
context.set("unsafe", "<script>alert('XSS')</script>")
context.set("safe", new MarkupString("<b>Bold</b>"))
// Unsafe content is escaped
env.fromString("{{ unsafe }}").render(context)
// Output: "<script>alert('XSS')</script>"
// Safe content is not escaped
env.fromString("{{ safe }}").render(context)
// Output: "<b>Bold</b>"- Open
JinjaX/JinjaX.xojo_projectin Xojo IDE - Build the project or run the desktop test application
- Use the test application to run the 55 template examples
- Launch the JinjaX desktop application
- Navigate to the Tests section
- Run individual test groups or the full suite
- View results in the test output window
// Define a custom filter function
function myFilter(value as String) as String
return value.ReverseString
end function
// Register it with the environment
var env = new JinjaEnvironment()
env.registerFilter("reverse", AddressOf myFilter)
// Use in templates
env.fromString("{{ text | reverse }}").render(context)- Replace Jinja2 with Jinja naming throughout
- Update specification references
- No functional changes
- Complete Jinja template engine implementation
- All core features: inheritance, includes, filters, control flow
- 40+ AST node types, 30+ classes
- Comprehensive test suite with 55 templates
- Production-ready error handling
JinjaX is provided as-is for use in Xojo applications.
- Jinja Documentation: jinja.palletsprojects.com
- MarkupSafe: markupsafe.palletsprojects.com
- Xojo: xojo.com
For bug reports, feature requests, or contributions, please refer to the development documentation in the development docs/ directory.
Current Version: v1.0.1 Status: Stable Last Updated: 2026-03-17