#!/usr/bin/env python3 """Erzeugt den lokalen simple-icons-Index aus dem npm-Paket. Das npm-Paket enthält rund 3.500 einzelne SVG-Dateien (etwa 15 MB). Für den Betrieb genügen Slug, Titel, Markenfarbe, Pfaddaten und Aliasnamen – daraus wird eine einzelne gzip-komprimierte JSON-Datei von etwa 2 MB. Aufruf: python scripts/vendor_simple_icons.py [--version 16] [--keep-tmp] """ import argparse import gzip import json import re import shutil import subprocess import sys import tarfile import tempfile from pathlib import Path TARGET = Path(__file__).resolve().parent.parent / "app" / "assets" / "simple_icons.json.gz" PATH_PATTERN = re.compile(r' Path: """Lädt das npm-Paket herunter und entpackt es.""" result = subprocess.run( ["npm", "pack", f"simple-icons@{version}"], cwd=workdir, capture_output=True, text=True, check=True, ) archive = workdir / result.stdout.strip().splitlines()[-1] with tarfile.open(archive) as tar: tar.extractall(workdir, filter="data") return workdir / "package" def collect_aliases(entry: dict) -> list[str]: """Sammelt alternative Schreibweisen aus den Metadaten.""" names: list[str] = [] aliases = entry.get("aliases") or {} for key in ("aka", "alt", "old", "dup"): value = aliases.get(key) if isinstance(value, list): names.extend(item if isinstance(item, str) else item.get("title", "") for item in value) elif isinstance(value, dict): names.extend(str(item) for item in value.values()) return [name for name in names if name] def build(package: Path) -> list[dict]: """Baut den kompakten Index aus Metadaten und SVG-Pfaden.""" metadata = json.loads((package / "data" / "simple-icons.json").read_text(encoding="utf-8")) icons: list[dict] = [] for entry in metadata: svg_file = package / "icons" / f"{entry['slug']}.svg" if not svg_file.exists(): print(f" übersprungen (keine SVG-Datei): {entry['slug']}", file=sys.stderr) continue match = PATH_PATTERN.search(svg_file.read_text(encoding="utf-8")) if not match: print(f" übersprungen (kein Pfad): {entry['slug']}", file=sys.stderr) continue icon = { "s": entry["slug"], "t": entry["title"], "h": entry["hex"], "p": match.group(1), } aliases = collect_aliases(entry) if aliases: icon["a"] = aliases icons.append(icon) return icons def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--version", default="16", help="npm-Version von simple-icons.") parser.add_argument("--keep-tmp", action="store_true", help="Arbeitsverzeichnis behalten.") args = parser.parse_args() workdir = Path(tempfile.mkdtemp(prefix="simple-icons-")) try: print(f"Lade simple-icons@{args.version} …") package = download(args.version, workdir) version = json.loads((package / "package.json").read_text())["version"] icons = build(package) payload = {"version": version, "icons": icons} raw = json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode("utf-8") TARGET.parent.mkdir(parents=True, exist_ok=True) TARGET.write_bytes(gzip.compress(raw, 9)) print( f"{len(icons)} Icons aus simple-icons {version} geschrieben nach " f"{TARGET.relative_to(TARGET.parent.parent.parent)} " f"({TARGET.stat().st_size / 1_000_000:.2f} MB)." ) return 0 finally: if not args.keep_tmp: shutil.rmtree(workdir, ignore_errors=True) if __name__ == "__main__": raise SystemExit(main())