fix: hotreloading files with multiple templates, asset hotreloading

This commit is contained in:
Jonathan Kelley committed 2024-03-13 02:13:37 -07:00
1 parent 05476cad76
commit bca5335f31
16 files changed
+723 -453

No files matched your search

+11 -6
View File
@@ -4,7 +4,8 @@
(function () {
var protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
var url = protocol + '//' + window.location.host + '/_dioxus/ws';
var poll_interval = 8080;
var poll_interval = 8080;
var reload_upon_connect = () => {
window.setTimeout(
() => {
@@ -15,11 +16,15 @@
poll_interval);
};
var ws = new WebSocket(url);
ws.onmessage = (ev) => {
if (ev.data == "reload") {
window.location.reload();
}
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;
})()
+1 -1
View File
@@ -64,7 +64,7 @@ impl ExecWithRustFlagsSetter for subprocess::Exec {
/// Build client (WASM).
/// Note: `rust_flags` argument is only used for the fullstack platform.
pub fn build(
pub fn build_web(
config: &CrateConfig,
skip_assets: bool,
rust_flags: Option<String>,
+2 -2
View File
@@ -57,7 +57,7 @@ impl Build {
let build_result = match platform {
Platform::Web => {
// `rust_flags` are used by fullstack's client build.
crate::builder::build(&crate_config, self.build.skip_assets, rust_flags)?
crate::builder::build_web(&crate_config, self.build.skip_assets, rust_flags)?
}
Platform::Desktop => {
// Since desktop platform doesn't use `rust_flags`, this
@@ -80,7 +80,7 @@ impl Build {
}
None => web_config.features = Some(vec![web_feature]),
};
crate::builder::build(
crate::builder::build_web(
&web_config,
self.build.skip_assets,
Some(client_rust_flags),
+1 -1
View File
@@ -138,7 +138,7 @@ async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<()
.unwrap()
.map
.values()
.filter_map(|(_, template_slot)| *template_slot)
.flat_map(|v| v.templates.values().copied())
.collect()
};
for template in templates {
+72 -14
View File
@@ -6,6 +6,7 @@ use dioxus_core::Template;
use dioxus_hot_reload::HotReloadMsg;
use dioxus_html::HtmlCtx;
use dioxus_rsx::hot_reload::*;
use fs_extra::{dir::CopyOptions, file};
use notify::{RecommendedWatcher, Watcher};
use std::{
path::PathBuf,
@@ -157,17 +158,9 @@ fn hotreload_files(
for path in &event.paths {
// for various assets that might be linked in, we just try to hotreloading them forcefully
// That is, unless they appear in an include! macro, in which case we need to a full rebuild....
// if this is not a rust file, rebuild the whole project
let path_extension = path.extension().and_then(|p| p.to_str());
if path_extension != Some("rs") {
*needs_full_rebuild = true;
if path_extension == Some("rs~") {
*needs_full_rebuild = false;
}
let Some(ext) = path.extension().and_then(|v| v.to_str()) else {
continue;
}
};
// Workaround for notify and vscode-like editor:
// when edit & save a file in vscode, there will be two notifications,
@@ -179,8 +172,67 @@ fn hotreload_files(
}
}
match ext {
// Attempt hot reload
"rs" => {}
// Anything with a .file is also ignored
_ if path.file_stem().is_none() || ext.ends_with("~") => {}
// Anything else is a maybe important file that needs to be rebuilt
_ => {
// If it happens to be a file in the asset directory, there's a chance we can hotreload it.
// Only css is currently supported for hotreload
if ext == "css" {
let asset_dir = config
.crate_dir
.join(&config.dioxus_config.application.asset_dir);
if path.starts_with(&asset_dir) {
let local_path: PathBuf = path
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_string()
.parse()
.unwrap();
println!(
"maybe tracking asset: {:?}, {:#?}",
local_path,
rsx_file_map.tracked_assets()
);
if let Some(f) = rsx_file_map.is_tracking_asset(&local_path) {
println!(
"Hot reloading asset - it's tracked by the rsx!: {:?}",
local_path
);
// copy the asset over tothe output directory
let output_dir = config.out_dir();
fs_extra::copy_items(
&[path],
output_dir,
&CopyOptions::new().overwrite(true),
)
.unwrap();
messages.push(HotReloadMsg::UpdateAsset(local_path));
continue;
}
}
}
*needs_full_rebuild = true;
}
};
match rsx_file_map.update_rsx(path, &config.crate_dir) {
Ok(UpdateResult::UpdatedRsx(msgs)) => {
println!("Updated: {:?}", msgs);
messages.extend(
msgs.into_iter()
.map(|msg| HotReloadMsg::UpdateTemplate(msg)),
@@ -196,6 +248,8 @@ fn hotreload_files(
}
}
// If full rebuild, extend the file map with the new file map
// This will wipe away any previous cached changed templates
if *needs_full_rebuild {
// Reset the file map to the new state of the project
let FileMapBuildResult {
@@ -208,10 +262,14 @@ fn hotreload_files(
}
*rsx_file_map = new_file_map;
} else {
for msg in messages {
let _ = hot_reload.messages.send(msg);
}
return;
}
println!("Hot reloading: {:?}", messages);
for msg in messages {
let _ = hot_reload.messages.send(msg);
}
}
+31 -7
View File
@@ -8,6 +8,7 @@ use axum::{
Extension,
};
use dioxus_hot_reload::HotReloadMsg;
use futures_util::{pin_mut, FutureExt};
pub async fn hot_reload_handler(
ws: WebSocketUpgrade,
@@ -34,9 +35,11 @@ async fn hotreload_loop(mut socket: WebSocket, state: HotReloadState) -> anyhow:
.unwrap()
.map
.values()
.filter_map(|(_, template_slot)| *template_slot)
.flat_map(|v| v.templates.values().copied())
.collect::<Vec<_>>();
println!("previously changed: {:?}", templates);
for template in templates {
socket
.send(Message::Text(serde_json::to_string(&template).unwrap()))
@@ -46,16 +49,37 @@ async fn hotreload_loop(mut socket: WebSocket, state: HotReloadState) -> anyhow:
let mut rx = state.messages.subscribe();
loop {
if let Ok(msg) = rx.recv().await {
let msg = match msg {
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,
_ = _socket => break,
};
let Ok(msg) = msg else { break };
println!("msg: {:?}", msg);
match msg {
HotReloadMsg::UpdateTemplate(template) => {
Message::Text(serde_json::to_string(&template).unwrap())
}
HotReloadMsg::UpdateAsset(_) => todo!(),
HotReloadMsg::UpdateAsset(asset) => {
Message::Text(format!("asset: {}", asset.display()))
}
HotReloadMsg::Shutdown => todo!(),
};
}
};
socket.send(msg).await?;
}
socket.send(msg).await?;
}
Ok(())
}
+2 -2
View File
@@ -52,7 +52,7 @@ pub async fn serve(
// Since web platform doesn't use `rust_flags`, this argument is explicitly
// set to `None`.
let first_build_result = crate::builder::build(&config, skip_assets, None)?;
let first_build_result = crate::builder::build_web(&config, skip_assets, None)?;
// generate dev-index page
Serve::regen_dev_page(&config, first_build_result.assets.as_ref())?;
@@ -173,7 +173,7 @@ fn build(
) -> Result<BuildResult> {
// Since web platform doesn't use `rust_flags`, this argument is explicitly
// set to `None`.
let result = std::panic::catch_unwind(|| builder::build(config, skip_assets, None))
let result = std::panic::catch_unwind(|| builder::build_web(config, skip_assets, None))
.map_err(|e| anyhow::anyhow!("Build failed: {e:?}"))?;
// change the websocket reload state to true;