Skip to content

Commit

Permalink
fix: merge
Browse files Browse the repository at this point in the history
  • Loading branch information
DogeDark committed Dec 11, 2024
2 parents d76bcbd + e598cfe commit fd8556e
Show file tree
Hide file tree
Showing 14 changed files with 295 additions and 215 deletions.
346 changes: 176 additions & 170 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,10 @@ futures = "0.3.30"
futures-channel = "0.3.21"
futures-util = { version = "0.3", default-features = false }
rustc-hash = "1.1.0"
wasm-bindgen = "0.2.95"
wasm-bindgen = "0.2.99"
wasm-bindgen-futures = "0.4.42"
js-sys = "0.3.76"
web-sys = { version = "0.3.76", default-features = false }
html_parser = "0.7.0"
thiserror = "1.0.40"
prettyplease = { version = "0.2.20", features = ["verbatim"] }
Expand Down Expand Up @@ -215,8 +217,6 @@ rustversion = "1.0.17"
rand = "0.8.5"
longest-increasing-subsequence = "0.1.0"
trybuild = "1.0"
js-sys = "0.3.56"
web-sys = { version = "0.3.56", default-features = false }
dirs = "5.0.1"
cargo-config2 = "0.1.26"
criterion = { version = "0.5" }
Expand Down
31 changes: 28 additions & 3 deletions packages/cli/src/serve/handle.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{AppBundle, Platform, Result};
use anyhow::Context;
use std::{
fs,
net::SocketAddr,
path::{Path, PathBuf},
process::Stdio,
Expand Down Expand Up @@ -163,7 +164,7 @@ impl AppHandle {
/// This will return the bundled name of the asset such that we can send it to the clients letting
/// them know what to reload. It's not super important that this is robust since most clients will
/// kick all stylsheets without necessarily checking the name.
pub(crate) fn hotreload_bundled_asset(&self, changed_file: &PathBuf) -> Option<PathBuf> {
pub(crate) async fn hotreload_bundled_asset(&self, changed_file: &PathBuf) -> Option<PathBuf> {
let mut bundled_name = None;

// Use the build dir if there's no runtime asset dir as the override. For the case of ios apps,
Expand All @@ -187,15 +188,39 @@ impl AppHandle {
}
}

// Canonicalize the path as Windows may use long-form paths "\\\\?\\C:\\".
let changed_file = fs::canonicalize(changed_file)
.inspect_err(|e| tracing::debug!("Failed to canonicalize hotreloaded asset: {e}"))
.ok()?;

// The asset might've been renamed thanks to the manifest, let's attempt to reload that too
if let Some(resource) = self.app.app.assets.assets.get(changed_file).as_ref() {
let res = std::fs::copy(changed_file, asset_dir.join(resource.bundled_path()));
if let Some(resource) = self.app.app.assets.assets.get(&changed_file).as_ref() {
let res = std::fs::copy(&changed_file, asset_dir.join(resource.bundled_path()));
bundled_name = Some(PathBuf::from(resource.bundled_path()));
if let Err(e) = res {
tracing::debug!("Failed to hotreload asset {e}");
}
}

// If the emulator is android, we need to copy the asset to the device with `adb push asset /data/local/tmp/dx/assets/filename.ext`
if self.app.build.build.platform() == Platform::Android {
if let Some(bundled_name) = bundled_name.as_ref() {
let target = format!("/data/local/tmp/dx/{}", bundled_name.display());
tracing::debug!("Pushing asset to device: {target}");
let res = tokio::process::Command::new("adb")
.arg("push")
.arg(&changed_file)
.arg(target)
.output()
.await
.context("Failed to push asset to device");

if let Err(e) = res {
tracing::debug!("Failed to push asset to device: {e}");
}
}
}

// Now we can return the bundled asset name to send to the hotreload engine
bundled_name
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/serve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> {

// if change is hotreloadable, hotreload it
// and then send that update to all connected clients
if let Some(hr) = runner.attempt_hot_reload(files) {
if let Some(hr) = runner.attempt_hot_reload(files).await {
// Only send a hotreload message for templates and assets - otherwise we'll just get a full rebuild
if hr.templates.is_empty()
&& hr.assets.is_empty()
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/serve/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ impl AppRunner {
Ok(())
}

pub(crate) fn attempt_hot_reload(
pub(crate) async fn attempt_hot_reload(
&mut self,
modified_files: Vec<PathBuf>,
) -> Option<HotReloadMsg> {
Expand All @@ -183,7 +183,7 @@ impl AppRunner {

// Otherwise, it might be an asset and we should look for it in all the running apps
for runner in self.running.values() {
if let Some(bundled_name) = runner.hotreload_bundled_asset(&path) {
if let Some(bundled_name) = runner.hotreload_bundled_asset(&path).await {
// todo(jon): don't hardcode this here
let asset_relative = PathBuf::from("/assets/").join(bundled_name);
assets.push(asset_relative);
Expand Down
17 changes: 13 additions & 4 deletions packages/desktop/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,19 @@ fn get_mime_by_ext(trimmed: &Path) -> &'static str {

#[cfg(target_os = "android")]
pub(crate) fn to_java_load_asset(filepath: &str) -> Option<Vec<u8>> {
let normalized = filepath
.trim_start_matches("/assets/")
.trim_start_matches('/');

// in debug mode, the asset might be under `/data/local/tmp/dx/` - attempt to read it from there if it exists
#[cfg(debug_assertions)]
{
let path = std::path::PathBuf::from("/data/local/tmp/dx/").join(normalized);
if path.exists() {
return std::fs::read(path).ok();
}
}

use std::{io::Read, ptr::NonNull};

let ctx = ndk_context::android_context();
Expand All @@ -301,10 +314,6 @@ pub(crate) fn to_java_load_asset(filepath: &str) -> Option<Vec<u8>> {
NonNull::new(asset_manager).expect("Invalid asset manager"),
);

let normalized = filepath
.trim_start_matches("/assets/")
.trim_start_matches('/');

let cstr = std::ffi::CString::new(normalized).unwrap();

let mut asset = asset_manager.open(&cstr)?;
Expand Down
7 changes: 0 additions & 7 deletions packages/dioxus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,3 @@ features = [
"liveview",
"server"
]
targets = [
"wasm32-unknown-unknown",
"aarch64-apple-darwin",
"x86_64-unknown-linux-gnu",
"x86_64-pc-windows-msvc",
"aarch64-linux-android",
]
2 changes: 1 addition & 1 deletion packages/fullstack/src/serve_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ impl ServeConfigBuilder {
///
/// It is equivalent to calling `streaming_mode(StreamingMode::OutOfOrder)`
///
/// /// ```rust, no_run
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # fn app() -> Element { todo!() }
/// dioxus::LaunchBuilder::new()
Expand Down
2 changes: 1 addition & 1 deletion packages/interpreter/src/js/hash.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[6449103750905854967, 3846442265490457921, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 7422899642446454418, 5052021921702764563, 12925655762638175824, 5638004933879392817]
[6449103750905854967, 3846442265490457921, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 12156139214887111728, 5052021921702764563, 12925655762638175824, 5638004933879392817]
2 changes: 1 addition & 1 deletion packages/interpreter/src/js/native.js

Large diffs are not rendered by default.

43 changes: 34 additions & 9 deletions packages/interpreter/src/ts/native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,10 @@ export class NativeInterpreter extends JSChannel_ {
// Windows drag-n-drop fix code. Called by wry drag-n-drop handler over the event loop.
handleWindowsDragDrop() {
if (window.dxDragLastElement) {
const dragLeaveEvent = new DragEvent("dragleave", { bubbles: true, cancelable: true });
const dragLeaveEvent = new DragEvent("dragleave", {
bubbles: true,
cancelable: true,
});
window.dxDragLastElement.dispatchEvent(dragLeaveEvent);

let data = new DataTransfer();
Expand All @@ -179,7 +182,11 @@ export class NativeInterpreter extends JSChannel_ {
const file = new File(["content"], "file.txt", { type: "text/plain" });
data.items.add(file);

const dragDropEvent = new DragEvent("drop", { bubbles: true, cancelable: true, dataTransfer: data });
const dragDropEvent = new DragEvent("drop", {
bubbles: true,
cancelable: true,
dataTransfer: data,
});
window.dxDragLastElement.dispatchEvent(dragDropEvent);
window.dxDragLastElement = null;
}
Expand All @@ -190,19 +197,28 @@ export class NativeInterpreter extends JSChannel_ {

if (element != window.dxDragLastElement) {
if (window.dxDragLastElement) {
const dragLeaveEvent = new DragEvent("dragleave", { bubbles: true, cancelable: true });
const dragLeaveEvent = new DragEvent("dragleave", {
bubbles: true,
cancelable: true,
});
window.dxDragLastElement.dispatchEvent(dragLeaveEvent);
}

const dragOverEvent = new DragEvent("dragover", { bubbles: true, cancelable: true });
const dragOverEvent = new DragEvent("dragover", {
bubbles: true,
cancelable: true,
});
element.dispatchEvent(dragOverEvent);
window.dxDragLastElement = element;
}
}

handleWindowsDragLeave() {
if (window.dxDragLastElement) {
const dragLeaveEvent = new DragEvent("dragleave", { bubbles: true, cancelable: true });
const dragLeaveEvent = new DragEvent("dragleave", {
bubbles: true,
cancelable: true,
});
window.dxDragLastElement.dispatchEvent(dragLeaveEvent);
window.dxDragLastElement = null;
}
Expand Down Expand Up @@ -382,10 +398,19 @@ export class NativeInterpreter extends JSChannel_ {
let stylesheets = document.querySelectorAll("link[rel=stylesheet]");
for (let i = 0; i < stylesheets.length; i++) {
let sheet = stylesheets[i] as HTMLLinkElement;
// Using `cache: reload` will force the browser to re-fetch the stylesheet and bust the cache
fetch(sheet.href, { cache: "reload" }).then(() => {
sheet.href = sheet.href + "?" + Math.random();
});
// Split up the url and add a extra random query param to force the browser to reload he asset
const splitByQuery = sheet.href.split("?");
let url = splitByQuery[0];
let query = splitByQuery[1];
if (!query) {
query = "";
}
let queryParams = new URLSearchParams(query);
// Delete the existing dx_force_reload entry if it exists
queryParams.delete("dx_force_reload");
// And add a new random dx_force_reload query param to force the browser to reload the asset
queryParams.append("dx_force_reload", Math.random().toString());
sheet.href = `${url}?${queryParams}`;
}
}

Expand Down
5 changes: 0 additions & 5 deletions packages/manganis/manganis-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ use linker::generate_link_section;
/// # use manganis::{asset, Asset};
/// const _: Asset = asset!("/assets/asset.txt");
/// ```
/// Or you can use URLs to read the asset at build time from a remote location
/// ```rust
/// # use manganis::{asset, Asset};
/// const _: Asset = asset!("/assets/image.png");
/// ```
///
/// # Images
///
Expand Down
37 changes: 32 additions & 5 deletions packages/web/src/devtools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
//! We also set up a little recursive timer that will attempt to reconnect if the connection is lost.
use std::fmt::Display;
use std::rc::Rc;
use std::time::Duration;

use dioxus_core::ScopeId;
use dioxus_core::prelude::RuntimeGuard;
use dioxus_core::{Runtime, ScopeId};
use dioxus_devtools::{DevserverMsg, HotReloadMsg};
use dioxus_document::eval;
use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
Expand All @@ -22,17 +24,22 @@ const POLL_INTERVAL_SCALE_FACTOR: i32 = 2;
/// Amount of time that toats should be displayed.
const TOAST_TIMEOUT: Duration = Duration::from_secs(5);

pub(crate) fn init() -> UnboundedReceiver<HotReloadMsg> {
pub(crate) fn init(runtime: Rc<Runtime>) -> UnboundedReceiver<HotReloadMsg> {
// Create the tx/rx pair that we'll use for the top-level future in the dioxus loop
let (tx, rx) = unbounded();

// Wire up the websocket to the devserver
make_ws(tx, POLL_INTERVAL_MIN, false);
make_ws(runtime, tx, POLL_INTERVAL_MIN, false);

rx
}

fn make_ws(tx: UnboundedSender<HotReloadMsg>, poll_interval: i32, reload: bool) {
fn make_ws(
runtime: Rc<Runtime>,
tx: UnboundedSender<HotReloadMsg>,
poll_interval: i32,
reload: bool,
) {
// Get the location of the devserver, using the current location plus the /_dioxus path
// The idea here being that the devserver is always located on the /_dioxus behind a proxy
let location = web_sys::window().unwrap().location();
Expand All @@ -49,6 +56,7 @@ fn make_ws(tx: UnboundedSender<HotReloadMsg>, poll_interval: i32, reload: bool)

// Set the onmessage handler to bounce messages off to the main dioxus loop
let tx_ = tx.clone();
let runtime_ = runtime.clone();
ws.set_onmessage(Some(
Closure::<dyn FnMut(MessageEvent)>::new(move |e: MessageEvent| {
let Ok(text) = e.data().dyn_into::<JsString>() else {
Expand All @@ -72,6 +80,7 @@ fn make_ws(tx: UnboundedSender<HotReloadMsg>, poll_interval: i32, reload: bool)

// The devserver is telling us that it started a full rebuild. This does not mean that it is ready.
Ok(DevserverMsg::FullReloadStart) => show_toast(
runtime_.clone(),
"Your app is being rebuilt.",
"A non-hot-reloadable change occurred and we must rebuild.",
ToastLevel::Info,
Expand All @@ -80,6 +89,7 @@ fn make_ws(tx: UnboundedSender<HotReloadMsg>, poll_interval: i32, reload: bool)
),
// The devserver is telling us that the full rebuild failed.
Ok(DevserverMsg::FullReloadFailed) => show_toast(
runtime_.clone(),
"Oops! The build failed.",
"We tried to rebuild your app, but something went wrong.",
ToastLevel::Error,
Expand All @@ -90,6 +100,7 @@ fn make_ws(tx: UnboundedSender<HotReloadMsg>, poll_interval: i32, reload: bool)
// The devserver is telling us to reload the whole page
Ok(DevserverMsg::FullReloadCommand) => {
show_toast(
runtime_.clone(),
"Successfully rebuilt.",
"Your app was rebuilt successfully and without error.",
ToastLevel::Success,
Expand Down Expand Up @@ -121,11 +132,13 @@ fn make_ws(tx: UnboundedSender<HotReloadMsg>, poll_interval: i32, reload: bool)

// set timeout to reload the page in timeout_ms
let tx = tx.clone();
let runtime = runtime.clone();
web_sys::window()
.unwrap()
.set_timeout_with_callback_and_timeout_and_arguments_0(
Closure::<dyn FnMut()>::new(move || {
make_ws(
runtime.clone(),
tx.clone(),
POLL_INTERVAL_MAX.min(poll_interval * POLL_INTERVAL_SCALE_FACTOR),
true,
Expand Down Expand Up @@ -189,6 +202,7 @@ impl Display for ToastLevel {

/// Displays a toast to the developer.
fn show_toast(
runtime: Rc<Runtime>,
header_text: &str,
message: &str,
level: ToastLevel,
Expand All @@ -202,6 +216,8 @@ fn show_toast(
false => "showDXToast",
};

// Create the guard before running eval which uses the global runtime context
let _guard = RuntimeGuard::new(runtime);
ScopeId::ROOT.in_runtime(|| {
eval(&format!(
r#"
Expand Down Expand Up @@ -233,7 +249,18 @@ pub(crate) fn invalidate_browser_asset_cache() {
use wasm_bindgen::JsCast;
let link: web_sys::Element = links.get(x).unwrap().unchecked_into();
if let Some(href) = link.get_attribute("href") {
_ = link.set_attribute("href", &format!("{}?{}", href, noise));
let (url, query) = href.split_once('?').unwrap_or((&href, ""));
let mut query_params: Vec<&str> = query.split('&').collect();
// Remove the old force reload param
query_params.retain(|param| !param.starts_with("dx_force_reload="));
// Add the new force reload param
let force_reload = format!("dx_force_reload={noise}");
query_params.push(&force_reload);

// Rejoin the query
let query = query_params.join("&");

_ = link.set_attribute("href", &format!("{url}?{query}"));
}
}
}
6 changes: 3 additions & 3 deletions packages/web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! {
#[cfg(feature = "document")]
virtual_dom.in_runtime(document::init_document);

#[cfg(all(feature = "devtools", debug_assertions))]
let mut hotreload_rx = devtools::init();

let runtime = virtual_dom.runtime();

#[cfg(all(feature = "devtools", debug_assertions))]
let mut hotreload_rx = devtools::init(runtime.clone());

let should_hydrate = web_config.hydrate;

let mut websys_dom = WebsysDom::new(web_config, runtime);
Expand Down

0 comments on commit fd8556e

Please sign in to comment.