spawn hot reloading in a seperate thread
This commit is contained in:
1 parent
126ed2f9b8
commit
064bee4a9f
13 files changed
+106
-65
No files matched your search
@@ -13,15 +13,9 @@ dioxus-server = { path = "../../" }
|
|||||||
axum = { version = "0.6.12", optional = true }
|
axum = { version = "0.6.12", optional = true }
|
||||||
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
||||||
serde = "1.0.159"
|
serde = "1.0.159"
|
||||||
|
execute = "0.2.12"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["web"]
|
default = ["web"]
|
||||||
ssr = ["axum", "tokio", "dioxus-server/axum"]
|
ssr = ["axum", "tokio", "dioxus-server/axum"]
|
||||||
web = ["dioxus-web"]
|
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")]
|
#[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();
|
PostServerData::register().unwrap();
|
||||||
GetServerData::register().unwrap();
|
GetServerData::register().unwrap();
|
||||||
tokio::runtime::Runtime::new()
|
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}"
|
"Server said: {text}"
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -16,15 +16,9 @@ tokio = { version = "1.27.0", features = ["full"], optional = true }
|
|||||||
serde = "1.0.159"
|
serde = "1.0.159"
|
||||||
tower-http = { version = "0.4.0", features = ["fs"], optional = true }
|
tower-http = { version = "0.4.0", features = ["fs"], optional = true }
|
||||||
http = { version = "0.2.9", optional = true }
|
http = { version = "0.2.9", optional = true }
|
||||||
|
execute = "0.2.12"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["web"]
|
default = ["web"]
|
||||||
ssr = ["axum", "tokio", "dioxus-server/axum", "tower-http", "http"]
|
ssr = ["axum", "tokio", "dioxus-server/axum", "tower-http", "http"]
|
||||||
web = ["dioxus-web", "dioxus-router/web"]
|
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")]
|
#[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;
|
use axum::extract::State;
|
||||||
PostServerData::register().unwrap();
|
PostServerData::register().unwrap();
|
||||||
GetServerData::register().unwrap();
|
GetServerData::register().unwrap();
|
||||||
|
|||||||
@@ -13,15 +13,9 @@ dioxus-server = { path = "../../" }
|
|||||||
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
||||||
serde = "1.0.159"
|
serde = "1.0.159"
|
||||||
salvo = { version = "0.37.9", optional = true }
|
salvo = { version = "0.37.9", optional = true }
|
||||||
|
execute = "0.2.12"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["web"]
|
default = ["web"]
|
||||||
ssr = ["salvo", "tokio", "dioxus-server/salvo"]
|
ssr = ["salvo", "tokio", "dioxus-server/salvo"]
|
||||||
web = ["dioxus-web"]
|
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")]
|
#[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::*;
|
use salvo::prelude::*;
|
||||||
PostServerData::register().unwrap();
|
PostServerData::register().unwrap();
|
||||||
GetServerData::register().unwrap();
|
GetServerData::register().unwrap();
|
||||||
|
|||||||
@@ -13,15 +13,9 @@ dioxus-server = { path = "../../" }
|
|||||||
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
||||||
serde = "1.0.159"
|
serde = "1.0.159"
|
||||||
warp = { version = "0.3.3", optional = true }
|
warp = { version = "0.3.3", optional = true }
|
||||||
|
execute = "0.2.12"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["web"]
|
default = ["web"]
|
||||||
ssr = ["warp", "tokio", "dioxus-server/warp"]
|
ssr = ["warp", "tokio", "dioxus-server/warp"]
|
||||||
web = ["dioxus-web"]
|
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")]
|
#[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();
|
PostServerData::register().unwrap();
|
||||||
GetServerData::register().unwrap();
|
GetServerData::register().unwrap();
|
||||||
tokio::runtime::Runtime::new()
|
tokio::runtime::Runtime::new()
|
||||||
@@ -45,7 +58,7 @@ fn app(cx: Scope<AppProps>) -> Element {
|
|||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
h1 { "High-Five counter: {count}" }
|
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 |_| count -= 1, "Down low!" }
|
||||||
button {
|
button {
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
|
|||||||
@@ -290,11 +290,7 @@ where
|
|||||||
self.nest(
|
self.nest(
|
||||||
"/_dioxus",
|
"/_dioxus",
|
||||||
Router::new()
|
Router::new()
|
||||||
.route(
|
.route("/hot_reload", get(hot_reload_handler))
|
||||||
"/hot_reload",
|
|
||||||
get(hot_reload_handler)
|
|
||||||
.with_state(crate::hot_reload::HotReloadState::default()),
|
|
||||||
)
|
|
||||||
.route(
|
.route(
|
||||||
"/disconnect",
|
"/disconnect",
|
||||||
get(|ws: WebSocketUpgrade| async {
|
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.
|
/// 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"))]
|
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
||||||
pub async fn hot_reload_handler(
|
pub async fn hot_reload_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
|
||||||
ws: WebSocketUpgrade,
|
|
||||||
State(state): State<crate::hot_reload::HotReloadState>,
|
|
||||||
) -> impl IntoResponse {
|
|
||||||
use axum::extract::ws::Message;
|
use axum::extract::ws::Message;
|
||||||
use futures_util::StreamExt;
|
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");
|
println!("🔥 Hot Reload WebSocket connected");
|
||||||
{
|
{
|
||||||
// update any rsx calls that changed before the websocket connected.
|
// update any rsx calls that changed before the websocket connected.
|
||||||
@@ -448,7 +443,8 @@ pub async fn hot_reload_handler(
|
|||||||
println!("finished");
|
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 {
|
while let Some(change) = rx.next().await {
|
||||||
if let Some(template) = change {
|
if let Some(template) = change {
|
||||||
let template = { serde_json::to_string(&template).unwrap() };
|
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.
|
/// 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"))]
|
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct HotReloadHandler {
|
pub struct HotReloadHandler;
|
||||||
state: crate::hot_reload::HotReloadState,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
||||||
#[handler]
|
#[handler]
|
||||||
@@ -471,10 +469,10 @@ impl HotReloadHandler {
|
|||||||
use salvo::ws::Message;
|
use salvo::ws::Message;
|
||||||
use salvo::ws::WebSocketUpgrade;
|
use salvo::ws::WebSocketUpgrade;
|
||||||
|
|
||||||
let state = self.state.clone();
|
let state = crate::hot_reload::spawn_hot_reload().await;
|
||||||
|
|
||||||
WebSocketUpgrade::new()
|
WebSocketUpgrade::new()
|
||||||
.upgrade(req, res, |mut websocket| async move {
|
.upgrade(req, res, move |mut websocket| async move {
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
|
|
||||||
println!("🔥 Hot Reload WebSocket connected");
|
println!("🔥 Hot Reload WebSocket connected");
|
||||||
@@ -497,8 +495,9 @@ impl HotReloadHandler {
|
|||||||
println!("finished");
|
println!("finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut rx =
|
let mut rx = tokio_stream::wrappers::WatchStream::from_changes(
|
||||||
tokio_stream::wrappers::WatchStream::from_changes(state.message_receiver);
|
state.message_receiver.clone(),
|
||||||
|
);
|
||||||
while let Some(change) = rx.next().await {
|
while let Some(change) = rx.next().await {
|
||||||
if let Some(template) = change {
|
if let Some(template) = change {
|
||||||
let template = { serde_json::to_string(&template).unwrap() };
|
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;
|
use salvo::ws::WebSocketUpgrade;
|
||||||
WebSocketUpgrade::new()
|
WebSocketUpgrade::new()
|
||||||
.upgrade(req, res, |mut ws| async move {
|
.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 {
|
while let Some(msg) = ws.recv().await {
|
||||||
if msg.is_err() {
|
if msg.is_err() {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -372,12 +372,10 @@ pub fn connect_hot_reload() -> impl Filter<Extract = (impl Reply,), Error = warp
|
|||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use warp::ws::Message;
|
use warp::ws::Message;
|
||||||
|
|
||||||
let state = HotReloadState::default();
|
let hot_reload = warp::path!("_dioxus" / "hot_reload")
|
||||||
|
.and(warp::any().then(|| crate::hot_reload::spawn_hot_reload()))
|
||||||
warp::path!("_dioxus" / "hot_reload")
|
|
||||||
.and(warp::ws())
|
.and(warp::ws())
|
||||||
.and(warp::any().map(move || state.clone()))
|
.map(move |state: &'static HotReloadState, ws: warp::ws::Ws| {
|
||||||
.map(move |ws: warp::ws::Ws, state: HotReloadState| {
|
|
||||||
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
||||||
ws.on_upgrade(move |mut websocket| {
|
ws.on_upgrade(move |mut websocket| {
|
||||||
async move {
|
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(
|
let mut rx = tokio_stream::wrappers::WatchStream::from_changes(
|
||||||
state.message_receiver,
|
state.message_receiver.clone(),
|
||||||
);
|
);
|
||||||
while let Some(change) = rx.next().await {
|
while let Some(change) = rx.next().await {
|
||||||
if let Some(template) = change {
|
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(
|
let disconnect =
|
||||||
move |ws: warp::ws::Ws| {
|
warp::path!("_dioxus" / "disconnect")
|
||||||
|
.and(warp::ws())
|
||||||
|
.map(move |ws: warp::ws::Ws| {
|
||||||
|
println!("disconnect");
|
||||||
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
||||||
ws.on_upgrade(move |mut websocket| async move {
|
ws.on_upgrade(move |mut websocket| async move {
|
||||||
struct DisconnectOnDrop(Option<warp::ws::WebSocket>);
|
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));
|
let mut ws = DisconnectOnDrop(Some(websocket));
|
||||||
|
|
||||||
loop {
|
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"))]
|
#[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 () {
|
let disconnect_js = r#"(function () {
|
||||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const url = protocol + '//' + window.location.host + '/_dioxus/disconnect';
|
const url = protocol + '//' + window.location.host + '/_dioxus/disconnect';
|
||||||
@@ -77,10 +78,9 @@ impl SSRState {
|
|||||||
|
|
||||||
// on initial page load connect to the disconnect ws
|
// on initial page load connect to the disconnect ws
|
||||||
const ws = new WebSocket(url);
|
const ws = new WebSocket(url);
|
||||||
ws.onopen = () => {
|
// if we disconnect, start polling
|
||||||
// if we disconnect, start polling
|
ws.onmessage = (m) => {console.log(m)};
|
||||||
ws.onclose = reload_upon_connect;
|
ws.onclose = reload_upon_connect;
|
||||||
};
|
|
||||||
})()"#;
|
})()"#;
|
||||||
|
|
||||||
html += r#"<script>"#;
|
html += r#"<script>"#;
|
||||||
|
|||||||
Reference in new issue
Block a user