#!/usr/bin/env python3 import re import sys from html.parser import HTMLParser from pathlib import Path from urllib.parse import urlparse ROOT = Path(__file__).resolve().parent.parent INDEX = ROOT / "index.html" SKIP = ("#", "mailto:", "tel:", "data:", "javascript:") FETCH_RELS = {"stylesheet", "icon", "preload", "modulepreload", "manifest", "apple-touch-icon"} errors = [] def check(ref, base, where, fetched): if not ref or ref.startswith(SKIP): return if ref.startswith(("http://", "https://", "//")): if fetched: errors.append(f"{where}: external request {ref}") return path = urlparse(ref).path target = ROOT / path.lstrip("/") if path.startswith("/") else base.parent / path if not target.exists(): errors.append(f"{where}: missing file {ref}") class Page(HTMLParser): def handle_starttag(self, tag, attrs): a = dict(attrs) refs = [] if tag in ("script", "img", "source", "iframe", "video", "audio"): refs.append(a.get("src")) for part in (a.get("srcset") or "").split(","): if part.strip(): refs.append(part.split()[0]) elif tag == "link" and FETCH_RELS & set((a.get("rel") or "").split()): refs.append(a.get("href")) for ref in refs: check(ref, INDEX, "index.html", True) if tag == "a": check(a.get("href"), INDEX, "index.html", False) for name in ("index.html", "favicon.svg", "robots.txt"): if not (ROOT / name).is_file(): errors.append(f"missing required file: {name}") if INDEX.is_file(): Page().feed(INDEX.read_text(encoding="utf-8")) for css in ROOT.glob("assets/*.css"): for ref in re.findall(r"url\(\s*['\"]?([^'\")]+)", css.read_text(encoding="utf-8")): check(ref, css, str(css.relative_to(ROOT)), True) if errors: print("\n".join(errors)) sys.exit(1) print("site check passed")