Replace custom template syntax with Jinja2 #88

Closed
opened 2026-06-16 23:26:35 +00:00 by marvin8 · 2 comments
marvin8 commented 2026-06-16 23:26:35 +00:00 (Migrated from codeberg.org)

The template system has grown organically — first basic {var} substitution, then conditional prefix/suffix syntax ({[prefix]var[suffix]}, #69), and now a fallback syntax is being requested (#87). Each addition requires extending a hand-rolled regex parser, and the direction of travel is clear: we're building a template language one issue at a time.

Rather than continue patching the custom syntax, this issue proposes switching to Jinja2 as the template engine. Jinja2 is mature, well-documented, and already solves everything users have asked for — and everything they're likely to ask for next:

  • Variable substitution: {{ title }}
  • Fallback to another variable: {{ description | default(title) }}
  • Conditional text: {% if author %} by {{ author }}{% endif %}
  • String manipulation: {{ title | truncate(100) }}
  • Combining multiple conditions and filters in a single expression

This is a breaking change: existing {var} templates will need to be updated to {{ var }} syntax. The plan is to ship this with a major version bump and provide a migration guide in the docs.

Issue #87 (fallback syntax) will serve as the concrete use case to implement and validate against during development.

The template system has grown organically — first basic `{var}` substitution, then conditional prefix/suffix syntax (`{[prefix]var[suffix]}`, #69), and now a fallback syntax is being requested (#87). Each addition requires extending a hand-rolled regex parser, and the direction of travel is clear: we're building a template language one issue at a time. Rather than continue patching the custom syntax, this issue proposes switching to [Jinja2](https://jinja.palletsprojects.com/) as the template engine. Jinja2 is mature, well-documented, and already solves everything users have asked for — and everything they're likely to ask for next: - Variable substitution: `{{ title }}` - Fallback to another variable: `{{ description | default(title) }}` - Conditional text: `{% if author %} by {{ author }}{% endif %}` - String manipulation: `{{ title | truncate(100) }}` - Combining multiple conditions and filters in a single expression This is a **breaking change**: existing `{var}` templates will need to be updated to `{{ var }}` syntax. The plan is to ship this with a major version bump and provide a migration guide in the docs. Issue #87 (fallback syntax) will serve as the concrete use case to implement and validate against during development.
coding-agent-marvin8 commented 2026-06-18 08:07:32 +00:00 (Migrated from codeberg.org)

Findings and plan of attack

Explored the current template system in full. Summary of what changes:

Current system (to be removed)

  • _format_conditional_template() in publish.py — regex parser handling {[prefix]var[suffix]} patterns
  • str.format(**params) fallback for standard {var} substitution
  • \\n preprocessing hack (post_template.replace("\\\\n", "\n")) because JSON strings can't express newlines naturally
  • bot_post_template (inline string) on Configuration; post_template (inline string) on FeedInfo

New system

  • Jinja2 as the template engine — handles variable substitution, conditionals, filters (including default() for issue #87's fallback use case), and anything else users might need
  • External .j2 files only — no inline template strings in config. This lets users write natural multiline templates without the \\n hack
  • New config fields: bot_post_template_file (bot level), post_template_file (feed level) — paths resolved relative to the config file's location
  • Templates loaded at config-load time; rest of the code still receives a plain string
  • Old inline fields (bot_post_template, post_template) raise a clear error pointing to the migration utility
  • Default when no file specified: unchanged built-in fallback (Jinja2 syntax: {{ title }}\n\n{{ link }})

Migration utility

New CLI subcommand: feed2fedi migrate-templates <config-file>

  • Dry-run by default; --apply to execute
  • Converts old syntax to Jinja2: {var}{{ var }}, {[prefix]var[suffix]}{% if var and var.strip() %}prefix{{ var }}suffix{% endif %}, \\n → real newline
  • Writes bot_post_template.j2 and {sanitized_url}_post_template.j2 alongside the config
  • Updates config JSON in place

Commit sequence (TDD)

  1. Failing tests for Jinja2 template rendering
  2. Implement Jinja2 engine + config field changes
  3. Failing tests for migrate-templates
  4. Implement migrate-templates CLI subcommand
  5. 📝 Update docs
## Findings and plan of attack Explored the current template system in full. Summary of what changes: ### Current system (to be removed) - `_format_conditional_template()` in `publish.py` — regex parser handling `{[prefix]var[suffix]}` patterns - `str.format(**params)` fallback for standard `{var}` substitution - `\\n` preprocessing hack (`post_template.replace("\\\\n", "\n")`) because JSON strings can't express newlines naturally - `bot_post_template` (inline string) on `Configuration`; `post_template` (inline string) on `FeedInfo` ### New system - **Jinja2** as the template engine — handles variable substitution, conditionals, filters (including `default()` for issue #87's fallback use case), and anything else users might need - **External `.j2` files only** — no inline template strings in config. This lets users write natural multiline templates without the `\\n` hack - New config fields: `bot_post_template_file` (bot level), `post_template_file` (feed level) — paths resolved relative to the config file's location - Templates loaded at config-load time; rest of the code still receives a plain string - Old inline fields (`bot_post_template`, `post_template`) raise a clear error pointing to the migration utility - Default when no file specified: unchanged built-in fallback (Jinja2 syntax: `{{ title }}\n\n{{ link }}`) ### Migration utility New CLI subcommand: `feed2fedi migrate-templates <config-file>` - Dry-run by default; `--apply` to execute - Converts old syntax to Jinja2: `{var}` → `{{ var }}`, `{[prefix]var[suffix]}` → `{% if var and var.strip() %}prefix{{ var }}suffix{% endif %}`, `\\n` → real newline - Writes `bot_post_template.j2` and `{sanitized_url}_post_template.j2` alongside the config - Updates config JSON in place ### Commit sequence (TDD) 1. ✅ Failing tests for Jinja2 template rendering 2. ✨ Implement Jinja2 engine + config field changes 3. ✅ Failing tests for `migrate-templates` 4. ✨ Implement `migrate-templates` CLI subcommand 5. 📝 Update docs
marvin8 commented 2026-06-19 06:25:21 +00:00 (Migrated from codeberg.org)

Addressed as part of version 4.0.0... which is a breaking change! The config file has subtley changed, however there is a migration helper included (feed2fedi-migrate-templates)

Addressed as part of version 4.0.0... which is a breaking change! The config file has subtley changed, however there is a migration helper included (`feed2fedi-migrate-templates`)
Sign in to join this conversation.
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
marvin8/feed2fedi#88
No description provided.