feat: fix hot loading for config.toml changes on Linux (#2498)

This commit is contained in:
orphen 2024-05-25 03:57:04 +09:00 committed by Vincent Prouillet
parent 7294c76f6b
commit 2980d16da3
2 changed files with 33 additions and 20 deletions

View File

@ -46,7 +46,6 @@ use notify_debouncer_full::{new_debouncer, notify::RecursiveMode, notify::Watche
use ws::{Message, Sender, WebSocket}; use ws::{Message, Sender, WebSocket};
use errors::{anyhow, Context, Error, Result}; use errors::{anyhow, Context, Error, Result};
use pathdiff::diff_paths;
use site::sass::compile_sass; use site::sass::compile_sass;
use site::{Site, SITE_CONTENT}; use site::{Site, SITE_CONTENT};
use utils::fs::{clean_site_output_folder, copy_file}; use utils::fs::{clean_site_output_folder, copy_file};
@ -458,18 +457,21 @@ pub fn serve(
} }
let config_path = PathBuf::from(config_file); let config_path = PathBuf::from(config_file);
let config_path_rel = diff_paths(&config_path, root_dir).unwrap_or_else(|| config_path.clone()); let root_dir_str = root_dir.to_str().expect("Project root dir is not valid UTF-8.");
// An array of (path, WatchMode) where the path should be watched for changes, // An array of (path, WatchMode, RecursiveMode) where the path is watched for changes,
// and the WatchMode value indicates whether this file/folder must exist for // the WatchMode value indicates whether this path must exist for zola serve to operate,
// zola serve to operate // and the RecursiveMode value indicates whether to watch nested directories.
let watch_this = vec![ let watch_this = vec![
(config_path_rel.to_str().unwrap_or("config.toml"), WatchMode::Required), // The first entry is ultimtely to watch config.toml in a more robust manner on Linux when
("content", WatchMode::Required), // the file changes by way of a caching strategy used by editors such as vim.
("sass", WatchMode::Condition(site.config.compile_sass)), // https://github.com/getzola/zola/issues/2266
("static", WatchMode::Optional), (root_dir_str, WatchMode::Required, RecursiveMode::NonRecursive),
("templates", WatchMode::Optional), ("content", WatchMode::Required, RecursiveMode::Recursive),
("themes", WatchMode::Condition(site.config.theme.is_some())), ("sass", WatchMode::Condition(site.config.compile_sass), RecursiveMode::Recursive),
("static", WatchMode::Optional, RecursiveMode::Recursive),
("templates", WatchMode::Optional, RecursiveMode::Recursive),
("themes", WatchMode::Condition(site.config.theme.is_some()), RecursiveMode::Recursive),
]; ];
// Setup watchers // Setup watchers
@ -482,16 +484,16 @@ pub fn serve(
// - the path exists but has incorrect permissions // - the path exists but has incorrect permissions
// watchers will contain the paths we're actually watching // watchers will contain the paths we're actually watching
let mut watchers = Vec::new(); let mut watchers = Vec::new();
for (entry, mode) in watch_this { for (entry, watch_mode, recursive_mode) in watch_this {
let watch_path = root_dir.join(entry); let watch_path = root_dir.join(entry);
let should_watch = match mode { let should_watch = match watch_mode {
WatchMode::Required => true, WatchMode::Required => true,
WatchMode::Optional => watch_path.exists(), WatchMode::Optional => watch_path.exists(),
WatchMode::Condition(b) => b && watch_path.exists(), WatchMode::Condition(b) => b && watch_path.exists(),
}; };
if should_watch { if should_watch {
debouncer.watcher() debouncer.watcher()
.watch(&root_dir.join(entry), RecursiveMode::Recursive) .watch(&root_dir.join(entry), recursive_mode)
.with_context(|| format!("Can't watch `{}` for changes in folder `{}`. Does it exist, and do you have correct permissions?", entry, root_dir.display()))?; .with_context(|| format!("Can't watch `{}` for changes in folder `{}`. Does it exist, and do you have correct permissions?", entry, root_dir.display()))?;
watchers.push(entry.to_string()); watchers.push(entry.to_string());
} }
@ -575,12 +577,17 @@ pub fn serve(
broadcaster broadcaster
}; };
println!( // We watch for changes in the config by monitoring its parent directory, but we ignore all
"Listening for changes in {}{}{{{}}}", // ordinary peer files. Map the parent directory back to the config file name to not confuse
root_dir.display(), // the end user.
MAIN_SEPARATOR, let config_name =
watchers.join(",") config_path.file_name().unwrap().to_str().expect("Config name is not valid UTF-8.");
); let watch_list = watchers
.iter()
.map(|w| if w == root_dir_str { config_name } else { w })
.collect::<Vec<&str>>()
.join(",");
println!("Listening for changes in {}{}{{{}}}", root_dir.display(), MAIN_SEPARATOR, watch_list);
let preserve_dotfiles_in_output = site.config.preserve_dotfiles_in_output; let preserve_dotfiles_in_output = site.config.preserve_dotfiles_in_output;

View File

@ -96,6 +96,12 @@ pub fn filter_events(
continue; continue;
} }
// Ignore ordinary files peer to config.toml. This assumes all other files we care
// about are nested more deeply than config.toml or are directories peer to config.toml.
if path != config_path && path.is_file() && path.parent() == config_path.parent() {
continue;
}
let (change_k, partial_p) = detect_change_kind(root_dir, &path, config_path); let (change_k, partial_p) = detect_change_kind(root_dir, &path, config_path);
meaningful_events.insert(path, (partial_p, simple_kind.unwrap(), change_k)); meaningful_events.insert(path, (partial_p, simple_kind.unwrap(), change_k));
} }