Create a Static Site Generation platform; Deduplicate hot reloading code (#2226)
* create static site generation helpers in the router crate * work on integrating static site generation into fullstack * move ssg into a separate crate * integrate ssg with the launch builder * simplify ssg example * fix static_routes for child routes * move CLI hot reloading websocket code into dioxus-hot-reload * fix some unused imports * use the same hot reloading websocket code for fullstack * fix fullstack hot reloading * move cli hot reloading logic into the hot reload crate * ssg example working with dx serve * add more examples * fix clippy * fix formatting * fix hot reload doctest imports * fix axum imports * don't run server doc tests * Fix hot reload websocket doc examples
This commit is contained in:
1 parent
460b70e0f0
commit
245003a5d4
59 files changed
+1507
-791
No files matched your search
@@ -1,36 +0,0 @@
|
||||
// Dioxus-CLI
|
||||
// https://github.com/DioxusLabs/dioxus/tree/master/packages/cli
|
||||
// NOTE: This is also used in the fullstack package at ../packages/fullstack/src/render.rs, if you make changes here, make sure to update the version in there as well
|
||||
// TODO: Extract hot reloading with axum into a separate crate or use the fullstack hot reloading Axum extension trait here
|
||||
|
||||
(function () {
|
||||
var protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||
var url = protocol + "//" + window.location.host + "/_dioxus/ws";
|
||||
var poll_interval = 8080;
|
||||
|
||||
var reload_upon_connect = (event) => {
|
||||
// Firefox will send a 1001 code when the connection is closed because the page is reloaded
|
||||
// Only firefox will trigger the onclose event when the page is reloaded manually: https://stackoverflow.com/questions/10965720/should-websocket-onclose-be-triggered-by-user-navigation-or-refresh
|
||||
// We should not reload the page in this case
|
||||
if (event.code === 1001) {
|
||||
return;
|
||||
}
|
||||
window.setTimeout(() => {
|
||||
var ws = new WebSocket(url);
|
||||
ws.onopen = () => window.location.reload();
|
||||
ws.onclose = reload_upon_connect;
|
||||
}, poll_interval);
|
||||
};
|
||||
|
||||
var ws = new WebSocket(url);
|
||||
|
||||
ws.onmessage = (ev) => {
|
||||
console.log("Received message: ", ev, ev.data);
|
||||
|
||||
if (ev.data == "reload") {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = reload_upon_connect;
|
||||
})();
|
||||
@@ -564,10 +564,7 @@ pub fn gen_page(config: &CrateConfig, manifest: Option<&AssetManifest>, serve: b
|
||||
replace_or_insert_before("{script_include}", &script_str, "</body", &mut html);
|
||||
|
||||
if serve {
|
||||
html += &format!(
|
||||
"<script>{}</script>",
|
||||
include_str!("./assets/autoreload.js")
|
||||
);
|
||||
html += &format!("<script>{}</script>", dioxus_hot_reload::RECONNECT_SCRIPT);
|
||||
}
|
||||
|
||||
let base_path = match &config.dioxus_config.web.app.base_path {
|
||||
|
||||
@@ -65,7 +65,7 @@ impl Build {
|
||||
// argument is explicitly set to `None`.
|
||||
crate::builder::build_desktop(&crate_config, false, self.build.skip_assets, None)?
|
||||
}
|
||||
Platform::Fullstack => {
|
||||
Platform::Fullstack | Platform::StaticGeneration => {
|
||||
// Fullstack mode must be built with web configs on the desktop
|
||||
// (server) binary as well as the web binary
|
||||
let _config = AssetConfigDropGuard::new();
|
||||
|
||||
@@ -3,7 +3,7 @@ use manganis_cli_support::AssetManifest;
|
||||
|
||||
use super::*;
|
||||
use cargo_toml::Dependency::{Detailed, Inherited, Simple};
|
||||
use std::{fs::create_dir_all, io::Write, path::PathBuf};
|
||||
use std::fs::create_dir_all;
|
||||
|
||||
/// Run the WASM project on dev-server
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
@@ -18,7 +18,7 @@ impl Serve {
|
||||
let mut crate_config = dioxus_cli_config::CrateConfig::new(bin)?;
|
||||
let serve_cfg = self.serve.clone();
|
||||
|
||||
// change the relase state.
|
||||
// change the release state.
|
||||
let hot_reload = self.serve.hot_reload || crate_config.dioxus_config.application.hot_reload;
|
||||
crate_config.with_hot_reload(hot_reload);
|
||||
crate_config.with_cross_origin_policy(self.serve.cross_origin_policy);
|
||||
@@ -67,7 +67,9 @@ impl Serve {
|
||||
match platform {
|
||||
Platform::Web => web::startup(crate_config.clone(), &serve_cfg).await?,
|
||||
Platform::Desktop => desktop::startup(crate_config.clone(), &serve_cfg).await?,
|
||||
Platform::Fullstack => fullstack::startup(crate_config.clone(), &serve_cfg).await?,
|
||||
Platform::Fullstack | Platform::StaticGeneration => {
|
||||
fullstack::startup(crate_config.clone(), &serve_cfg).await?
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::server::SharedFileMap;
|
||||
use crate::{
|
||||
cfg::ConfigOptsServe,
|
||||
server::{
|
||||
@@ -16,7 +17,6 @@ use std::{
|
||||
process::{Child, Command},
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
use tokio::sync::broadcast::{self};
|
||||
|
||||
#[cfg(feature = "plugin")]
|
||||
use crate::plugin::PluginManager;
|
||||
@@ -33,7 +33,7 @@ pub(crate) async fn startup_with_platform<P: Platform + Send + 'static>(
|
||||
) -> Result<()> {
|
||||
set_ctrl_c(&config);
|
||||
|
||||
let hot_reload_state = match config.hot_reload {
|
||||
let file_map = match config.hot_reload {
|
||||
true => {
|
||||
let FileMapBuildResult { map, errors } =
|
||||
FileMap::<HtmlCtx>::create(config.crate_dir.clone()).unwrap();
|
||||
@@ -44,16 +44,16 @@ pub(crate) async fn startup_with_platform<P: Platform + Send + 'static>(
|
||||
|
||||
let file_map = Arc::new(Mutex::new(map));
|
||||
|
||||
let hot_reload_tx = broadcast::channel(100).0;
|
||||
|
||||
Some(HotReloadState {
|
||||
messages: hot_reload_tx.clone(),
|
||||
file_map: file_map.clone(),
|
||||
})
|
||||
Some(file_map.clone())
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
|
||||
let hot_reload_state = HotReloadState {
|
||||
receiver: Default::default(),
|
||||
file_map,
|
||||
};
|
||||
|
||||
serve::<P>(config, serve_cfg, hot_reload_state).await?;
|
||||
|
||||
Ok(())
|
||||
@@ -73,15 +73,15 @@ fn set_ctrl_c(config: &CrateConfig) {
|
||||
async fn serve<P: Platform + Send + 'static>(
|
||||
config: CrateConfig,
|
||||
serve: &ConfigOptsServe,
|
||||
hot_reload_state: Option<HotReloadState>,
|
||||
hot_reload_state: HotReloadState,
|
||||
) -> Result<()> {
|
||||
let hot_reload: tokio::task::JoinHandle<Result<()>> = tokio::spawn({
|
||||
let hot_reload_state = hot_reload_state.clone();
|
||||
async move {
|
||||
match hot_reload_state {
|
||||
Some(hot_reload_state) => {
|
||||
match hot_reload_state.file_map.clone() {
|
||||
Some(file_map) => {
|
||||
// The open interprocess sockets
|
||||
start_desktop_hot_reload(hot_reload_state).await?;
|
||||
start_desktop_hot_reload(hot_reload_state, file_map).await?;
|
||||
}
|
||||
None => {
|
||||
std::future::pending::<()>().await;
|
||||
@@ -113,7 +113,10 @@ async fn serve<P: Platform + Send + 'static>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<()> {
|
||||
async fn start_desktop_hot_reload(
|
||||
hot_reload_state: HotReloadState,
|
||||
file_map: SharedFileMap,
|
||||
) -> Result<()> {
|
||||
let metadata = cargo_metadata::MetadataCommand::new()
|
||||
.no_deps()
|
||||
.exec()
|
||||
@@ -137,7 +140,6 @@ async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<()
|
||||
|
||||
// listen for connections
|
||||
std::thread::spawn({
|
||||
let file_map = hot_reload_state.file_map.clone();
|
||||
let channels = channels.clone();
|
||||
let aborted = aborted.clone();
|
||||
move || {
|
||||
@@ -184,7 +186,7 @@ async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<()
|
||||
}
|
||||
});
|
||||
|
||||
let mut hot_reload_rx = hot_reload_state.messages.subscribe();
|
||||
let mut hot_reload_rx = hot_reload_state.receiver.subscribe();
|
||||
|
||||
while let Ok(msg) = hot_reload_rx.recv().await {
|
||||
let channels = &mut *channels.lock().unwrap();
|
||||
|
||||
@@ -2,14 +2,12 @@ use crate::{cfg::ConfigOptsServe, BuildResult, Result};
|
||||
use dioxus_cli_config::CrateConfig;
|
||||
|
||||
use cargo_metadata::diagnostic::Diagnostic;
|
||||
use dioxus_core::Template;
|
||||
use dioxus_hot_reload::HotReloadMsg;
|
||||
use dioxus_hot_reload::{HotReloadMsg, HotReloadReceiver};
|
||||
use dioxus_html::HtmlCtx;
|
||||
use dioxus_rsx::hot_reload::*;
|
||||
use fs_extra::dir::CopyOptions;
|
||||
use notify::{RecommendedWatcher, Watcher};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use tokio::sync::broadcast::{self};
|
||||
|
||||
mod output;
|
||||
use output::*;
|
||||
@@ -19,25 +17,14 @@ pub mod web;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HotReloadState {
|
||||
/// Pending hotreload updates to be sent to all connected clients
|
||||
pub messages: broadcast::Sender<HotReloadMsg>,
|
||||
/// The receiver for hot reload messages
|
||||
pub receiver: HotReloadReceiver,
|
||||
|
||||
/// The file map that tracks the state of the projecta
|
||||
pub file_map: SharedFileMap,
|
||||
pub file_map: Option<SharedFileMap>,
|
||||
}
|
||||
type SharedFileMap = Arc<Mutex<FileMap<HtmlCtx>>>;
|
||||
|
||||
impl HotReloadState {
|
||||
pub fn all_templates(&self) -> Vec<Template> {
|
||||
self.file_map
|
||||
.lock()
|
||||
.unwrap()
|
||||
.map
|
||||
.values()
|
||||
.flat_map(|v| v.templates.values().copied())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
type SharedFileMap = Arc<Mutex<FileMap<HtmlCtx>>>;
|
||||
|
||||
/// Sets up a file watcher.
|
||||
///
|
||||
@@ -46,7 +33,7 @@ async fn setup_file_watcher<F: Fn() -> Result<BuildResult> + Send + 'static>(
|
||||
build_with: F,
|
||||
config: &CrateConfig,
|
||||
web_info: Option<WebServerInfo>,
|
||||
hot_reload: Option<HotReloadState>,
|
||||
hot_reload: HotReloadState,
|
||||
) -> Result<RecommendedWatcher> {
|
||||
let mut last_update_time = chrono::Local::now().timestamp();
|
||||
|
||||
@@ -96,7 +83,7 @@ async fn setup_file_watcher<F: Fn() -> Result<BuildResult> + Send + 'static>(
|
||||
fn watch_event<F>(
|
||||
event: notify::Event,
|
||||
last_update_time: &mut i64,
|
||||
hot_reload: &Option<HotReloadState>,
|
||||
hot_reload: &HotReloadState,
|
||||
config: &CrateConfig,
|
||||
build_with: &F,
|
||||
web_info: &Option<WebServerInfo>,
|
||||
@@ -119,8 +106,14 @@ fn watch_event<F>(
|
||||
// By default we want to not do a full rebuild, and instead let the hot reload system invalidate it
|
||||
let mut needs_full_rebuild = false;
|
||||
|
||||
if let Some(hot_reload) = &hot_reload {
|
||||
hotreload_files(hot_reload, &mut needs_full_rebuild, &event, config);
|
||||
if let Some(file_map) = &hot_reload.file_map {
|
||||
hotreload_files(
|
||||
hot_reload,
|
||||
file_map,
|
||||
&mut needs_full_rebuild,
|
||||
&event,
|
||||
config,
|
||||
);
|
||||
}
|
||||
|
||||
if needs_full_rebuild {
|
||||
@@ -161,12 +154,13 @@ fn full_rebuild<F>(
|
||||
|
||||
fn hotreload_files(
|
||||
hot_reload: &HotReloadState,
|
||||
file_map: &SharedFileMap,
|
||||
needs_full_rebuild: &mut bool,
|
||||
event: ¬ify::Event,
|
||||
config: &CrateConfig,
|
||||
) {
|
||||
// find changes to the rsx in the file
|
||||
let mut rsx_file_map = hot_reload.file_map.lock().unwrap();
|
||||
let mut rsx_file_map = file_map.lock().unwrap();
|
||||
let mut messages: Vec<HotReloadMsg> = Vec::new();
|
||||
|
||||
for path in &event.paths {
|
||||
@@ -220,7 +214,7 @@ fn hotreload_files(
|
||||
}
|
||||
|
||||
for msg in messages {
|
||||
let _ = hot_reload.messages.send(msg);
|
||||
hot_reload.receiver.send_message(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +292,7 @@ fn attempt_css_reload(
|
||||
_ = rsx_file_map.is_tracking_asset(&local_path)?;
|
||||
|
||||
// copy the asset over to the output directory
|
||||
// todo this whole css hotreloading shouldbe less hacky and more robust
|
||||
// todo this whole css hotreloading should be less hacky and more robust
|
||||
_ = fs_extra::copy_items(
|
||||
&[path],
|
||||
config.out_dir(),
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
use crate::server::HotReloadState;
|
||||
use axum::{
|
||||
extract::{
|
||||
ws::{Message, WebSocket},
|
||||
WebSocketUpgrade,
|
||||
},
|
||||
response::IntoResponse,
|
||||
Extension,
|
||||
};
|
||||
use dioxus_hot_reload::HotReloadMsg;
|
||||
use futures_util::{pin_mut, FutureExt};
|
||||
|
||||
pub async fn hot_reload_handler(
|
||||
ws: WebSocketUpgrade,
|
||||
Extension(state): Extension<HotReloadState>,
|
||||
) -> impl IntoResponse {
|
||||
ws.on_upgrade(|socket| async move {
|
||||
let err = hotreload_loop(socket, state).await;
|
||||
|
||||
if let Err(err) = err {
|
||||
tracing::error!("Hotreload receiver failed: {}", err);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn hotreload_loop(mut socket: WebSocket, state: HotReloadState) -> anyhow::Result<()> {
|
||||
tracing::info!("🔥 Hot Reload WebSocket connected");
|
||||
|
||||
// update any rsx calls that changed before the websocket connected.
|
||||
// These templates will be sent down immediately so the page is in sync with the hotreloaded version
|
||||
// The compiled version will be different from the one we actually want to present
|
||||
for template in state.all_templates() {
|
||||
socket
|
||||
.send(Message::Text(serde_json::to_string(&template).unwrap()))
|
||||
.await?;
|
||||
}
|
||||
|
||||
let mut rx = state.messages.subscribe();
|
||||
|
||||
loop {
|
||||
let msg = {
|
||||
// Poll both the receiver and the socket
|
||||
//
|
||||
// This shuts us down if the connection is closed.
|
||||
let mut _socket = socket.recv().fuse();
|
||||
let mut _rx = rx.recv().fuse();
|
||||
|
||||
pin_mut!(_socket, _rx);
|
||||
|
||||
let msg = futures_util::select! {
|
||||
msg = _rx => msg,
|
||||
e = _socket => {
|
||||
if let Some(Err(e)) = e {
|
||||
tracing::info!("🔥 Hot Reload WebSocket Error: {}", e);
|
||||
} else {
|
||||
tracing::info!("🔥 Hot Reload WebSocket Closed");
|
||||
}
|
||||
break;
|
||||
},
|
||||
};
|
||||
|
||||
let Ok(msg) = msg else { break };
|
||||
|
||||
match msg {
|
||||
HotReloadMsg::UpdateTemplate(template) => {
|
||||
Message::Text(serde_json::to_string(&template).unwrap())
|
||||
}
|
||||
HotReloadMsg::UpdateAsset(asset) => {
|
||||
Message::Text(format!("reload-asset: {}", asset.display()))
|
||||
}
|
||||
HotReloadMsg::Shutdown => {
|
||||
tracing::info!("🔥 Hot Reload WebSocket shutting down");
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
socket.send(msg).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
serve::Serve,
|
||||
server::{
|
||||
output::{print_console_info, PrettierOptions, WebServerInfo},
|
||||
setup_file_watcher, HotReloadState,
|
||||
setup_file_watcher,
|
||||
},
|
||||
BuildResult, Result,
|
||||
};
|
||||
@@ -14,28 +14,20 @@ use std::{
|
||||
net::{SocketAddr, UdpSocket},
|
||||
sync::Arc,
|
||||
};
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
mod hot_reload;
|
||||
mod proxy;
|
||||
mod server;
|
||||
|
||||
use server::*;
|
||||
|
||||
pub struct WsReloadState {
|
||||
update: broadcast::Sender<()>,
|
||||
}
|
||||
use super::HotReloadState;
|
||||
|
||||
pub async fn startup(config: CrateConfig, serve_cfg: &ConfigOptsServe) -> Result<()> {
|
||||
set_ctrlc_handler(&config);
|
||||
|
||||
let ip = get_ip().unwrap_or(String::from("0.0.0.0"));
|
||||
|
||||
let mut hot_reload_state = None;
|
||||
|
||||
if config.hot_reload {
|
||||
hot_reload_state = Some(build_hotreload_filemap(&config));
|
||||
}
|
||||
let hot_reload_state = build_hotreload_filemap(&config);
|
||||
|
||||
serve(ip, config, hot_reload_state, serve_cfg).await
|
||||
}
|
||||
@@ -44,7 +36,7 @@ pub async fn startup(config: CrateConfig, serve_cfg: &ConfigOptsServe) -> Result
|
||||
pub async fn serve(
|
||||
ip: String,
|
||||
config: CrateConfig,
|
||||
hot_reload_state: Option<HotReloadState>,
|
||||
hot_reload_state: HotReloadState,
|
||||
opts: &ConfigOptsServe,
|
||||
) -> Result<()> {
|
||||
let skip_assets = opts.skip_assets;
|
||||
@@ -59,16 +51,13 @@ pub async fn serve(
|
||||
|
||||
tracing::info!("🚀 Starting development server...");
|
||||
|
||||
// WS Reload Watching
|
||||
let (reload_tx, _) = broadcast::channel(100);
|
||||
|
||||
// We got to own watcher so that it exists for the duration of serve
|
||||
// Otherwise full reload won't work.
|
||||
let _watcher = setup_file_watcher(
|
||||
{
|
||||
let config = config.clone();
|
||||
let reload_tx = reload_tx.clone();
|
||||
move || build(&config, &reload_tx, skip_assets)
|
||||
let hot_reload_state = hot_reload_state.clone();
|
||||
move || build(&config, &hot_reload_state, skip_assets)
|
||||
},
|
||||
&config,
|
||||
Some(WebServerInfo {
|
||||
@@ -79,10 +68,6 @@ pub async fn serve(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let ws_reload_state = Arc::new(WsReloadState {
|
||||
update: reload_tx.clone(),
|
||||
});
|
||||
|
||||
// HTTPS
|
||||
// Before console info so it can stop if mkcert isn't installed or fails
|
||||
let rustls_config = get_rustls(&config).await?;
|
||||
@@ -102,7 +87,7 @@ pub async fn serve(
|
||||
);
|
||||
|
||||
// Router
|
||||
let router = setup_router(config.clone(), ws_reload_state, hot_reload_state).await?;
|
||||
let router = setup_router(config.clone(), hot_reload_state).await?;
|
||||
|
||||
// Start server
|
||||
start_server(port, router, opts.open, rustls_config, &config).await?;
|
||||
@@ -173,7 +158,7 @@ fn get_ip() -> Option<String> {
|
||||
|
||||
fn build(
|
||||
config: &CrateConfig,
|
||||
reload_tx: &broadcast::Sender<()>,
|
||||
hot_reload_state: &HotReloadState,
|
||||
skip_assets: bool,
|
||||
) -> Result<BuildResult> {
|
||||
// Since web platform doesn't use `rust_flags`, this argument is explicitly
|
||||
@@ -189,7 +174,7 @@ fn build(
|
||||
}
|
||||
}
|
||||
|
||||
let _ = reload_tx.send(());
|
||||
hot_reload_state.receiver.reload();
|
||||
|
||||
result
|
||||
}
|
||||
@@ -207,14 +192,17 @@ fn set_ctrlc_handler(config: &CrateConfig) {
|
||||
}
|
||||
|
||||
fn build_hotreload_filemap(config: &CrateConfig) -> HotReloadState {
|
||||
let FileMapBuildResult { map, errors } = FileMap::create(config.crate_dir.clone()).unwrap();
|
||||
|
||||
for err in errors {
|
||||
tracing::error!("{}", err);
|
||||
}
|
||||
|
||||
HotReloadState {
|
||||
messages: broadcast::channel(100).0.clone(),
|
||||
file_map: Arc::new(Mutex::new(map)).clone(),
|
||||
file_map: config.hot_reload.then(|| {
|
||||
let FileMapBuildResult { map, errors } =
|
||||
FileMap::create(config.crate_dir.clone()).unwrap();
|
||||
|
||||
for err in errors {
|
||||
tracing::error!("{}", err);
|
||||
}
|
||||
|
||||
Arc::new(Mutex::new(map))
|
||||
}),
|
||||
receiver: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
use super::{hot_reload::*, WsReloadState};
|
||||
use crate::{server::HotReloadState, Result};
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::{
|
||||
ws::{Message, WebSocket},
|
||||
Extension, WebSocketUpgrade,
|
||||
},
|
||||
extract::Extension,
|
||||
http::{
|
||||
self,
|
||||
header::{HeaderName, HeaderValue},
|
||||
@@ -17,7 +13,8 @@ use axum::{
|
||||
};
|
||||
use axum_server::tls_rustls::RustlsConfig;
|
||||
use dioxus_cli_config::{CrateConfig, WebHttpsConfig};
|
||||
use std::{fs, io, process::Command, sync::Arc};
|
||||
use dioxus_hot_reload::HotReloadRouterExt;
|
||||
use std::{fs, io, process::Command};
|
||||
use tower::ServiceBuilder;
|
||||
use tower_http::{
|
||||
cors::{Any, CorsLayer},
|
||||
@@ -26,11 +23,7 @@ use tower_http::{
|
||||
};
|
||||
|
||||
/// Sets up and returns a router
|
||||
pub async fn setup_router(
|
||||
config: CrateConfig,
|
||||
ws_reload: Arc<WsReloadState>,
|
||||
hot_reload: Option<HotReloadState>,
|
||||
) -> Result<Router> {
|
||||
pub async fn setup_router(config: CrateConfig, hot_reload: HotReloadState) -> Result<Router> {
|
||||
// Setup cors
|
||||
let cors = CorsLayer::new()
|
||||
// allow `GET` and `POST` when accessing the resource
|
||||
@@ -93,17 +86,12 @@ pub async fn setup_router(
|
||||
};
|
||||
|
||||
// Setup websocket
|
||||
router = router.route("/_dioxus/ws", get(ws_handler));
|
||||
router = router.connect_hot_reload();
|
||||
|
||||
// Setup routes
|
||||
router = router
|
||||
.route("/_dioxus/hot_reload", get(hot_reload_handler))
|
||||
.layer(cors)
|
||||
.layer(Extension(ws_reload));
|
||||
|
||||
if let Some(hot_reload) = hot_reload {
|
||||
router = router.layer(Extension(hot_reload))
|
||||
}
|
||||
.layer(Extension(hot_reload.receiver.clone()));
|
||||
|
||||
Ok(router)
|
||||
}
|
||||
@@ -138,34 +126,6 @@ fn no_cache(
|
||||
response
|
||||
}
|
||||
|
||||
/// Handle websockets
|
||||
async fn ws_handler(
|
||||
ws: WebSocketUpgrade,
|
||||
Extension(state): Extension<Arc<WsReloadState>>,
|
||||
) -> impl IntoResponse {
|
||||
ws.on_upgrade(move |socket| ws_reload_handler(socket, state))
|
||||
}
|
||||
|
||||
async fn ws_reload_handler(mut socket: WebSocket, state: Arc<WsReloadState>) {
|
||||
let mut rx = state.update.subscribe();
|
||||
|
||||
let reload_watcher = tokio::spawn(async move {
|
||||
loop {
|
||||
rx.recv().await.unwrap();
|
||||
|
||||
let _ = socket.send(Message::Text(String::from("reload"))).await;
|
||||
|
||||
// ignore the error
|
||||
println!("forcing reload");
|
||||
|
||||
// flush the errors after recompling
|
||||
rx = rx.resubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
reload_watcher.await.unwrap();
|
||||
}
|
||||
|
||||
const DEFAULT_KEY_PATH: &str = "ssl/key.pem";
|
||||
const DEFAULT_CERT_PATH: &str = "ssl/cert.pem";
|
||||
|
||||
|
||||
Reference in new issue
Block a user