From e7cf38278428307967d1df0aacb4fb22ea96fc03 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 6 Jun 2022 13:30:32 -0500 Subject: [PATCH] create websocket for hot reloading --- packages/web/Cargo.toml | 3 ++ packages/web/src/lib.rs | 66 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index a3001a6b2..9ebe2989b 100644 --- a/packages/web/Cargo.toml +++ b/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 = [ "web" ] } +dioxus-rsx-interpreter = { path = "../rsx_interpreter", version = "*", optional = true } js-sys = "0.3.56" wasm-bindgen = { version = "0.2.79", features = ["enable-interning"] } @@ -30,6 +31,7 @@ futures-util = "0.3.19" smallstr = "0.2.0" serde-wasm-bindgen = "0.4.2" futures-channel = "0.3.21" +serde_json = { version = "1.0", optional = true } [dependencies.web-sys] version = "0.3.56" @@ -75,6 +77,7 @@ features = [ [features] default = ["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] dioxus-core-macro = { path = "../core-macro" } diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index d66d8bc3f..3caac1b86 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -217,6 +217,72 @@ pub async fn run_with_props(root: Component, root_props: T 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::() { + let msg: SetRsxMessage = serde_json::from_str(&format!("{text}")).unwrap(); + RSX_CONTEXT.insert(msg.location, msg.new_text); + } + }) as Box); + + ws.set_onmessage(Some(cl.as_ref().unchecked_ref())); + + let (error_channel_sender, error_channel_receiver) = unbounded(); + + struct WebErrorHandler { + sender: UnboundedSender, + } + + 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 { log::trace!("waiting for work"); // if virtualdom has nothing, wait for it to have something before requesting idle time