mod.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. use crate::{BuildResult, CrateConfig, Result};
  2. use cargo_metadata::diagnostic::Diagnostic;
  3. use dioxus_core::Template;
  4. use dioxus_html::HtmlCtx;
  5. use dioxus_rsx::hot_reload::*;
  6. use notify::{RecommendedWatcher, Watcher};
  7. use std::{
  8. path::PathBuf,
  9. sync::{Arc, Mutex},
  10. };
  11. use tokio::sync::broadcast::{self};
  12. mod output;
  13. use output::*;
  14. pub mod desktop;
  15. pub mod web;
  16. /// Sets up a file watcher
  17. async fn setup_file_watcher<F: Fn() -> Result<BuildResult> + Send + 'static>(
  18. build_with: F,
  19. config: &CrateConfig,
  20. web_info: Option<WebServerInfo>,
  21. hot_reload: Option<HotReloadState>,
  22. ) -> Result<RecommendedWatcher> {
  23. let mut last_update_time = chrono::Local::now().timestamp();
  24. // file watcher: check file change
  25. let allow_watch_path = config
  26. .dioxus_config
  27. .web
  28. .watcher
  29. .watch_path
  30. .clone()
  31. .unwrap_or_else(|| vec![PathBuf::from("src"), PathBuf::from("examples")]);
  32. let watcher_config = config.clone();
  33. let mut watcher = notify::recommended_watcher(move |info: notify::Result<notify::Event>| {
  34. let config = watcher_config.clone();
  35. if let Ok(e) = info {
  36. if chrono::Local::now().timestamp() > last_update_time {
  37. let mut needs_full_rebuild;
  38. if let Some(hot_reload) = &hot_reload {
  39. // find changes to the rsx in the file
  40. let mut rsx_file_map = hot_reload.file_map.lock().unwrap();
  41. let mut messages: Vec<Template<'static>> = Vec::new();
  42. // In hot reload mode, we only need to rebuild if non-rsx code is changed
  43. needs_full_rebuild = false;
  44. for path in &e.paths {
  45. // if this is not a rust file, rebuild the whole project
  46. if path.extension().and_then(|p| p.to_str()) != Some("rs") {
  47. needs_full_rebuild = true;
  48. break;
  49. }
  50. // Workaround for notify and vscode-like editor:
  51. // when edit & save a file in vscode, there will be two notifications,
  52. // the first one is a file with empty content.
  53. // filter the empty file notification to avoid false rebuild during hot-reload
  54. if let Ok(metadata) = fs::metadata(path) {
  55. if metadata.len() == 0 {
  56. continue;
  57. }
  58. }
  59. match rsx_file_map.update_rsx(path, &config.crate_dir) {
  60. Ok(UpdateResult::UpdatedRsx(msgs)) => {
  61. messages.extend(msgs);
  62. needs_full_rebuild = false;
  63. }
  64. Ok(UpdateResult::NeedsRebuild) => {
  65. needs_full_rebuild = true;
  66. }
  67. Err(err) => {
  68. log::error!("{}", err);
  69. }
  70. }
  71. }
  72. if needs_full_rebuild {
  73. // Reset the file map to the new state of the project
  74. let FileMapBuildResult {
  75. map: new_file_map,
  76. errors,
  77. } = FileMap::<HtmlCtx>::create(config.crate_dir.clone()).unwrap();
  78. for err in errors {
  79. log::error!("{}", err);
  80. }
  81. *rsx_file_map = new_file_map;
  82. } else {
  83. for msg in messages {
  84. let _ = hot_reload.messages.send(msg);
  85. }
  86. }
  87. } else {
  88. needs_full_rebuild = true;
  89. }
  90. if needs_full_rebuild {
  91. match build_with() {
  92. Ok(res) => {
  93. last_update_time = chrono::Local::now().timestamp();
  94. #[allow(clippy::redundant_clone)]
  95. print_console_info(
  96. &config,
  97. PrettierOptions {
  98. changed: e.paths.clone(),
  99. warnings: res.warnings,
  100. elapsed_time: res.elapsed_time,
  101. },
  102. web_info.clone(),
  103. );
  104. #[cfg(feature = "plugin")]
  105. let _ = PluginManager::on_serve_rebuild(
  106. chrono::Local::now().timestamp(),
  107. e.paths,
  108. );
  109. }
  110. Err(e) => log::error!("{}", e),
  111. }
  112. }
  113. }
  114. }
  115. })
  116. .unwrap();
  117. for sub_path in allow_watch_path {
  118. if let Err(err) = watcher.watch(
  119. &config.crate_dir.join(sub_path),
  120. notify::RecursiveMode::Recursive,
  121. ) {
  122. log::error!("Failed to watch path: {}", err);
  123. }
  124. }
  125. Ok(watcher)
  126. }
  127. #[derive(Clone)]
  128. pub struct HotReloadState {
  129. pub messages: broadcast::Sender<Template<'static>>,
  130. pub file_map: Arc<Mutex<FileMap<HtmlCtx>>>,
  131. }