Răsfoiți Sursa

Merge pull request #839 from Demonthos/hot-reloading-resilient

[Hot reloading] Collect non-fatal errors when searching for files
Jon Kelley 2 ani în urmă
părinte
comite
34e9ab531a

+ 24 - 10
packages/hot-reload/src/lib.rs

@@ -7,7 +7,7 @@ use std::{
 
 use dioxus_core::Template;
 use dioxus_rsx::{
-    hot_reload::{FileMap, UpdateResult},
+    hot_reload::{FileMap, FileMapBuildResult, UpdateResult},
     HotReloadingContext,
 };
 use interprocess::local_socket::{LocalSocketListener, LocalSocketStream};
@@ -141,17 +141,24 @@ pub fn init<Ctx: HotReloadingContext + Send + 'static>(cfg: Config<Ctx>) {
 
         let temp_file = std::env::temp_dir().join("@dioxusin");
         let channels = Arc::new(Mutex::new(Vec::new()));
-        let file_map = Arc::new(Mutex::new(FileMap::<Ctx>::new_with_filter(
-            crate_dir.clone(),
-            |path| {
-                // skip excluded paths
-                excluded_paths.iter().any(|p| path.starts_with(p)) ||
+        let FileMapBuildResult {
+            map: file_map,
+            errors,
+        } = FileMap::<Ctx>::create_with_filter(crate_dir.clone(), |path| {
+            // skip excluded paths
+            excluded_paths.iter().any(|p| path.starts_with(p)) ||
                 // respect .gitignore
                 gitignore
                     .matched_path_or_any_parents(path, path.is_dir())
                     .is_ignore()
-            },
-        )));
+        })
+        .unwrap();
+        for err in errors {
+            if log {
+                println!("hot reloading failed to initialize:\n{err:?}");
+            }
+        }
+        let file_map = Arc::new(Mutex::new(file_map));
 
         if let Ok(local_socket_stream) = LocalSocketListener::bind(temp_file.as_path()) {
             let aborted = Arc::new(Mutex::new(false));
@@ -283,7 +290,7 @@ pub fn init<Ctx: HotReloadingContext + Send + 'static>(cfg: Config<Ctx>) {
                                     .unwrap()
                                     .update_rsx(path, crate_dir.as_path())
                                 {
-                                    UpdateResult::UpdatedRsx(msgs) => {
+                                    Ok(UpdateResult::UpdatedRsx(msgs)) => {
                                         for msg in msgs {
                                             let mut i = 0;
                                             while i < channels.len() {
@@ -299,13 +306,20 @@ pub fn init<Ctx: HotReloadingContext + Send + 'static>(cfg: Config<Ctx>) {
                                             }
                                         }
                                     }
-                                    UpdateResult::NeedsRebuild => {
+                                    Ok(UpdateResult::NeedsRebuild) => {
                                         drop(channels);
                                         if rebuild() {
                                             return;
                                         }
                                         break;
                                     }
+                                    Err(err) => {
+                                        if log {
+                                            println!(
+                                                "hot reloading failed to update rsx:\n{err:?}"
+                                            );
+                                        }
+                                    }
                                 }
                             }
                         }

+ 47 - 17
packages/rsx/src/hot_reload/hot_reloading_file_map.rs

@@ -17,6 +17,14 @@ pub enum UpdateResult {
     NeedsRebuild,
 }
 
+/// The result of building a FileMap
+pub struct FileMapBuildResult<Ctx: HotReloadingContext> {
+    /// The FileMap that was built
+    pub map: FileMap<Ctx>,
+    /// Any errors that occurred while building the FileMap that were not fatal
+    pub errors: Vec<io::Error>,
+}
+
 pub struct FileMap<Ctx: HotReloadingContext> {
     pub map: HashMap<PathBuf, (String, Option<Template<'static>>)>,
     phantom: std::marker::PhantomData<Ctx>,
@@ -24,46 +32,63 @@ pub struct FileMap<Ctx: HotReloadingContext> {
 
 impl<Ctx: HotReloadingContext> FileMap<Ctx> {
     /// Create a new FileMap from a crate directory
-    pub fn new(path: PathBuf) -> Self {
-        Self::new_with_filter(path, |_| false)
+    pub fn create(path: PathBuf) -> io::Result<FileMapBuildResult<Ctx>> {
+        Self::create_with_filter(path, |_| false)
     }
 
     /// Create a new FileMap from a crate directory
-    pub fn new_with_filter(path: PathBuf, mut filter: impl FnMut(&Path) -> bool) -> Self {
+    pub fn create_with_filter(
+        path: PathBuf,
+        mut filter: impl FnMut(&Path) -> bool,
+    ) -> io::Result<FileMapBuildResult<Ctx>> {
+        struct FileMapSearchResult {
+            map: HashMap<PathBuf, (String, Option<Template<'static>>)>,
+            errors: Vec<io::Error>,
+        }
         fn find_rs_files(
             root: PathBuf,
             filter: &mut impl FnMut(&Path) -> bool,
-        ) -> io::Result<HashMap<PathBuf, (String, Option<Template<'static>>)>> {
+        ) -> io::Result<FileMapSearchResult> {
             let mut files = HashMap::new();
+            let mut errors = Vec::new();
             if root.is_dir() {
                 for entry in (fs::read_dir(root)?).flatten() {
                     let path = entry.path();
                     if !filter(&path) {
-                        files.extend(find_rs_files(path, filter)?);
+                        let FileMapSearchResult {
+                            map,
+                            errors: child_errors,
+                        } = find_rs_files(path, filter)?;
+                        errors.extend(child_errors);
+                        files.extend(map);
                     }
                 }
             } else if root.extension().and_then(|s| s.to_str()) == Some("rs") {
                 if let Ok(mut file) = File::open(root.clone()) {
                     let mut src = String::new();
-                    file.read_to_string(&mut src).expect("Unable to read file");
+                    file.read_to_string(&mut src)?;
                     files.insert(root, (src, None));
                 }
             }
-            Ok(files)
+            Ok(FileMapSearchResult { map: files, errors })
         }
 
+        let FileMapSearchResult { map, errors } = find_rs_files(path, &mut filter)?;
         let result = Self {
-            map: find_rs_files(path, &mut filter).unwrap(),
+            map,
             phantom: std::marker::PhantomData,
         };
-        result
+        Ok(FileMapBuildResult {
+            map: result,
+            errors,
+        })
     }
 
     /// Try to update the rsx in a file
-    pub fn update_rsx(&mut self, file_path: &Path, crate_dir: &Path) -> UpdateResult {
-        let mut file = File::open(file_path).unwrap();
+    pub fn update_rsx(&mut self, file_path: &Path, crate_dir: &Path) -> io::Result<UpdateResult> {
+        let mut file = File::open(file_path)?;
         let mut src = String::new();
-        file.read_to_string(&mut src).expect("Unable to read file");
+        file.read_to_string(&mut src)?;
         if let Ok(syntax) = syn::parse_file(&src) {
             if let Some((old_src, template_slot)) = self.map.get_mut(file_path) {
                 if let Ok(old) = syn::parse_file(old_src) {
@@ -99,7 +124,7 @@ impl<Ctx: HotReloadingContext> FileMap<Ctx> {
                                         {
                                             // dioxus cannot handle empty templates
                                             if template.roots.is_empty() {
-                                                return UpdateResult::NeedsRebuild;
+                                                return Ok(UpdateResult::NeedsRebuild);
                                             } else {
                                                 // if the template is the same, don't send it
                                                 if let Some(old_template) = template_slot {
@@ -111,20 +136,25 @@ impl<Ctx: HotReloadingContext> FileMap<Ctx> {
                                                 messages.push(template);
                                             }
                                         } else {
-                                            return UpdateResult::NeedsRebuild;
+                                            return Ok(UpdateResult::NeedsRebuild);
                                         }
                                     }
                                 }
                             }
-                            return UpdateResult::UpdatedRsx(messages);
+                            return Ok(UpdateResult::UpdatedRsx(messages));
                         }
                     }
                 }
             } else {
                 // if this is a new file, rebuild the project
-                *self = FileMap::new(crate_dir.to_path_buf());
+                let FileMapBuildResult { map, mut errors } =
+                    FileMap::create(crate_dir.to_path_buf())?;
+                if let Some(err) = errors.pop() {
+                    return Err(err);
+                }
+                *self = map;
             }
         }
-        UpdateResult::NeedsRebuild
+        Ok(UpdateResult::NeedsRebuild)
     }
 }