mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 14:40:44 +00:00
spawn hot reloading in a seperate thread
This commit is contained in:
parent
126ed2f9b8
commit
064bee4a9f
13 changed files with 106 additions and 65 deletions
|
@ -13,15 +13,9 @@ dioxus-server = { path = "../../" }
|
|||
axum = { version = "0.6.12", optional = true }
|
||||
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
||||
serde = "1.0.159"
|
||||
execute = "0.2.12"
|
||||
|
||||
[features]
|
||||
default = ["web"]
|
||||
ssr = ["axum", "tokio", "dioxus-server/axum"]
|
||||
web = ["dioxus-web"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
panic = "abort"
|
||||
opt-level = 3
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
|
|
|
@ -19,6 +19,19 @@ fn main() {
|
|||
);
|
||||
#[cfg(feature = "ssr")]
|
||||
{
|
||||
// Start hot reloading
|
||||
hot_reload_init!(dioxus_hot_reload::Config::new().with_rebuild_callback(|| {
|
||||
execute::shell("dioxus build --features web")
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
execute::shell("cargo run --features ssr --no-default-features")
|
||||
.spawn()
|
||||
.unwrap();
|
||||
true
|
||||
}));
|
||||
|
||||
PostServerData::register().unwrap();
|
||||
GetServerData::register().unwrap();
|
||||
tokio::runtime::Runtime::new()
|
||||
|
@ -65,7 +78,7 @@ fn app(cx: Scope<AppProps>) -> Element {
|
|||
}
|
||||
}
|
||||
},
|
||||
"Run a server function"
|
||||
"Run a server function! testing1234"
|
||||
}
|
||||
"Server said: {text}"
|
||||
})
|
||||
|
|
|
@ -16,15 +16,9 @@ tokio = { version = "1.27.0", features = ["full"], optional = true }
|
|||
serde = "1.0.159"
|
||||
tower-http = { version = "0.4.0", features = ["fs"], optional = true }
|
||||
http = { version = "0.2.9", optional = true }
|
||||
execute = "0.2.12"
|
||||
|
||||
[features]
|
||||
default = ["web"]
|
||||
ssr = ["axum", "tokio", "dioxus-server/axum", "tower-http", "http"]
|
||||
web = ["dioxus-web", "dioxus-router/web"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
panic = "abort"
|
||||
opt-level = 3
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
|
|
|
@ -20,6 +20,19 @@ fn main() {
|
|||
);
|
||||
#[cfg(feature = "ssr")]
|
||||
{
|
||||
// Start hot reloading
|
||||
hot_reload_init!(dioxus_hot_reload::Config::new().with_rebuild_callback(|| {
|
||||
execute::shell("dioxus build --features web")
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
execute::shell("cargo run --features ssr --no-default-features")
|
||||
.spawn()
|
||||
.unwrap();
|
||||
true
|
||||
}));
|
||||
|
||||
use axum::extract::State;
|
||||
PostServerData::register().unwrap();
|
||||
GetServerData::register().unwrap();
|
||||
|
|
|
@ -13,15 +13,9 @@ dioxus-server = { path = "../../" }
|
|||
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
||||
serde = "1.0.159"
|
||||
salvo = { version = "0.37.9", optional = true }
|
||||
execute = "0.2.12"
|
||||
|
||||
[features]
|
||||
default = ["web"]
|
||||
ssr = ["salvo", "tokio", "dioxus-server/salvo"]
|
||||
web = ["dioxus-web"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
panic = "abort"
|
||||
opt-level = 3
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
|
|
|
@ -19,6 +19,19 @@ fn main() {
|
|||
);
|
||||
#[cfg(feature = "ssr")]
|
||||
{
|
||||
// Start hot reloading
|
||||
hot_reload_init!(dioxus_hot_reload::Config::new().with_rebuild_callback(|| {
|
||||
execute::shell("dioxus build --features web")
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
execute::shell("cargo run --features ssr --no-default-features")
|
||||
.spawn()
|
||||
.unwrap();
|
||||
true
|
||||
}));
|
||||
|
||||
use salvo::prelude::*;
|
||||
PostServerData::register().unwrap();
|
||||
GetServerData::register().unwrap();
|
||||
|
|
|
@ -13,15 +13,9 @@ dioxus-server = { path = "../../" }
|
|||
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
||||
serde = "1.0.159"
|
||||
warp = { version = "0.3.3", optional = true }
|
||||
execute = "0.2.12"
|
||||
|
||||
[features]
|
||||
default = ["web"]
|
||||
ssr = ["warp", "tokio", "dioxus-server/warp"]
|
||||
web = ["dioxus-web"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
panic = "abort"
|
||||
opt-level = 3
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
|
|
|
@ -19,6 +19,19 @@ fn main() {
|
|||
);
|
||||
#[cfg(feature = "ssr")]
|
||||
{
|
||||
// Start hot reloading
|
||||
hot_reload_init!(dioxus_hot_reload::Config::new().with_rebuild_callback(|| {
|
||||
execute::shell("dioxus build --features web")
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
execute::shell("cargo run --features ssr --no-default-features")
|
||||
.spawn()
|
||||
.unwrap();
|
||||
true
|
||||
}));
|
||||
|
||||
PostServerData::register().unwrap();
|
||||
GetServerData::register().unwrap();
|
||||
tokio::runtime::Runtime::new()
|
||||
|
@ -45,7 +58,7 @@ fn app(cx: Scope<AppProps>) -> Element {
|
|||
|
||||
cx.render(rsx! {
|
||||
h1 { "High-Five counter: {count}" }
|
||||
button { onclick: move |_| count += 1, "Up high!" }
|
||||
button { onclick: move |_| count += 10, "Up high!" }
|
||||
button { onclick: move |_| count -= 1, "Down low!" }
|
||||
button {
|
||||
onclick: move |_| {
|
||||
|
|
|
@ -290,11 +290,7 @@ where
|
|||
self.nest(
|
||||
"/_dioxus",
|
||||
Router::new()
|
||||
.route(
|
||||
"/hot_reload",
|
||||
get(hot_reload_handler)
|
||||
.with_state(crate::hot_reload::HotReloadState::default()),
|
||||
)
|
||||
.route("/hot_reload", get(hot_reload_handler))
|
||||
.route(
|
||||
"/disconnect",
|
||||
get(|ws: WebSocketUpgrade| async {
|
||||
|
@ -420,14 +416,13 @@ fn report_err<E: Error>(e: E) -> Response<BoxBody> {
|
|||
|
||||
/// A handler for Dioxus web hot reload websocket. This will send the updated static parts of the RSX to the client when they change.
|
||||
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
||||
pub async fn hot_reload_handler(
|
||||
ws: WebSocketUpgrade,
|
||||
State(state): State<crate::hot_reload::HotReloadState>,
|
||||
) -> impl IntoResponse {
|
||||
pub async fn hot_reload_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
|
||||
use axum::extract::ws::Message;
|
||||
use futures_util::StreamExt;
|
||||
|
||||
ws.on_upgrade(|mut socket| async move {
|
||||
let state = crate::hot_reload::spawn_hot_reload().await;
|
||||
|
||||
ws.on_upgrade(move |mut socket| async move {
|
||||
println!("🔥 Hot Reload WebSocket connected");
|
||||
{
|
||||
// update any rsx calls that changed before the websocket connected.
|
||||
|
@ -448,7 +443,8 @@ pub async fn hot_reload_handler(
|
|||
println!("finished");
|
||||
}
|
||||
|
||||
let mut rx = tokio_stream::wrappers::WatchStream::from_changes(state.message_receiver);
|
||||
let mut rx =
|
||||
tokio_stream::wrappers::WatchStream::from_changes(state.message_receiver.clone());
|
||||
while let Some(change) = rx.next().await {
|
||||
if let Some(template) = change {
|
||||
let template = { serde_json::to_string(&template).unwrap() };
|
||||
|
|
|
@ -455,9 +455,7 @@ impl HotReloadHandler {
|
|||
/// A handler for Dioxus web hot reload websocket. This will send the updated static parts of the RSX to the client when they change.
|
||||
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
||||
#[derive(Default)]
|
||||
pub struct HotReloadHandler {
|
||||
state: crate::hot_reload::HotReloadState,
|
||||
}
|
||||
pub struct HotReloadHandler;
|
||||
|
||||
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
||||
#[handler]
|
||||
|
@ -471,10 +469,10 @@ impl HotReloadHandler {
|
|||
use salvo::ws::Message;
|
||||
use salvo::ws::WebSocketUpgrade;
|
||||
|
||||
let state = self.state.clone();
|
||||
let state = crate::hot_reload::spawn_hot_reload().await;
|
||||
|
||||
WebSocketUpgrade::new()
|
||||
.upgrade(req, res, |mut websocket| async move {
|
||||
.upgrade(req, res, move |mut websocket| async move {
|
||||
use futures_util::StreamExt;
|
||||
|
||||
println!("🔥 Hot Reload WebSocket connected");
|
||||
|
@ -497,8 +495,9 @@ impl HotReloadHandler {
|
|||
println!("finished");
|
||||
}
|
||||
|
||||
let mut rx =
|
||||
tokio_stream::wrappers::WatchStream::from_changes(state.message_receiver);
|
||||
let mut rx = tokio_stream::wrappers::WatchStream::from_changes(
|
||||
state.message_receiver.clone(),
|
||||
);
|
||||
while let Some(change) = rx.next().await {
|
||||
if let Some(template) = change {
|
||||
let template = { serde_json::to_string(&template).unwrap() };
|
||||
|
@ -518,7 +517,7 @@ async fn ignore_ws(req: &mut Request, res: &mut Response) -> Result<(), salvo::h
|
|||
use salvo::ws::WebSocketUpgrade;
|
||||
WebSocketUpgrade::new()
|
||||
.upgrade(req, res, |mut ws| async move {
|
||||
let _ = dbg!(ws.send(salvo::ws::Message::text("connected")).await);
|
||||
let _ = ws.send(salvo::ws::Message::text("connected")).await;
|
||||
while let Some(msg) = ws.recv().await {
|
||||
if msg.is_err() {
|
||||
return;
|
||||
|
|
|
@ -372,12 +372,10 @@ pub fn connect_hot_reload() -> impl Filter<Extract = (impl Reply,), Error = warp
|
|||
use futures_util::StreamExt;
|
||||
use warp::ws::Message;
|
||||
|
||||
let state = HotReloadState::default();
|
||||
|
||||
warp::path!("_dioxus" / "hot_reload")
|
||||
let hot_reload = warp::path!("_dioxus" / "hot_reload")
|
||||
.and(warp::any().then(|| crate::hot_reload::spawn_hot_reload()))
|
||||
.and(warp::ws())
|
||||
.and(warp::any().map(move || state.clone()))
|
||||
.map(move |ws: warp::ws::Ws, state: HotReloadState| {
|
||||
.map(move |state: &'static HotReloadState, ws: warp::ws::Ws| {
|
||||
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
||||
ws.on_upgrade(move |mut websocket| {
|
||||
async move {
|
||||
|
@ -404,7 +402,7 @@ pub fn connect_hot_reload() -> impl Filter<Extract = (impl Reply,), Error = warp
|
|||
}
|
||||
|
||||
let mut rx = tokio_stream::wrappers::WatchStream::from_changes(
|
||||
state.message_receiver,
|
||||
state.message_receiver.clone(),
|
||||
);
|
||||
while let Some(change) = rx.next().await {
|
||||
if let Some(template) = change {
|
||||
|
@ -416,9 +414,12 @@ pub fn connect_hot_reload() -> impl Filter<Extract = (impl Reply,), Error = warp
|
|||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.or(warp::path!("_dioxus" / "disconnect").and(warp::ws()).map(
|
||||
move |ws: warp::ws::Ws| {
|
||||
});
|
||||
let disconnect =
|
||||
warp::path!("_dioxus" / "disconnect")
|
||||
.and(warp::ws())
|
||||
.map(move |ws: warp::ws::Ws| {
|
||||
println!("disconnect");
|
||||
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
||||
ws.on_upgrade(move |mut websocket| async move {
|
||||
struct DisconnectOnDrop(Option<warp::ws::WebSocket>);
|
||||
|
@ -432,10 +433,12 @@ pub fn connect_hot_reload() -> impl Filter<Extract = (impl Reply,), Error = warp
|
|||
let mut ws = DisconnectOnDrop(Some(websocket));
|
||||
|
||||
loop {
|
||||
ws.0.as_mut().unwrap().next().await;
|
||||
if ws.0.as_mut().unwrap().next().await.is_none() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
))
|
||||
});
|
||||
disconnect.or(hot_reload)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,3 +44,18 @@ impl Default for HotReloadState {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hot reloading can be expensive to start so we spawn a new thread
|
||||
static HOT_RELOAD_STATE: tokio::sync::OnceCell<HotReloadState> = tokio::sync::OnceCell::const_new();
|
||||
pub(crate) async fn spawn_hot_reload() -> &'static HotReloadState {
|
||||
HOT_RELOAD_STATE
|
||||
.get_or_init(|| async {
|
||||
println!("spinning up hot reloading");
|
||||
let r = tokio::task::spawn_blocking(HotReloadState::default)
|
||||
.await
|
||||
.unwrap();
|
||||
println!("hot reloading ready");
|
||||
r
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ impl SSRState {
|
|||
|
||||
#[cfg(all(debug_assertions, feature = "hot-reload"))]
|
||||
{
|
||||
// In debug mode, we need to add a script to the page that will reload the page if the websocket disconnects to make full recompile hot reloads work
|
||||
let disconnect_js = r#"(function () {
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const url = protocol + '//' + window.location.host + '/_dioxus/disconnect';
|
||||
|
@ -77,10 +78,9 @@ impl SSRState {
|
|||
|
||||
// on initial page load connect to the disconnect ws
|
||||
const ws = new WebSocket(url);
|
||||
ws.onopen = () => {
|
||||
// if we disconnect, start polling
|
||||
ws.onclose = reload_upon_connect;
|
||||
};
|
||||
// if we disconnect, start polling
|
||||
ws.onmessage = (m) => {console.log(m)};
|
||||
ws.onclose = reload_upon_connect;
|
||||
})()"#;
|
||||
|
||||
html += r#"<script>"#;
|
||||
|
|
Loading…
Reference in a new issue