#!/usr/bin/env python3 """检查 Markdown 中引用的本地图片是否真实存在。""" from __future__ import annotations import argparse import json from pathlib import Path import re import sys from typing import Iterable, List, Optional from urllib.parse import urlparse IMAGE_RE = re.compile(r"!\[[^\]]*\]\(([^)]+)\)") def die(message: str, code: int = 1) -> None: print(f"错误:{message}", file=sys.stderr) raise SystemExit(code) def normalize_target(raw: str) -> str: value = raw.strip() if value.startswith("<") and value.endswith(">"): return value[1:-1].strip() if " " in value: value = value.split()[0] return value.strip("'\"") def is_remote_or_anchor(target: str) -> bool: parsed = urlparse(target) return bool(parsed.scheme in {"http", "https", "data", "mailto"} or target.startswith("#")) def find_missing(markdown_path: Path) -> List[str]: text = markdown_path.read_text(encoding="utf-8") base_dir = markdown_path.parent missing: List[str] = [] for match in IMAGE_RE.finditer(text): target = normalize_target(match.group(1)) if not target or is_remote_or_anchor(target): continue image_path = Path(target) if not image_path.is_absolute(): image_path = base_dir / image_path if not image_path.exists(): missing.append(target) return missing def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="检查 Markdown 本地图片路径是否存在。") parser.add_argument("markdown", help="要检查的 article.md 或其他 Markdown 文件。") return parser def main(argv: Optional[Iterable[str]] = None) -> int: parser = build_parser() args = parser.parse_args(list(argv) if argv is not None else None) markdown_path = Path(args.markdown).expanduser().resolve() if not markdown_path.exists(): die(f"Markdown 文件不存在:{markdown_path}") missing = find_missing(markdown_path) result = { "markdown": str(markdown_path), "missing_count": len(missing), "missing": missing, } print(json.dumps(result, ensure_ascii=False, indent=2)) return 1 if missing else 0 if __name__ == "__main__": raise SystemExit(main())