Source code for pjkm.core.templates.composer
"""Template composer: layers multiple templates to build a complete project."""
from __future__ import annotations
from pathlib import Path
from typing import Any
from pjkm.core.templates.loader import TemplateLoader
from pjkm.core.templates.renderer import TemplateRenderer
[docs]
class TemplateComposer:
"""Composes a project by layering base + archetype + fragment templates.
Rendering order:
1. "base" template (shared by all archetypes)
2. Archetype-specific template (e.g., "single_package")
3. Fragment templates (e.g., from package groups)
"""
def __init__(
self,
loader: TemplateLoader | None = None,
renderer: TemplateRenderer | None = None,
) -> None:
self._loader = loader or TemplateLoader()
self._renderer = renderer or TemplateRenderer()
[docs]
def compose(
self,
archetype: str,
dest: Path,
data: dict[str, Any],
fragments: list[str] | None = None,
pretend: bool = False,
) -> list[str]:
"""Layer templates to build a complete project.
Returns a list of template names that were applied.
"""
applied: list[str] = []
# 1. Base template
base_path = self._loader.resolve("base")
self._renderer.render(
template_path=base_path,
dest=dest,
data=data,
overwrite=False,
pretend=pretend,
)
applied.append("base")
# 2. Archetype template
arch_path = self._loader.resolve(archetype)
self._renderer.render(
template_path=arch_path,
dest=dest,
data=data,
overwrite=True,
pretend=pretend,
)
applied.append(archetype)
# 3. Fragments
for frag_name in fragments or []:
frag_path = self._loader.resolve(f"fragments/{frag_name}")
self._renderer.render(
template_path=frag_path,
dest=dest,
data=data,
overwrite=True,
skip_if_exists=["*.py"],
pretend=pretend,
)
applied.append(f"fragments/{frag_name}")
return applied