Skip to content

jedt3d/JinjaX

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JinjaX - Jinja Template Engine for Xojo

A complete implementation of the Jinja template engine in Xojo, providing powerful template rendering capabilities for Xojo applications.

Features

  • Full Jinja Compatibility: Lexer, parser, and renderer implementing Jinja specification
  • Template Inheritance: extends block support for template hierarchies
  • Template Includes: include statement with context passing
  • Macros: Define and call reusable template macros
  • Control Flow: if/elif/else conditionals and for loops 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

Quick Start

Basic Template Rendering

// 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!"

Loading Templates from Files

// 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)

Template Inheritance

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 %}

Using Filters

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"

Control Flow

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 %}

Architecture

Core Components

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

Template Loaders

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)

AST Nodes

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...

Project Structure

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

Testing

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.

Built-in Filters

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

Exception Handling

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 try

MarkupSafe Integration

HTML 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: "&lt;script&gt;alert('XSS')&lt;/script&gt;"

// Safe content is not escaped
env.fromString("{{ safe }}").render(context)
// Output: "<b>Bold</b>"

Development

Building from Source

  1. Open JinjaX/JinjaX.xojo_project in Xojo IDE
  2. Build the project or run the desktop test application
  3. Use the test application to run the 55 template examples

Running Tests

  1. Launch the JinjaX desktop application
  2. Navigate to the Tests section
  3. Run individual test groups or the full suite
  4. View results in the test output window

Adding Custom Filters

// 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)

Version History

v1.0.1 (Documentation Update)

  • Replace Jinja2 with Jinja naming throughout
  • Update specification references
  • No functional changes

v1.0 (Initial Release)

  • 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

License

JinjaX is provided as-is for use in Xojo applications.

References

Contributing

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

About

JinjaX, a Python Jinja Template port to Xojo. It work with any Xojo project types.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors