diff --git a/crates/zensical/src/config/project.rs b/crates/zensical/src/config/project.rs index 2577b7d..5aaa322 100644 --- a/crates/zensical/src/config/project.rs +++ b/crates/zensical/src/config/project.rs @@ -82,6 +82,8 @@ pub struct Project { pub extra_templates: Vec, /// Markdown extension configuration. pub mdx_configs: MdxConfigs, + /// Markdown extension configuration hash. + pub mdx_configs_hash: u64, /// Plugins. pub plugins: Plugins, /// Navigation structure. diff --git a/crates/zensical/src/workflow.rs b/crates/zensical/src/workflow.rs index 4a96125..227300d 100644 --- a/crates/zensical/src/workflow.rs +++ b/crates/zensical/src/workflow.rs @@ -137,8 +137,10 @@ pub fn process_markdown( .map_concurrency( with_id(move |id: &Id, path: String| { let data = fs::read_to_string(path)?; - cached(&config, id, data, |data| Markdown::new(id, data)) - .into_report() + cached(&config, id, (config.hash, data), |(_, data)| { + Markdown::new(id, data) + }) + .into_report() }), 1, ) diff --git a/python/zensical/config.py b/python/zensical/config.py index d08e2bc..97fde26 100644 --- a/python/zensical/config.py +++ b/python/zensical/config.py @@ -23,8 +23,10 @@ from __future__ import annotations +import hashlib import importlib import os +import pickle import yaml try: @@ -34,7 +36,6 @@ except ModuleNotFoundError: from click import ClickException from deepmerge import always_merger -from functools import partial from typing import Any, IO from yaml import BaseLoader, Loader, YAMLError from yaml.constructor import ConstructorError @@ -113,6 +114,13 @@ def parse_mkdocs_config(path: str) -> dict: return _CONFIG +def get_config(): + """ + Return configuration. + """ + return _CONFIG + + def get_theme_dir() -> str: """ Return the theme directory. @@ -444,6 +452,7 @@ def _apply_defaults(config: dict, path: str) -> dict: # Ensure the table of contents title is initialized, as it's used inside # the template, and the table of contents extension is always defined config["mdx_configs"]["toc"].setdefault("title", None) + config["mdx_configs_hash"] = _hash(mdx_configs) # Convert plugins configuration config["plugins"] = _convert_plugins(config.get("plugins", []), config) @@ -473,6 +482,14 @@ def set_default( return entry[key] +def _hash(data: any) -> int: + """ + Compute a hash for the given data. + """ + hash = hashlib.sha1(pickle.dumps(data)) + return int(hash.hexdigest(), 16) % (2**64) + + def _convert_extra(data: dict | list) -> dict | list: """ Recursively convert all None values in a dictionary or list to empty strings. diff --git a/python/zensical/markdown.py b/python/zensical/markdown.py index 11811aa..c5be44d 100644 --- a/python/zensical/markdown.py +++ b/python/zensical/markdown.py @@ -30,7 +30,7 @@ from datetime import date, datetime from markdown import Markdown from yaml import SafeLoader -from .config import _CONFIG +from .config import get_config from .extensions.links import LinksExtension from .extensions.search import SearchExtension @@ -61,7 +61,7 @@ def render(content: str, path: str) -> dict: in order to support the specific syntax of Python Markdown. We're working on moving the entire rendering chain to Rust. """ - config = _CONFIG + config = get_config() # Initialize Markdown parser md = Markdown(