from jinja2_simple_tags import StandaloneTag import jinja2 from copy import deepcopy from mkdocs.structure.files import File, Files from mkdocs.plugins import event_priority, get_plugin_logger import hashlib from concurrent.futures import ThreadPoolExecutor from pathlib import Path logger = get_plugin_logger("static") def transform_file_url(url: str, hash: str): url_path = Path(url) return str(url_path.with_stem(url_path.stem + "." + hash[:10])) def hash_file(file: File) -> File: logger.debug("Hashing %s", file.url) hashed_file = deepcopy(file) with open(file.abs_src_path, "rb") as f: digest = hashlib.file_digest(f, "md5").hexdigest() hashed_file.abs_dest_path = transform_file_url(hashed_file.abs_dest_path, digest) hashed_file.dest_uri = transform_file_url(hashed_file.dest_uri, digest) hashed_file.url = transform_file_url(hashed_file.url, digest) return hashed_file @event_priority(-100) def on_files(files: Files, config): with ThreadPoolExecutor() as executor: for hashed_file in executor.map(hash_file, files.media_files()): files.append(hashed_file) # HACK: Add a fake file with a `src_uri` of the hashed path so other integrations can find the right file hashed_src_file = deepcopy(hashed_file) hashed_src_file.src_uri = hashed_src_file.dest_uri files.append(hashed_src_file) return files def on_env(env: jinja2.Environment, config, files): env.add_extension(make_static_extension(files)) env.add_extension(make_sri_extension(files)) return env def make_static_extension(files: Files): class StaticExtension(StandaloneTag): tags = {"static"} def render(self, filename): file = files.get_file_from_path(filename) if file is None: raise ValueError(f"Unable to find static file for {filename!r}") return f"/{file.url}" return StaticExtension def make_sri_extension(files: Files): class SRIExtension(StandaloneTag): tags = {"sri_integrity"} def render(self, filename): file = files.get_file_from_path(filename) if file is None: raise ValueError(f"Unable to find static file for {filename!r}") with open(file.abs_src_path, "rb") as f: digest = hashlib.file_digest(f, "sha256").hexdigest() return f"sha256-{digest}" return SRIExtension