Parcourir la source

queue hot reload changes for future clients (#2843)

Evan Almloff il y a 10 mois
Parent
commit
1e03e3946d

+ 13 - 2
packages/cli/src/serve/mod.rs

@@ -96,6 +96,9 @@ pub async fn serve_all(
                     // We're going to kick off a new build, interrupting the current build if it's ongoing
                     builder.build();
 
+                    // Clear the hot reload changes
+                    watcher.clear_hot_reload_changes();
+
                     // Tell the server to show a loading page for any new requests
                     server.start_build().await;
                 }
@@ -105,8 +108,16 @@ pub async fn serve_all(
             msg = server.wait() => {
                 // Run the server in the background
                 // Waiting for updates here lets us tap into when clients are added/removed
-                if let Some(msg) = msg {
-                    screen.new_ws_message(TargetPlatform::Web, msg);
+                match msg {
+                    Some(ServerUpdate::NewConnection) => {
+                        if let Some(msg) = watcher.applied_hot_reload_changes() {
+                            server.send_hotreload(msg).await;
+                        }
+                    }
+                    Some(ServerUpdate::Message(msg)) => {
+                        screen.new_ws_message(TargetPlatform::Web, msg);
+                    }
+                    None => {}
                 }
             }
 

+ 8 - 3
packages/cli/src/serve/server.rs

@@ -44,6 +44,11 @@ use tower_http::{
     ServiceBuilderExt,
 };
 
+pub enum ServerUpdate {
+    NewConnection,
+    Message(Message),
+}
+
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
 #[serde(tag = "type", content = "data")]
 enum Status {
@@ -231,7 +236,7 @@ impl Server {
     }
 
     /// Wait for new clients to be connected and then save them
-    pub async fn wait(&mut self) -> Option<Message> {
+    pub async fn wait(&mut self) -> Option<ServerUpdate> {
         let mut new_hot_reload_socket = self.new_hot_reload_sockets.next();
         let mut new_build_status_socket = self.new_build_status_sockets.next();
         let mut new_message = self
@@ -247,7 +252,7 @@ impl Server {
                 if let Some(new_socket) = new_hot_reload_socket {
                     drop(new_message);
                     self.hot_reload_sockets.push(new_socket);
-                    return None;
+                    return Some(ServerUpdate::NewConnection);
                 } else {
                     panic!("Could not receive a socket - the devtools could not boot - the port is likely already in use");
                 }
@@ -269,7 +274,7 @@ impl Server {
             }
             (idx, message) = next_new_message => {
                 match message {
-                    Some(Ok(message)) => return Some(message),
+                    Some(Ok(message)) => return Some(ServerUpdate::Message(message)),
                     _ => {
                         drop(new_message);
                         _ = self.hot_reload_sockets.remove(idx);

+ 50 - 2
packages/cli/src/serve/watcher.rs

@@ -1,3 +1,4 @@
+use std::collections::{HashMap, HashSet};
 use std::{fs, path::PathBuf, time::Duration};
 
 use crate::serve::hot_reloading_file_map::FileMap;
@@ -24,6 +25,7 @@ pub struct Watcher {
     queued_events: Vec<notify::Event>,
     file_map: FileMap,
     ignore: Gitignore,
+    applied_hot_reload_message: Option<HotReloadMsg>,
 }
 
 impl Watcher {
@@ -132,6 +134,7 @@ impl Watcher {
             ignore,
             queued_events: Vec::new(),
             _last_update_time: chrono::Local::now().timestamp(),
+            applied_hot_reload_message: None,
         }
     }
 
@@ -251,11 +254,56 @@ impl Watcher {
             templates.extend(hotreloaded_templates);
         }
 
-        Some(HotReloadMsg {
+        let msg = HotReloadMsg {
             templates,
             assets,
             unknown_files,
-        })
+        };
+
+        self.add_hot_reload_message(&msg);
+
+        Some(msg)
+    }
+
+    /// Get any hot reload changes that have been applied since the last full rebuild
+    pub fn applied_hot_reload_changes(&mut self) -> Option<HotReloadMsg> {
+        self.applied_hot_reload_message.clone()
+    }
+
+    /// Clear the hot reload changes. This should be called any time a new build is starting
+    pub fn clear_hot_reload_changes(&mut self) {
+        self.applied_hot_reload_message.take();
+    }
+
+    /// Store the hot reload changes for any future clients that connect
+    fn add_hot_reload_message(&mut self, msg: &HotReloadMsg) {
+        match &mut self.applied_hot_reload_message {
+            Some(applied) => {
+                // Merge the assets, unknown files, and templates
+                // We keep the newer change if there is both a old and new change
+                let mut templates: HashMap<String, _> = std::mem::take(&mut applied.templates)
+                    .into_iter()
+                    .map(|template| (template.location.clone(), template))
+                    .collect();
+                let mut assets: HashSet<PathBuf> =
+                    std::mem::take(&mut applied.assets).into_iter().collect();
+                let mut unknown_files: HashSet<PathBuf> =
+                    std::mem::take(&mut applied.unknown_files)
+                        .into_iter()
+                        .collect();
+                for template in &msg.templates {
+                    templates.insert(template.location.clone(), template.clone());
+                }
+                assets.extend(msg.assets.iter().cloned());
+                unknown_files.extend(msg.unknown_files.iter().cloned());
+                applied.templates = templates.into_values().collect();
+                applied.assets = assets.into_iter().collect();
+                applied.unknown_files = unknown_files.into_iter().collect();
+            }
+            None => {
+                self.applied_hot_reload_message = Some(msg.clone());
+            }
+        }
     }
 
     /// Ensure the changes we've received from the queue are actually legit changes to either assets or