mod.rs 6.6 KB

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