panic on errors in hot-reload if no error handler socket is connected

This commit is contained in:
Evan Almloff 2022-07-01 09:01:15 -05:00
parent 5c767ececd
commit f0655a11ad
6 changed files with 84 additions and 52 deletions

View file

@ -86,6 +86,8 @@ impl DesktopController {
(serde_json::to_string(&err).unwrap() + "\n").as_bytes(),
)
.unwrap();
} else {
panic!("{}", err);
}
}
}

View file

@ -113,9 +113,9 @@ impl ToTokens for CapturedContextBuilder {
let expr = segment.to_token_stream();
let as_string = expr.to_string();
let format_expr = if format_args.is_empty() {
"{".to_string() + format_args + "}"
"{".to_string() + &format_args + "}"
} else {
"{".to_string() + ":" + format_args + "}"
"{".to_string() + ":" + &format_args + "}"
};
Some(quote! {
FormattedArg{

View file

@ -1,3 +1,5 @@
use std::fmt::Display;
use serde::{Deserialize, Serialize};
use crate::CodeLocation;
@ -37,3 +39,18 @@ impl ParseError {
ParseError { message, location }
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::ParseError(error) => write!(
f,
"parse error:\n--> at {}:{}:{}\n\t{:?}\n",
error.location.file_path, error.location.line, error.location.column, error.message
),
Error::RecompileRequiredError(reason) => {
write!(f, "recompile required: {:?}\n", reason)
}
}
}
}

View file

@ -39,7 +39,7 @@ fn resolve_ifmt(ifmt: &IfmtInput, captured: &IfmtArgs) -> Result<String, Error>
}
}
}
Segment::Literal(lit) => result.push_str(lit),
Segment::Literal(lit) => result.push_str(&lit),
}
}
Ok(result)
@ -117,12 +117,12 @@ fn build_node<'a>(
ElementAttr::AttrExpression { .. }
| ElementAttr::CustomAttrExpression { .. } => {
let (name, value) = match &attr.attr {
let (name, value, span) = match &attr.attr {
ElementAttr::AttrExpression { name, value } => {
(name.to_string(), value)
(name.to_string(), value, name.span())
}
ElementAttr::CustomAttrExpression { name, value } => {
(name.value(), value)
(name.value(), value, name.span())
}
_ => unreachable!(),
};
@ -140,6 +140,11 @@ fn build_node<'a>(
is_volatile: false,
namespace,
});
} else {
return Err(Error::ParseError(ParseError::new(
syn::Error::new(span, format!("unknown attribute: {}", name)),
ctx.location.clone(),
)));
}
} else {
return Err(Error::RecompileRequiredError(

View file

@ -150,6 +150,8 @@ impl RsxContext {
fn report_error(&self, error: Error) {
if let Some(handler) = &self.data.write().unwrap().error_handler {
handler.handle_error(error)
} else {
panic!("no error handler set for this platform...\n{}", error);
}
}

View file

@ -62,6 +62,7 @@ use dioxus_core::prelude::Component;
use dioxus_core::SchedulerMsg;
use dioxus_core::VirtualDom;
use futures_util::FutureExt;
use web_sys::console;
mod cache;
mod cfg;
@ -172,50 +173,6 @@ pub fn launch_with_props<T>(
pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T, cfg: WebConfig) {
let mut dom = VirtualDom::new_with_props(root, root_props);
for s in crate::cache::BUILTIN_INTERNED_STRINGS {
wasm_bindgen::intern(s);
}
for s in &cfg.cached_strings {
wasm_bindgen::intern(s);
}
let tasks = dom.get_scheduler_channel();
let sender_callback: Rc<dyn Fn(SchedulerMsg)> =
Rc::new(move |event| tasks.unbounded_send(event).unwrap());
let should_hydrate = cfg.hydrate;
let mut websys_dom = dom::WebsysDom::new(cfg, sender_callback);
log::trace!("rebuilding app");
if should_hydrate {
// todo: we need to split rebuild and initialize into two phases
// it's a waste to produce edits just to get the vdom loaded
let _ = dom.rebuild();
if let Err(err) = websys_dom.rehydrate(&dom) {
log::error!(
"Rehydration failed {:?}. Rebuild DOM into element from scratch",
&err
);
websys_dom.root.set_text_content(None);
// errrrr we should split rebuild into two phases
// one that initializes things and one that produces edits
let edits = dom.rebuild();
websys_dom.apply_edits(edits.edits);
}
} else {
let edits = dom.rebuild();
websys_dom.apply_edits(edits.edits);
}
let mut work_loop = ric_raf::RafLoop::new();
#[cfg(feature = "hot-reload")]
{
use dioxus_rsx_interpreter::error::Error;
@ -274,12 +231,61 @@ pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T
// forward stream to the websocket
dom.base_scope().spawn_forever(async move {
while let Some(err) = error_channel_receiver.next().await {
ws.send_with_str(serde_json::to_string(&err).unwrap().as_str())
.unwrap();
if ws.ready_state() == WebSocket::OPEN {
ws.send_with_str(serde_json::to_string(&err).unwrap().as_str())
.unwrap();
} else {
console::warn_1(&"WebSocket is not open, cannot send error. Run with dioxus serve --hot-reload to enable hot reloading.".into());
panic!("{}", err);
}
}
});
}
for s in crate::cache::BUILTIN_INTERNED_STRINGS {
wasm_bindgen::intern(s);
}
for s in &cfg.cached_strings {
wasm_bindgen::intern(s);
}
let tasks = dom.get_scheduler_channel();
let sender_callback: Rc<dyn Fn(SchedulerMsg)> =
Rc::new(move |event| tasks.unbounded_send(event).unwrap());
let should_hydrate = cfg.hydrate;
let mut websys_dom = dom::WebsysDom::new(cfg, sender_callback);
log::trace!("rebuilding app");
if should_hydrate {
// todo: we need to split rebuild and initialize into two phases
// it's a waste to produce edits just to get the vdom loaded
let _ = dom.rebuild();
if let Err(err) = websys_dom.rehydrate(&dom) {
log::error!(
"Rehydration failed {:?}. Rebuild DOM into element from scratch",
&err
);
websys_dom.root.set_text_content(None);
// errrrr we should split rebuild into two phases
// one that initializes things and one that produces edits
let edits = dom.rebuild();
websys_dom.apply_edits(edits.edits);
}
} else {
let edits = dom.rebuild();
websys_dom.apply_edits(edits.edits);
}
let mut work_loop = ric_raf::RafLoop::new();
loop {
log::trace!("waiting for work");
// if virtualdom has nothing, wait for it to have something before requesting idle time