show panics in the toast in wasm (#4582)

This commit is contained in:
Jonathan Kelley authored and GitHub committed 2025-08-19 17:13:13 -07:00
1 parent 165447a647
commit 74163984be
7 files changed
+68 -12

No files matched your search

Generated
-1
View File
@@ -5686,7 +5686,6 @@ dependencies = [
name = "dioxus-logger"
version = "0.7.0-rc.0"
dependencies = [
"console_error_panic_hook",
"dioxus",
"dioxus-cli-config",
"tracing",
-1
View File
@@ -307,7 +307,6 @@ criterion = { version = "0.6" }
cargo_metadata = "0.19.2"
parking_lot = "0.12.4"
tracing-wasm = "0.2.1"
console_error_panic_hook = "0.1.7"
base16 = "0.2.1"
digest = "0.10.7"
sha2 = "0.10.9"
-1
View File
@@ -22,7 +22,6 @@ default = []
[target.'cfg(target_arch = "wasm32")'.dependencies]
tracing-wasm = { workspace = true }
console_error_panic_hook = { workspace = true }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tracing-subscriber = { workspace = true, features = ["fmt"] }
-1
View File
@@ -91,7 +91,6 @@ pub fn init(level: Level) -> Result<(), SetGlobalDefaultError> {
let layer = tracing_wasm::WASMLayer::new(layer_config);
let reg = Registry::default().with(layer);
console_error_panic_hook::set_once();
set_global_default(reg)
}
+2
View File
@@ -14,6 +14,7 @@ use wasm_bindgen::JsCast as _;
/// ```
pub struct Config {
pub(crate) hydrate: bool,
pub(crate) panic_hook: bool,
pub(crate) root: ConfigRoot,
#[cfg(feature = "document")]
pub(crate) history: Option<Rc<dyn dioxus_history::History>>,
@@ -96,6 +97,7 @@ impl Default for Config {
root: ConfigRoot::RootName("main".to_string()),
#[cfg(feature = "document")]
history: None,
panic_hook: true,
}
}
}
+63 -5
View File
@@ -3,12 +3,12 @@
//! This sets up a websocket connection to the devserver and handles messages from it.
//! We also set up a little recursive timer that will attempt to reconnect if the connection is lost.
use std::fmt::Display;
use std::time::Duration;
use dioxus_devtools::{DevserverMsg, HotReloadMsg};
use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use js_sys::JsString;
use std::fmt::Display;
use std::time::Duration;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen::{closure::Closure, JsValue};
use web_sys::{window, CloseEvent, MessageEvent, WebSocket};
@@ -21,15 +21,23 @@ const POLL_INTERVAL_SCALE_FACTOR: i32 = 2;
const TOAST_TIMEOUT: Duration = Duration::from_secs(5);
const TOAST_TIMEOUT_LONG: Duration = Duration::from_secs(3600); // Duration::MAX is too long for JS.
pub(crate) fn init() -> UnboundedReceiver<HotReloadMsg> {
pub(crate) fn init(config: &crate::Config) -> 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.clone(), POLL_INTERVAL_MIN, false);
// Set up the playground
playground(tx);
// Set up the panic hook
if config.panic_hook {
std::panic::set_hook(Box::new(|info| {
hook_impl(info);
}));
}
rx
}
@@ -223,7 +231,7 @@ pub(crate) fn show_toast(
_ = js_sys::eval(&format!(
r#"
if (typeof {js_fn_name} !== "undefined") {{
window.{js_fn_name}("{header_text}", "{message}", "{level}", {as_ms});
window.{js_fn_name}(`{header_text}`, `{message}`, `{level}`, {as_ms});
}}
"#,
));
@@ -289,3 +297,53 @@ fn playground(tx: UnboundedSender<HotReloadMsg>) {
binding.forget();
}
fn hook_impl(info: &std::panic::PanicHookInfo) {
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn error(msg: String);
type Error;
#[wasm_bindgen(constructor)]
fn new() -> Error;
#[wasm_bindgen(structural, method, getter)]
fn stack(error: &Error) -> String;
}
let mut msg = info.to_string();
// Add the error stack to our message.
//
// This ensures that even if the `console` implementation doesn't
// include stacks for `console.error`, the stack is still available
// for the user. Additionally, Firefox's console tries to clean up
// stack traces, and ruins Rust symbols in the process
// (https://bugzilla.mozilla.org/show_bug.cgi?id=1519569) but since
// it only touches the logged message's associated stack, and not
// the message's contents, by including the stack in the message
// contents we make sure it is available to the user.
msg.push_str("\n\nStack:\n\n");
let e = Error::new();
let stack = e.stack();
msg.push_str(&stack);
// Safari's devtools, on the other hand, _do_ mess with logged
// messages' contents, so we attempt to break their heuristics for
// doing that by appending some whitespace.
// https://github.com/rustwasm/console_error_panic_hook/issues/7
msg.push_str("\n\n");
// Log the panic with `console.error`!
error(msg.clone());
show_toast(
"App panicked! See console for details.",
&msg,
ToastLevel::Error,
TOAST_TIMEOUT_LONG,
false,
)
}
+3 -3
View File
@@ -51,6 +51,9 @@ pub use hydration::*;
/// wasm_bindgen_futures::spawn_local(app_fut);
/// ```
pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! {
#[cfg(all(feature = "devtools", debug_assertions))]
let mut hotreload_rx = devtools::init(&web_config);
#[cfg(feature = "document")]
if let Some(history) = web_config.history.clone() {
virtual_dom.in_runtime(|| dioxus_core::ScopeId::ROOT.provide_context(history));
@@ -61,9 +64,6 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! {
let runtime = virtual_dom.runtime();
#[cfg(all(feature = "devtools", debug_assertions))]
let mut hotreload_rx = devtools::init();
// If the hydrate feature is enabled, launch the client with hydration enabled
let should_hydrate = web_config.hydrate || cfg!(feature = "hydrate");