Source code for neurocore.scaffold.render
"""Template rendering helpers shared by ``neurocore init`` and ``neurocore new``.
Templates use a minimal ``{{ var }}`` substitution syntax (no real Jinja2
dependency). ``render_tree`` recursively copies a template directory, rendering
every text file through the same substitution.
"""
from __future__ import annotations
from pathlib import Path
# Binary/opaque file extensions copied verbatim (not rendered as text).
_BINARY_SUFFIXES = {".png", ".jpg", ".jpeg", ".gif", ".ico", ".pdf", ".zip"}
# Template directory shipped with the package.
TEMPLATES_DIR = Path(__file__).parent / "templates"
[docs]
def render_text(content: str, context: dict[str, str]) -> str:
"""Apply ``{{ var }}`` substitutions to a string."""
for key, value in context.items():
content = content.replace(f"{{{{ {key} }}}}", value)
return content
[docs]
def render_template(template_path: Path, context: dict[str, str]) -> str:
"""Render a single template file to a string."""
return render_text(template_path.read_text(), context)
[docs]
def render_tree(src_dir: Path, dest_dir: Path, context: dict[str, str]) -> None:
"""Recursively copy ``src_dir`` into ``dest_dir``, rendering text files.
Directory names and file names are rendered too, so a template file named
``{{ project_name }}.py`` is renamed accordingly. ``.gitkeep`` files create
empty directories without leaving the marker behind.
"""
dest_dir.mkdir(parents=True, exist_ok=True)
for item in sorted(src_dir.iterdir()):
rendered_name = render_text(item.name, context)
if item.is_dir():
render_tree(item, dest_dir / rendered_name, context)
elif item.name == ".gitkeep":
# Marker: ensure the parent directory exists, but don't copy it.
dest_dir.mkdir(parents=True, exist_ok=True)
elif item.suffix in _BINARY_SUFFIXES:
(dest_dir / rendered_name).write_bytes(item.read_bytes())
else:
(dest_dir / rendered_name).write_text(
render_text(item.read_text(), context)
)