# swift-jinja
**Repository Path**: whisperkit/swift-jinja
## Basic Information
- **Project Name**: swift-jinja
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-03-06
- **Last Updated**: 2026-03-06
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Jinja
A Swift implementation of the
[Jinja2 template engine](https://jinja.palletsprojects.com/en/3.1.x/).
Jinja templates are widely used for generating HTML, configuration files, code generation, and text processing.
This implementation is focused primarily on the features needed to generate LLM chat templates.
## Requirements
- Swift 6.0+ / Xcode 16+
## Installation
### Swift Package Manager
Add the following to your `Package.swift` file:
```swift
dependencies: [
.package(url: "https://github.com/huggingface/swift-jinja.git", from: "2.0.0")
]
```
Then add the dependency to your target:
```swift
.target(
name: "YourTarget",
dependencies: [
.product(name: "Jinja", package: "swift-jinja")
]
)
```
## Features
This package implements a subset of the functionality of the
[official Python implementation](https://jinja.palletsprojects.com/en/stable/templates/).
### Supported Features ✅
- **Variables**:
`{{ variable }}`, `{{ object.attribute }}`, `{{ dict['key'] }}`
- **Comments**:
`{# comment #}`
- **Statements**:
`{% statement %}`
- **Value Types**:
Boolean (`true`, `false`),
integers (`42`),
floats (`3.14`),
strings (`"hello"`),
arrays (`[1, 2, 3]`),
objects (`{"key": "value"}`), and
null (`null`)
- **Arithmetic Operators**:
`+`, `-`, `*`, `/`,
`//` (floor division),
`**` (exponentiation),
`%`
- **String Concatenation Operators**:
`~`
and automatic concatenation of adjacent string literals
- **Comparison Operators**:
`==`, `!=`, `<`, `<=`, `>`, `>=`
- **Logical Operators**:
`and`, `or`, `not`
- **Membership Operator**:
`in`
- **Attribute Access**:
`.` and `[]`
- **Conditionals**:
`{% if %}`, `{% elif %}`, `{% else %}`, `{% endif %}`
- **Loops**:
`{% for item in list %}...{% endfor %}`
- **Loop Variables**:
`loop.index`, `loop.index0`, `loop.first`, `loop.last`, `loop.length`
- **Loop Filtering**:
`{% for item in list if condition %}`
- **Loop Controls**:
`{% break %}`, `{% continue %}`
- **Variable Assignment**:
`{% set variable = value %}`
- **Macros**:
`{% macro name() %}...{% endmacro %}`
- **Macro Calls**:
`{% call macro_name() %}`
- **Filter Statements**:
`{{ name | upper }}`
- **Filter Blocks**:
`{% filter upper %}...{% endfilter %}`
- **Generation Blocks**:
`{% generation %}...{% endgeneration %}`
(Hugging Face extension for marking assistant-generated content)
- **Tests**:
`is` operator for type/value checks (e.g. `{% if value is number %}`)
- **Global Functions**:
`range()`, `lipsum()`, `dict()`, `cycler()`, `joiner()`, `namespace()`, `strftime_now()`
- **Exception Handling**:
`raise_exception()` (throws `Exception` error)
Supported Filters
- [x] `abs()`
- [x] `attr()`
- [x] `batch()`
- [x] `capitalize()`
- [x] `center()`
- [x] `default()`
- [x] `dictsort()`
- [x] `escape()`
- [x] `filesizeformat()`
- [x] `first()`
- [x] `float()`
- [x] `forceescape()`
- [x] `format()`
- [x] `groupby()`
- [x] `indent()`
- [x] `int()`
- [x] `items()`
- [x] `join()`
- [x] `last()`
- [x] `length()`
- [x] `list()`
- [x] `lower()`
- [x] `map()`
- [x] `max()`
- [x] `min()`
- [x] `pprint()`
- [x] `random()`
- [x] `reject()`
- [x] `rejectattr()`
- [x] `replace()`
- [x] `reverse()`
- [x] `round()`
- [x] `safe()`
- [x] `select()`
- [x] `selectattr()`
- [x] `slice()`
- [x] `sort()`
- [x] `string()`
- [x] `striptags()`
- [x] `sum()`
- [x] `title()`
- [x] `tojson()`
- [x] `trim()`
- [x] `truncate()`
- [x] `unique()`
- [x] `upper()`
- [x] `urlencode()`
- [x] `urlize()`
- [x] `wordcount()`
- [x] `wordwrap()`
- [x] `xmlattr()`
Supported tests
- [x] `boolean()`
- [x] `callable()`
- [x] `defined()`
- [x] `divisibleby()`
- [x] `eq()`
- [x] `escaped()`
- [x] `even()`
- [x] `false()`
- [x] `filter()`
- [x] `float()`
- [x] `ge()`
- [x] `gt()`
- [x] `in()`
- [x] `integer()`
- [x] `iterable()`
- [x] `le()`
- [x] `lower()`
- [x] `lt()`
- [x] `mapping()`
- [x] `ne()`
- [x] `none()`
- [x] `number()`
- [x] `odd()`
- [x] `sameas()`
- [x] `sequence()`
- [x] `string()`
- [x] `test()`
- [x] `true()`
- [x] `undefined()`
- [x] `upper()`
### Not Supported Features ❌
- **Template Inheritance**:
`{% extends %}` and `{% block %}`
- **Template Includes**:
`{% include %}`
- **Template Imports**:
`{% import %}`, `{% from ... import %}`
- **Block Inheritance**:
`super()`, block scoping, required blocks
- **With Statement**:
`{% with %}` for variable scoping
- **Raw Blocks**:
`{% raw %}...{% endraw %}`
- **Internationalization**:
`{% trans %}`, `{% pluralize %}`, `i18n` extension
- **Debug Statement**:
`{% debug %}`
- **Do Statement**:
`{% do expression %}` (expression without output)
- **Autoescape**:
`{% autoescape %}`
blocks and automatic HTML escaping
- **Line Statements**:
Alternative syntax with prefix characters (`# for item in seq` instead of `{% for item in seq %}`)
- **Extensions**:
Custom extensions that add extra functionality at the parser level are not supported.
If you need features beyond custom tests, filters, or macros,
fork the package and implement changes directly.
## Usage
### Basic Template Rendering
```swift
import Jinja
// Create and render a simple template
let template = try Template("Hello, {{ name }}!")
let result = try template.render(["name": "World"])
print(result) // "Hello, World!"
```
### Template with Context Variables
```swift
// Template with multiple variables
let template = try Template("""
Welcome, {{ user.name }}!
You have {{ messages | length }} new messages.
""")
let context: [String: Value] = [
"user": ["name": "Alice"],
"messages": [
"Hello",
"How are you?",
"See you later"
]
]
let result = try template.render(context)
// "Welcome, Alice!\nYou have 3 new messages."
```
> [!IMPORTANT]
> **Migrating to Jinja v2.0**:
> Most code using Jinja v1 should work with v2 with minimal changes.
> The biggest breaking change is that the context parameter for rendering templates
> has changed from `[String: Any]` to `[String: Value]`.
>
> Thanks to `Value` being expressible by literals,
> existing code may work as-is.
> You can also try the `Value(any:)` constructor
> to automatically convert complex values:
>
> ```swift
> // Create template and context
> let template = try Template("Hello {{ user.name }}!")
>
> var context: [String: Value] = [
> // Use literals:
> "user": ["name": "Alice"],
> ]
>
> // ...or convert from Any value:
> let settings: [String: Any] = ["theme": "dark", "notifications": true]
> context["settings"] = try Value(any: settings)
>
> let result = try template.render(context)
> // "Hello Alice!"
> ```
### Control Flow
```swift
// Conditional rendering
let template = try Template("""
{% for item in items %}
{% if item.active %}
* {{ item.name }} ({{ item.price }})
{% endif %}
{% endfor %}
""")
let context: [String: Value] = [
"items": [
[
"name": "Coffee",
"price": 4.50,
"active": true
],
[
"name": "Tea",
"price": 3.25,
"active": false
]
]
]
let result = try template.render(context)
// " * Coffee (4.5)\n"
```
### Built-in Filters
Templates support Jinja
[built-in filters](https://jinja.palletsprojects.com/en/stable/templates/#jinja-filters)
for data transformation and manipulation.
```swift
// String manipulation filters
let template = try Template("""
{{ name | upper }}
{{ description | truncate(50) }}
{{ tags | join(", ") }}
""")
let context: [String: Value] = [
"name": "swift package",
"description": "A powerful template engine for Swift applications",
"tags": ["swift", "templates", "web"]
]
let result = try template.render(context)
```
### Tests
Jinja provides
[built-in tests](<(https://jinja.palletsprojects.com/en/stable/templates/#list-of-builtin-tests)>)
for conditional logic and type checking.
Tests are used with the `is` operator to evaluate conditions in templates.
These tests help you make decisions based on the
type, value, or properties of variables.
```swift
// Type and value checking with tests
let template = try Template("""
{% if user is defined %}
Welcome, {{ user.name }}!
{% else %}
Please log in.
{% endif %}
{% if messages is iterable and messages | length > 0 %}
You have {{ messages | length }} messages.
{% endif %}
{% if age is number and age >= 18 %}
You are an adult.
{% elif age is number and age < 18 %}
You are a minor.
{% endif %}
{% if status is none %}
Status not set.
{% elif status is true %}
Active
{% elif status is false %}
Inactive
{% endif %}
""")
let context: [String: Value] = [
"user": ["name": "Alice"],
"messages": ["Hello", "How are you?"],
"age": 25,
"status": true
]
let result = try template.render(context)
// "Welcome, Alice!\nYou have 2 messages.\nYou are an adult.\nActive"
```
### Template Options
```swift
// Configure template behavior
let options = Template.Options(
lstripBlocks: true, // Strip leading whitespace from blocks
trimBlocks: true // Remove trailing newlines from blocks
)
let template = try Template("""
{% for item in items %}
{{ item }}
{% endfor %}
""", with: options)
```
### Value Types
The `Value` enum represents all possible template values:
```swift
// Creating values directly
let context: [String: Value] = [
"text": "Hello",
"number": 42,
"decimal": 3.14,
"flag": true,
"items": ["a", "b"],
"user": ["name": "John", "age": 30],
"missing": .null
]
// ...or from Swift types
let swiftValue: Any? = ["name": "John", "items": [1, 2, 3]]
let jinjaValue = try Value(any: swiftValue)
```
## Examples
### HTML Generation
```swift
import Jinja
// Generate HTML from template
let htmlTemplate = try Template("""
{{ page.title }}
{{ page.heading }}
""")
let context: [String: Value] = [
"page": [
"title": "My Website",
"heading": "Welcome",
"items": .array([
["title": "Home", "url": "/"),
["title": "About", "url": "/about"],
["title": "Contact", "url": "/contact"]
]
]
]
let html = try htmlTemplate.render(context)
```
### Configuration File Generation
```swift
// Generate configuration files
let configTemplate = try Template("""
# {{ app.name }} Configuration
[server]
host = "{{ server.host }}"
port = {{ server.port }}
debug = {{ server.debug | lower }}
[database]
{% for db in databases %}
[database.{{ db.name }}]
url = "{{ db.url }}"
pool_size = {{ db.pool_size }}
{% endfor %}
""")
let context: [String: Value] = [
"app": ["name": "MyApp"],
"server": [
"host": "localhost",
"port": 8080,
"debug": true
],
"databases": [
[
"name": "primary",
"url": "postgresql://localhost/myapp",
"pool_size": 10
]
]
]
let config = try configTemplate.render(context)
```
### Chat Message Formatting
```swift
// Format chat messages (useful for AI/LLM applications)
let chatTemplate = try Template("""
{% for message in messages %}
{% if message.role == "system" %}
System: {{ message.content }}
{% elif message.role == "user" %}
User: {{ message.content }}
{% elif message.role == "assistant" %}
Assistant: {{ message.content }}
{% endif %}
{% endfor %}
""", with: Template.Options(lstripBlocks: true, trimBlocks: true))
let messages: [String: Value] = [
"messages": [
[
"role": "system",
"content": "You are a helpful assistant."
],
[
"role": "user",
"content": "What's the weather like?"
],
[
"role": "assistant",
"content": "I'd be happy to help with weather information!"
]
]
]
let formatted = try chatTemplate.render(messages)
```
## Contributing
This is a community project and we welcome contributions.
Please check out
[Issues tagged with `good first issue`][good-first-issues]
if you are looking for a place to start!
Please ensure your code passes the build and test suite
before submitting a pull request.
You can run the tests with `swift test`.
[good-first-issues]: https://github.com/huggingface/swift-jinja/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22
## License
[Apache 2](LICENSE).