feat: per-theme Jinja env enables theme-level macro override
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
+16
-2
@@ -16,7 +16,21 @@ from markupsafe import Markup
|
|||||||
from .core import sanitize, route_path, THEMES
|
from .core import sanitize, route_path, THEMES
|
||||||
|
|
||||||
ROOT=Path(__file__).resolve().parent.parent
|
ROOT=Path(__file__).resolve().parent.parent
|
||||||
LOADER=Environment(loader=FileSystemLoader(str(ROOT/'themes')),autoescape=select_autoescape(['html','xml']))
|
_ENVS={}
|
||||||
|
|
||||||
|
def env_for(theme):
|
||||||
|
"""Per-theme Jinja env: theme dir first, shared themes/ root as fallback.
|
||||||
|
|
||||||
|
Lets a theme override shared macros (_professional_components.html) while
|
||||||
|
inheriting everything else. Cached: one env per theme across large builds.
|
||||||
|
"""
|
||||||
|
env=_ENVS.get(theme)
|
||||||
|
if env is None:
|
||||||
|
theme_dir=ROOT/'themes'/theme
|
||||||
|
search=[str(theme_dir),str(ROOT/'themes')] if theme_dir.is_dir() else [str(ROOT/'themes')]
|
||||||
|
env=Environment(loader=FileSystemLoader(search),autoescape=select_autoescape(['html','xml']))
|
||||||
|
_ENVS[theme]=env
|
||||||
|
return env
|
||||||
|
|
||||||
TITLES={'product':'产品中心','vehicle':'车型中心','post':'新闻资讯','case':'项目案例','brand':'品牌中心','service':'服务与解决方案'}
|
TITLES={'product':'产品中心','vehicle':'车型中心','post':'新闻资讯','case':'项目案例','brand':'品牌中心','service':'服务与解决方案'}
|
||||||
|
|
||||||
@@ -186,4 +200,4 @@ def render(theme,site,page,records,mode='detail',listing_items=None,blocks_html=
|
|||||||
if theme not in THEMES:raise ValueError('不是内置专业主题')
|
if theme not in THEMES:raise ValueError('不是内置专业主题')
|
||||||
ctx=make_context(site,page,records,mode,listing_items,prepared=prepared)
|
ctx=make_context(site,page,records,mode,listing_items,prepared=prepared)
|
||||||
ctx.update(custom_blocks=Markup(blocks_html or ''),has_custom=bool(blocks_html),noindex=(ctx['kind']=='notfound'),designer_replace=meta(page).get('layout_mode')=='replace')
|
ctx.update(custom_blocks=Markup(blocks_html or ''),has_custom=bool(blocks_html),noindex=(ctx['kind']=='notfound'),designer_replace=meta(page).get('layout_mode')=='replace')
|
||||||
return LOADER.get_template(theme+'/pro.html').render(**ctx)
|
return env_for(theme).get_template('pro.html').render(**ctx)
|
||||||
|
|||||||
@@ -37,3 +37,20 @@ def test_discover_fails_fast_when_themes_dir_missing(tmp_path, monkeypatch):
|
|||||||
import pytest
|
import pytest
|
||||||
with pytest.raises(RuntimeError, match='themes directory missing'):
|
with pytest.raises(RuntimeError, match='themes directory missing'):
|
||||||
core.discover_themes()
|
core.discover_themes()
|
||||||
|
|
||||||
|
def test_env_for_theme_prefers_theme_dir_then_shared_root():
|
||||||
|
from app import theme_pro
|
||||||
|
env = theme_pro.env_for('industrial')
|
||||||
|
paths = [str(p).replace('\\', '/') for p in env.loader.searchpath]
|
||||||
|
assert paths[0].endswith('themes/industrial')
|
||||||
|
assert paths[1].endswith('themes')
|
||||||
|
# 共享宏库必须仍可解析(回落路径)
|
||||||
|
assert env.get_template('_professional_components.html') is not None
|
||||||
|
|
||||||
|
def test_env_for_is_cached_per_theme():
|
||||||
|
from app import theme_pro
|
||||||
|
assert theme_pro.env_for('industrial') is theme_pro.env_for('industrial')
|
||||||
|
|
||||||
|
def test_env_for_pro_template_loads_unprefixed():
|
||||||
|
from app import theme_pro
|
||||||
|
assert theme_pro.env_for('industrial').get_template('pro.html') is not None
|
||||||
|
|||||||
Reference in New Issue
Block a user