| from __future__ import annotations |
|
|
| import re |
| from pathlib import Path |
|
|
| _FILE_PLACEHOLDER_RE = re.compile(r"\{\{file:([^}]+)\}\}") |
|
|
|
|
|
|
| def expand_file_placeholders( |
| text: str, |
| *, |
| workspace_root: Path, |
| seen: set[Path] | None = None, |
| ) -> str: |
| workspace_root = workspace_root.resolve() |
| active = set() if seen is None else set(seen) |
|
|
| def replace(match: re.Match[str]) -> str: |
| raw_ref = match.group(1).strip() |
| include_path = Path(raw_ref) |
| if not include_path.is_absolute(): |
| include_path = workspace_root / include_path |
| include_path = include_path.resolve() |
| if include_path in active: |
| raise ValueError(f"cyclic {{file:...}} include detected at {include_path}") |
| included = include_path.read_text(encoding="utf-8") |
| return expand_file_placeholders( |
| included, |
| workspace_root=workspace_root, |
| seen={*active, include_path}, |
| ) |
|
|
| return _FILE_PLACEHOLDER_RE.sub(replace, text) |
|
|
|
|
|
|
| def materialize_expanded_card( |
| card_path: Path, |
| *, |
| workspace_root: Path, |
| out_dir: Path, |
| ) -> Path: |
| card_path = card_path.resolve() |
| expanded = expand_file_placeholders( |
| card_path.read_text(encoding="utf-8"), |
| workspace_root=workspace_root, |
| seen={card_path}, |
| ) |
| out_dir.mkdir(parents=True, exist_ok=True) |
| output_path = out_dir / f".{card_path.stem}.expanded{card_path.suffix}" |
| output_path.write_text(expanded, encoding="utf-8") |
| return output_path |
|
|