ソースを参照

create websocket for hot reloading

Evan Almloff 3 年 前
コミット
e7cf382784
2 ファイル変更69 行追加0 行削除
  1. 3 0
      packages/web/Cargo.toml
  2. 66 0
      packages/web/src/lib.rs

+ 3 - 0
packages/web/Cargo.toml

@@ -16,6 +16,7 @@ dioxus-html = { path = "../html", version = "^0.2.1", features = ["wasm-bind"] }
 dioxus-interpreter-js = { path = "../interpreter", version = "^0.2.1", features = [
 dioxus-interpreter-js = { path = "../interpreter", version = "^0.2.1", features = [
     "web"
     "web"
 ] }
 ] }
+dioxus-rsx-interpreter = { path = "../rsx_interpreter", version = "*", optional = true }
 
 
 js-sys = "0.3.56"
 js-sys = "0.3.56"
 wasm-bindgen = { version = "0.2.79", features = ["enable-interning"] }
 wasm-bindgen = { version = "0.2.79", features = ["enable-interning"] }
@@ -30,6 +31,7 @@ futures-util = "0.3.19"
 smallstr = "0.2.0"
 smallstr = "0.2.0"
 serde-wasm-bindgen = "0.4.2"
 serde-wasm-bindgen = "0.4.2"
 futures-channel = "0.3.21"
 futures-channel = "0.3.21"
+serde_json = { version = "1.0", optional = true }
 
 
 [dependencies.web-sys]
 [dependencies.web-sys]
 version = "0.3.56"
 version = "0.3.56"
@@ -75,6 +77,7 @@ features = [
 [features]
 [features]
 default = ["panic_hook"]
 default = ["panic_hook"]
 panic_hook = ["console_error_panic_hook"]
 panic_hook = ["console_error_panic_hook"]
+hot_reload = ["dioxus-rsx-interpreter", "web-sys/WebSocket", "web-sys/Location", "web-sys/MessageEvent", "serde_json"]
 
 
 [dev-dependencies]
 [dev-dependencies]
 dioxus-core-macro = { path = "../core-macro" }
 dioxus-core-macro = { path = "../core-macro" }

+ 66 - 0
packages/web/src/lib.rs

@@ -217,6 +217,72 @@ pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T
 
 
     let mut work_loop = ric_raf::RafLoop::new();
     let mut work_loop = ric_raf::RafLoop::new();
 
 
+    #[cfg(feature = "hot_reload")]
+    {
+        use dioxus_rsx_interpreter::error::Error;
+        use dioxus_rsx_interpreter::{ErrorHandler, SetRsxMessage, RSX_CONTEXT};
+        use futures_channel::mpsc::unbounded;
+        use futures_channel::mpsc::UnboundedSender;
+        use futures_util::StreamExt;
+        use wasm_bindgen::closure::Closure;
+        use wasm_bindgen::JsCast;
+        use web_sys::{MessageEvent, WebSocket};
+
+        let window = web_sys::window().unwrap();
+
+        let protocol = if window.location().protocol().unwrap() == "https:" {
+            "wss:"
+        } else {
+            "ws:"
+        };
+
+        let url = format!(
+            "{protocol} //{}/_dioxus/hot_reload",
+            window.location().host().unwrap()
+        );
+
+        let ws = WebSocket::new(&url).unwrap();
+
+        // change the rsx when new data is received
+        let cl = Closure::wrap(Box::new(|e: MessageEvent| {
+            if let Ok(text) = e.data().dyn_into::<js_sys::JsString>() {
+                let msg: SetRsxMessage = serde_json::from_str(&format!("{text}")).unwrap();
+                RSX_CONTEXT.insert(msg.location, msg.new_text);
+            }
+        }) as Box<dyn FnMut(MessageEvent)>);
+
+        ws.set_onmessage(Some(cl.as_ref().unchecked_ref()));
+
+        let (error_channel_sender, error_channel_receiver) = unbounded();
+
+        struct WebErrorHandler {
+            sender: UnboundedSender<Error>,
+        }
+
+        impl ErrorHandler for WebErrorHandler {
+            fn handle_error(&self, err: dioxus_rsx_interpreter::error::Error) {
+                self.sender.unbounded_send(err).unwrap();
+            }
+        }
+
+        RSX_CONTEXT.set_error_handler(WebErrorHandler {
+            sender: error_channel_sender,
+        });
+
+        // forward stream to the websocket
+        dom.base_scope().spawn_forever(async move {
+            error_channel_receiver
+                .for_each(|err| {
+                    if let Error::RecompileRequiredError(err) = err {
+                        ws.send_with_str(serde_json::to_string(&err).unwrap().as_str())
+                            .unwrap();
+                    }
+                    futures_util::future::ready(())
+                })
+                .await;
+        });
+    }
+
     loop {
     loop {
         log::trace!("waiting for work");
         log::trace!("waiting for work");
         // if virtualdom has nothing, wait for it to have something before requesting idle time
         // if virtualdom has nothing, wait for it to have something before requesting idle time