mod.rs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  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. if chrono::Local::now().timestamp() > last_update_time {
  30. let mut needs_full_rebuild;
  31. if let Some(hot_reload) = &hot_reload {
  32. // find changes to the rsx in the file
  33. let mut rsx_file_map = hot_reload.file_map.lock().unwrap();
  34. let mut messages: Vec<Template<'static>> = Vec::new();
  35. // In hot reload mode, we only need to rebuild if non-rsx code is changed
  36. needs_full_rebuild = false;
  37. for path in &e.paths {
  38. // if this is not a rust file, rebuild the whole project
  39. if path.extension().and_then(|p| p.to_str()) != Some("rs") {
  40. needs_full_rebuild = true;
  41. break;
  42. }
  43. // Workaround for notify and vscode-like editor:
  44. // when edit & save a file in vscode, there will be two notifications,
  45. // the first one is a file with empty content.
  46. // filter the empty file notification to avoid false rebuild during hot-reload
  47. if let Ok(metadata) = fs::metadata(path) {
  48. if metadata.len() == 0 {
  49. continue;
  50. }
  51. }
  52. match rsx_file_map.update_rsx(path, &config.crate_dir) {
  53. Ok(UpdateResult::UpdatedRsx(msgs)) => {
  54. messages.extend(msgs);
  55. needs_full_rebuild = false;
  56. }
  57. Ok(UpdateResult::NeedsRebuild) => {
  58. needs_full_rebuild = true;
  59. }
  60. Err(err) => {
  61. log::error!("{}", err);
  62. }
  63. }
  64. }
  65. if needs_full_rebuild {
  66. // Reset the file map to the new state of the project
  67. let FileMapBuildResult {
  68. map: new_file_map,
  69. errors,
  70. } = FileMap::<HtmlCtx>::create(config.crate_dir.clone()).unwrap();
  71. for err in errors {
  72. log::error!("{}", err);
  73. }
  74. *rsx_file_map = new_file_map;
  75. } else {
  76. for msg in messages {
  77. let _ = hot_reload.messages.send(msg);
  78. }
  79. }
  80. } else {
  81. needs_full_rebuild = true;
  82. }
  83. if needs_full_rebuild {
  84. match build_with() {
  85. Ok(res) => {
  86. last_update_time = chrono::Local::now().timestamp();
  87. #[allow(clippy::redundant_clone)]
  88. print_console_info(
  89. &config,
  90. PrettierOptions {
  91. changed: e.paths.clone(),
  92. warnings: res.warnings,
  93. elapsed_time: res.elapsed_time,
  94. },
  95. web_info.clone(),
  96. );
  97. #[cfg(feature = "plugin")]
  98. let _ = PluginManager::on_serve_rebuild(
  99. chrono::Local::now().timestamp(),
  100. e.paths,
  101. );
  102. }
  103. Err(e) => log::error!("{}", e),
  104. }
  105. }
  106. }
  107. }
  108. })
  109. .unwrap();
  110. for sub_path in allow_watch_path {
  111. if let Err(err) = watcher.watch(
  112. &config.crate_dir.join(sub_path),
  113. notify::RecursiveMode::Recursive,
  114. ) {
  115. log::error!("Failed to watch path: {}", err);
  116. }
  117. }
  118. Ok(watcher)
  119. }
  120. pub(crate) trait Platform {
  121. fn start(config: &CrateConfig, serve: &ConfigOptsServe) -> Result<Self>
  122. where
  123. Self: Sized;
  124. fn rebuild(&mut self, config: &CrateConfig) -> Result<BuildResult>;
  125. }
  126. #[derive(Clone)]
  127. pub struct HotReloadState {
  128. pub messages: broadcast::Sender<Template<'static>>,
  129. pub file_map: Arc<Mutex<FileMap<HtmlCtx>>>,
  130. }