diff --git a/crates/zensical/src/config/project.rs b/crates/zensical/src/config/project.rs index 1cd7eb5..21d2e36 100644 --- a/crates/zensical/src/config/project.rs +++ b/crates/zensical/src/config/project.rs @@ -95,4 +95,6 @@ pub struct Project { pub plugins_hash: u64, /// Source files. pub source_files: Vec<(PathBuf, u64)>, + /// Snippet files. + pub snippet_files: Vec<(PathBuf, u64)>, } diff --git a/crates/zensical/src/watcher.rs b/crates/zensical/src/watcher.rs index 14df241..01ba3fc 100644 --- a/crates/zensical/src/watcher.rs +++ b/crates/zensical/src/watcher.rs @@ -121,6 +121,15 @@ impl Watcher { return Err(Error::Disconnected); } + // Check if one of the source files managed by the Snippets + // Markdown extension changed, and restart the build + let mut iter = config.project.snippet_files.iter(); + if iter.any(|(path, _)| &*event.path() == path) + && !seen.insert((*event.path()).clone()) + { + return Err(Error::Disconnected); + } + // Ignore events in the site directory, since they are files // that were generated and should not trigger a rebuild. We // forward them to the reload channel in the server instead, @@ -198,6 +207,11 @@ impl Watcher { agent.watch(path)?; } + // Watch source files managed by Snippets Markdown extension + for (path, _) in &config.project.snippet_files { + agent.watch(path)?; + } + // Watch site directory, ensuring it exists let site_dir = config.get_site_dir(); fs::create_dir_all(&site_dir).unwrap(); diff --git a/python/zensical/config.py b/python/zensical/config.py index 83dbb5f..ed41a4e 100644 --- a/python/zensical/config.py +++ b/python/zensical/config.py @@ -453,6 +453,10 @@ def _apply_defaults(config: dict, path: str) -> dict: # List all source files for mkdocstrings config["source_files"] = _list_sources(config, path) + # List all snippet files referenced in pymdownx.snippets configuration, + # so we can watch them and trigger a rebuild when they change + config["snippet_files"] = _list_snippet_files(config, path) + # Hash all templates, so we rebuild if something changes config["template_hash"] = _hash(_list_templates(config)) @@ -541,6 +545,27 @@ def _list_sources(config: dict, config_file: str) -> list[tuple[str, int]]: return sorted(files_with_hash) +def _list_snippet_files( + config: dict, config_file: str +) -> list[tuple[str, int]]: + """List files referenced in pymdownx.snippets auto_append configuration.""" + snippets_config = config["mdx_configs"].get("pymdownx.snippets", {}) + auto_append = snippets_config.get("auto_append", []) + base_paths = snippets_config.get("base_path", ["."]) + + root = Path(config_file).parent.resolve() + files_with_mtime = [] + for file_name in auto_append: + for base_path in base_paths: + candidate = root.joinpath(base_path, file_name).resolve() + if candidate.is_file(): + mtime = int(os.path.getmtime(candidate)) + files_with_mtime.append((str(candidate), mtime)) + break + + return sorted(files_with_mtime) + + def _list_templates(config: dict) -> list[tuple[str, int]]: """List all template files in the theme directories.""" dirs = [get_theme_dir()]