|
@@ -9,7 +9,7 @@ use std::{
|
|
path::PathBuf,
|
|
path::PathBuf,
|
|
sync::{Arc, Mutex},
|
|
sync::{Arc, Mutex},
|
|
};
|
|
};
|
|
-use tokio::sync::broadcast::Sender;
|
|
|
|
|
|
+use tokio::sync::broadcast::{self};
|
|
|
|
|
|
mod output;
|
|
mod output;
|
|
use output::*;
|
|
use output::*;
|
|
@@ -21,6 +21,7 @@ async fn setup_file_watcher<F: Fn() -> Result<BuildResult> + Send + 'static>(
|
|
build_with: F,
|
|
build_with: F,
|
|
config: &CrateConfig,
|
|
config: &CrateConfig,
|
|
web_info: Option<WebServerInfo>,
|
|
web_info: Option<WebServerInfo>,
|
|
|
|
+ hot_reload: Option<HotReloadState>,
|
|
) -> Result<RecommendedWatcher> {
|
|
) -> Result<RecommendedWatcher> {
|
|
let mut last_update_time = chrono::Local::now().timestamp();
|
|
let mut last_update_time = chrono::Local::now().timestamp();
|
|
|
|
|
|
@@ -38,145 +39,100 @@ async fn setup_file_watcher<F: Fn() -> Result<BuildResult> + Send + 'static>(
|
|
let config = watcher_config.clone();
|
|
let config = watcher_config.clone();
|
|
if let Ok(e) = info {
|
|
if let Ok(e) = info {
|
|
if chrono::Local::now().timestamp() > last_update_time {
|
|
if chrono::Local::now().timestamp() > last_update_time {
|
|
- match build_with() {
|
|
|
|
- Ok(res) => {
|
|
|
|
- last_update_time = chrono::Local::now().timestamp();
|
|
|
|
-
|
|
|
|
- #[allow(clippy::redundant_clone)]
|
|
|
|
- print_console_info(
|
|
|
|
- &config,
|
|
|
|
- PrettierOptions {
|
|
|
|
- changed: e.paths.clone(),
|
|
|
|
- warnings: res.warnings,
|
|
|
|
- elapsed_time: res.elapsed_time,
|
|
|
|
- },
|
|
|
|
- web_info.clone(),
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- #[cfg(feature = "plugin")]
|
|
|
|
- let _ = PluginManager::on_serve_rebuild(
|
|
|
|
- chrono::Local::now().timestamp(),
|
|
|
|
- e.paths,
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- Err(e) => log::error!("{}", e),
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
- .unwrap();
|
|
|
|
-
|
|
|
|
- for sub_path in allow_watch_path {
|
|
|
|
- watcher
|
|
|
|
- .watch(
|
|
|
|
- &config.crate_dir.join(sub_path),
|
|
|
|
- notify::RecursiveMode::Recursive,
|
|
|
|
- )
|
|
|
|
- .unwrap();
|
|
|
|
- }
|
|
|
|
- Ok(watcher)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// Todo: reduce duplication and merge with setup_file_watcher()
|
|
|
|
-/// Sets up a file watcher with hot reload
|
|
|
|
-async fn setup_file_watcher_hot_reload<F: Fn() -> Result<BuildResult> + Send + 'static>(
|
|
|
|
- config: &CrateConfig,
|
|
|
|
- hot_reload_tx: Sender<Template<'static>>,
|
|
|
|
- file_map: Arc<Mutex<FileMap<HtmlCtx>>>,
|
|
|
|
- build_with: F,
|
|
|
|
- web_info: Option<WebServerInfo>,
|
|
|
|
-) -> Result<RecommendedWatcher> {
|
|
|
|
- // file watcher: check file change
|
|
|
|
- let allow_watch_path = config
|
|
|
|
- .dioxus_config
|
|
|
|
- .web
|
|
|
|
- .watcher
|
|
|
|
- .watch_path
|
|
|
|
- .clone()
|
|
|
|
- .unwrap_or_else(|| vec![PathBuf::from("src")]);
|
|
|
|
|
|
+ let mut needs_full_rebuild;
|
|
|
|
+ if let Some(hot_reload) = &hot_reload {
|
|
|
|
+ // find changes to the rsx in the file
|
|
|
|
+ let mut rsx_file_map = hot_reload.file_map.lock().unwrap();
|
|
|
|
+ let mut messages: Vec<Template<'static>> = Vec::new();
|
|
|
|
|
|
- let watcher_config = config.clone();
|
|
|
|
- let mut last_update_time = chrono::Local::now().timestamp();
|
|
|
|
|
|
+ // In hot reload mode, we only need to rebuild if non-rsx code is changed
|
|
|
|
+ needs_full_rebuild = false;
|
|
|
|
|
|
- let mut watcher = RecommendedWatcher::new(
|
|
|
|
- move |evt: notify::Result<notify::Event>| {
|
|
|
|
- let config = watcher_config.clone();
|
|
|
|
- // Give time for the change to take effect before reading the file
|
|
|
|
- std::thread::sleep(std::time::Duration::from_millis(100));
|
|
|
|
- if chrono::Local::now().timestamp() > last_update_time {
|
|
|
|
- if let Ok(evt) = evt {
|
|
|
|
- let mut messages: Vec<Template<'static>> = Vec::new();
|
|
|
|
- for path in evt.paths.clone() {
|
|
|
|
|
|
+ for path in &e.paths {
|
|
// if this is not a rust file, rebuild the whole project
|
|
// if this is not a rust file, rebuild the whole project
|
|
if path.extension().and_then(|p| p.to_str()) != Some("rs") {
|
|
if path.extension().and_then(|p| p.to_str()) != Some("rs") {
|
|
- match build_with() {
|
|
|
|
- Ok(res) => {
|
|
|
|
- print_console_info(
|
|
|
|
- &config,
|
|
|
|
- PrettierOptions {
|
|
|
|
- changed: evt.paths,
|
|
|
|
- warnings: res.warnings,
|
|
|
|
- elapsed_time: res.elapsed_time,
|
|
|
|
- },
|
|
|
|
- web_info.clone(),
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- Err(err) => {
|
|
|
|
- log::error!("{}", err);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return;
|
|
|
|
|
|
+ needs_full_rebuild = true;
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
- // find changes to the rsx in the file
|
|
|
|
- let mut map = file_map.lock().unwrap();
|
|
|
|
|
|
|
|
- match map.update_rsx(&path, &config.crate_dir) {
|
|
|
|
|
|
+ match rsx_file_map.update_rsx(path, &config.crate_dir) {
|
|
Ok(UpdateResult::UpdatedRsx(msgs)) => {
|
|
Ok(UpdateResult::UpdatedRsx(msgs)) => {
|
|
messages.extend(msgs);
|
|
messages.extend(msgs);
|
|
|
|
+ needs_full_rebuild = false;
|
|
}
|
|
}
|
|
Ok(UpdateResult::NeedsRebuild) => {
|
|
Ok(UpdateResult::NeedsRebuild) => {
|
|
- match build_with() {
|
|
|
|
- Ok(res) => {
|
|
|
|
- print_console_info(
|
|
|
|
- &config,
|
|
|
|
- PrettierOptions {
|
|
|
|
- changed: evt.paths,
|
|
|
|
- warnings: res.warnings,
|
|
|
|
- elapsed_time: res.elapsed_time,
|
|
|
|
- },
|
|
|
|
- web_info.clone(),
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- Err(err) => {
|
|
|
|
- log::error!("{}", err);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return;
|
|
|
|
|
|
+ needs_full_rebuild = true;
|
|
}
|
|
}
|
|
Err(err) => {
|
|
Err(err) => {
|
|
log::error!("{}", err);
|
|
log::error!("{}", err);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- for msg in messages {
|
|
|
|
- let _ = hot_reload_tx.send(msg);
|
|
|
|
|
|
+
|
|
|
|
+ if needs_full_rebuild {
|
|
|
|
+ // Reset the file map to the new state of the project
|
|
|
|
+ let FileMapBuildResult {
|
|
|
|
+ map: new_file_map,
|
|
|
|
+ errors,
|
|
|
|
+ } = FileMap::<HtmlCtx>::create(config.crate_dir.clone()).unwrap();
|
|
|
|
+
|
|
|
|
+ for err in errors {
|
|
|
|
+ log::error!("{}", err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ *rsx_file_map = new_file_map;
|
|
|
|
+ } else {
|
|
|
|
+ for msg in messages {
|
|
|
|
+ let _ = hot_reload.messages.send(msg);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ needs_full_rebuild = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if needs_full_rebuild {
|
|
|
|
+ match build_with() {
|
|
|
|
+ Ok(res) => {
|
|
|
|
+ last_update_time = chrono::Local::now().timestamp();
|
|
|
|
+
|
|
|
|
+ #[allow(clippy::redundant_clone)]
|
|
|
|
+ print_console_info(
|
|
|
|
+ &config,
|
|
|
|
+ PrettierOptions {
|
|
|
|
+ changed: e.paths.clone(),
|
|
|
|
+ warnings: res.warnings,
|
|
|
|
+ elapsed_time: res.elapsed_time,
|
|
|
|
+ },
|
|
|
|
+ web_info.clone(),
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ #[cfg(feature = "plugin")]
|
|
|
|
+ let _ = PluginManager::on_serve_rebuild(
|
|
|
|
+ chrono::Local::now().timestamp(),
|
|
|
|
+ e.paths,
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ Err(e) => log::error!("{}", e),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- last_update_time = chrono::Local::now().timestamp();
|
|
|
|
}
|
|
}
|
|
- },
|
|
|
|
- notify::Config::default(),
|
|
|
|
- )
|
|
|
|
|
|
+ }
|
|
|
|
+ })
|
|
.unwrap();
|
|
.unwrap();
|
|
|
|
|
|
for sub_path in allow_watch_path {
|
|
for sub_path in allow_watch_path {
|
|
- if let Err(err) = watcher.watch(
|
|
|
|
- &config.crate_dir.join(&sub_path),
|
|
|
|
- notify::RecursiveMode::Recursive,
|
|
|
|
- ) {
|
|
|
|
- log::error!("error watching {sub_path:?}: \n{}", err);
|
|
|
|
- }
|
|
|
|
|
|
+ watcher
|
|
|
|
+ .watch(
|
|
|
|
+ &config.crate_dir.join(sub_path),
|
|
|
|
+ notify::RecursiveMode::Recursive,
|
|
|
|
+ )
|
|
|
|
+ .unwrap();
|
|
}
|
|
}
|
|
-
|
|
|
|
Ok(watcher)
|
|
Ok(watcher)
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+#[derive(Clone)]
|
|
|
|
+pub struct HotReloadState {
|
|
|
|
+ pub messages: broadcast::Sender<Template<'static>>,
|
|
|
|
+ pub file_map: Arc<Mutex<FileMap<HtmlCtx>>>,
|
|
|
|
+}
|