mirror of
https://github.com/kbenestad/mdcms.git
synced 2026-06-18 07:24:31 +00:00
v0.4 Phase 3: asset validation in mdcms build
- Add validate_assets(): scans config.yml and theme.yml recursively for string values starting with assets/, and all markdown files in pages/ and posts/ via regex; deduplicates by (path, source) before checking - Add _collect_yaml_assets() helper for recursive YAML traversal - Call validate_assets() in run_build() after writing nav.yml and search.json; prints yellow warnings for each missing asset, build continues on all warnings https://claude.ai/code/session_015XtsgTMi8UtmgxEgb5Qt2c
This commit is contained in:
parent
1051a36044
commit
8ae283fe41
1 changed files with 64 additions and 0 deletions
64
mdcms.py
64
mdcms.py
|
|
@ -339,6 +339,66 @@ def generate_search_json(
|
|||
return json.dumps(out, indent=2, ensure_ascii=False)
|
||||
|
||||
|
||||
# ─── Asset validation ─────────────────────────────────────────
|
||||
|
||||
_ASSET_RE = re.compile(r'assets/[\w.\-/]+')
|
||||
|
||||
|
||||
def _collect_yaml_assets(val, source: str, out: list):
|
||||
if isinstance(val, str):
|
||||
if val.startswith("assets/"):
|
||||
out.append((val, source))
|
||||
elif isinstance(val, dict):
|
||||
for v in val.values():
|
||||
_collect_yaml_assets(v, source, out)
|
||||
elif isinstance(val, list):
|
||||
for item in val:
|
||||
_collect_yaml_assets(item, source, out)
|
||||
|
||||
|
||||
def validate_assets(site_path: Path, cfg: dict) -> list:
|
||||
"""Return list of warning strings for assets/ references that don't exist on disk."""
|
||||
refs: list = []
|
||||
|
||||
_collect_yaml_assets(cfg, "config.yml", refs)
|
||||
|
||||
theme_file = cfg.get("theme")
|
||||
if theme_file:
|
||||
theme_path = site_path / theme_file
|
||||
if theme_path.exists():
|
||||
try:
|
||||
theme_data = yaml.safe_load(theme_path.read_text(encoding="utf-8")) or {}
|
||||
_collect_yaml_assets(theme_data, theme_file, refs)
|
||||
except (OSError, yaml.YAMLError):
|
||||
pass
|
||||
|
||||
for folder in ("pages", "posts"):
|
||||
d = site_path / folder
|
||||
if not d.is_dir():
|
||||
continue
|
||||
for md_file in sorted(d.rglob("*.md")):
|
||||
try:
|
||||
content = md_file.read_text(encoding="utf-8")
|
||||
rel = str(md_file.relative_to(site_path)).replace("\\", "/")
|
||||
for m in _ASSET_RE.finditer(content):
|
||||
refs.append((m.group(), rel))
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
warnings = []
|
||||
seen: set = set()
|
||||
for asset_path, source in refs:
|
||||
key = (asset_path, source)
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
if not (site_path / asset_path).exists():
|
||||
warnings.append(
|
||||
f"Warning: asset not found: {asset_path}\n Referenced in: {source}"
|
||||
)
|
||||
return warnings
|
||||
|
||||
|
||||
# ─── Core build logic ─────────────────────────────────────────
|
||||
|
||||
def run_build(site_path: Path):
|
||||
|
|
@ -420,6 +480,10 @@ def run_build(site_path: Path):
|
|||
)
|
||||
click.echo(f" Wrote search.json ({len(live_pages) + len(post_records)} entries)")
|
||||
|
||||
asset_warnings = validate_assets(site_path, cfg)
|
||||
for w in asset_warnings:
|
||||
click.echo(click.style(w, fg="yellow"))
|
||||
|
||||
if auto_created:
|
||||
click.echo(click.style(
|
||||
f"\nNotice: {len(auto_created)} section(s) auto-created: {', '.join(auto_created)}\n"
|
||||
|
|
|
|||
Loading…
Reference in a new issue