Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,37 @@ python examples/fastapi_example.py
# Open http://localhost:8000
```

#### Sharing an existing JinjaX catalog

Most FastAPI + JinjaX projects already create a `Catalog` at startup — often
sharing its Jinja environment with `Jinja2Templates` so page templates and
components see the same custom filters, globals (e.g. `url_for`), and extensions.

**Pass that existing catalog to `JinjaxRenderer`** — do not create a new one:

```python
# consts.py — your existing setup
from fastapi.templating import Jinja2Templates
from jinjax import Catalog, JinjaX

templates = Jinja2Templates(directory="templates")
templates.env.add_extension(JinjaX) # share one Jinja environment
templates.env.globals["url_for"] = my_url_for # custom global
catalog = Catalog(jinja_env=templates.env) # catalog reuses that env
catalog.add_folder("templates/components")

# Wire the component framework to the SAME catalog
from component_framework.adapters.jinjax_renderer import JinjaxRenderer
from component_framework.core.component import Component

Component.renderer = JinjaxRenderer(catalog) # ✅ inherits filters/globals/extensions
```

> ⚠️ Creating a **fresh** `Catalog()` (as in minimal examples) gives it a *new,
> empty* Jinja environment. Component templates then silently lose your
> `url_for`, custom filters, and extensions and render incorrectly. Always hand
> `JinjaxRenderer` the catalog your app already configured.

### Django Example

```bash
Expand Down
15 changes: 12 additions & 3 deletions src/component_framework/adapters/jinjax_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,23 @@


class JinjaxRenderer(Renderer):
"""Renderer using Jinjax component system."""
"""Renderer using the JinjaX component system."""

def __init__(self, catalog: Catalog):
"""
Initialize Jinjax renderer.
Initialize the JinjaX renderer.

Pass the ``Catalog`` your application already configured rather than a
fresh one. The renderer renders through this catalog's Jinja
environment, so it inherits every custom filter, global (e.g.
``url_for``), and extension registered on it. A brand-new ``Catalog()``
has its own empty environment, so component templates would silently
lose that context. When sharing with ``Jinja2Templates``, build the
catalog from the same env, e.g. ``Catalog(jinja_env=templates.env)``.

Args:
catalog: Jinjax Catalog instance
catalog: The JinjaX ``Catalog`` instance to render through —
ideally the one shared with the rest of your app.
"""
self.catalog = catalog

Expand Down
56 changes: 56 additions & 0 deletions tests/test_jinjax_renderer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""Tests for JinjaxRenderer, focused on sharing an existing Catalog (issue #13).

The documented guidance is that consumers should pass their *existing* JinjaX
``Catalog`` to ``JinjaxRenderer`` so component templates inherit the host app's
custom globals, filters, and extensions. These tests lock that behavior in.
"""

from __future__ import annotations

import pytest

pytest.importorskip("jinjax")

from jinjax import Catalog # noqa: E402

from component_framework.adapters.jinjax_renderer import JinjaxRenderer # noqa: E402


def _write_component(folder, name: str, body: str) -> None:
(folder / f"{name}.jinja").write_text(body, encoding="utf-8")


def test_renderer_uses_shared_catalog_globals_and_filters(tmp_path):
"""A Catalog whose jinja_env carries custom globals/filters is honored by
JinjaxRenderer — this is the 'share your existing catalog' pattern from the
docs."""
components = tmp_path / "components"
components.mkdir()
_write_component(
components,
"Greeting",
"{# def name #}\n<p>{{ url_for('home') }} {{ name | shout }}</p>",
)

catalog = Catalog()
catalog.jinja_env.globals["url_for"] = lambda route: f"/{route}/"
catalog.jinja_env.filters["shout"] = lambda value: str(value).upper()
catalog.add_folder(str(components))

html = JinjaxRenderer(catalog).render("Greeting", {"name": "world"})

assert "/home/" in html # shared global reached the component
assert "WORLD" in html # shared filter reached the component


def test_fresh_catalog_does_not_inherit_host_globals():
"""Contrast: a fresh Catalog() (the README's old example) starts with an
empty jinja_env, so it lacks the host app's globals — exactly the silent
breakage issue #13 documents."""
shared = Catalog()
shared.jinja_env.globals["url_for"] = lambda route: f"/{route}/"

fresh = Catalog()

assert "url_for" in shared.jinja_env.globals
assert "url_for" not in fresh.jinja_env.globals
Loading