Browse Source

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

Evan Almloff 3 years ago
parent
commit
f0655a11ad

+ 2 - 0
packages/desktop/src/controller.rs

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

+ 2 - 2
packages/rsx_interpreter/src/captuered_context.rs

@@ -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{

+ 17 - 0
packages/rsx_interpreter/src/error.rs

@@ -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)
+            }
+        }
+    }
+}

+ 9 - 4
packages/rsx_interpreter/src/interperter.rs

@@ -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(

+ 2 - 0
packages/rsx_interpreter/src/lib.rs

@@ -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);
         }
     }
 

+ 52 - 46
packages/web/src/lib.rs

@@ -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