Browse Source

Switch to bun, clean up web implementation

Jonathan Kelley 1 year ago
parent
commit
22266cc560

+ 2 - 0
packages/desktop/headless_tests/utils.rs

@@ -1,3 +1,5 @@
+#![allow(unused)] // for whatever reason, the compiler is not recognizing the use of these functions
+
 use dioxus::prelude::*;
 use dioxus::prelude::*;
 use dioxus_core::Element;
 use dioxus_core::Element;
 
 

+ 1 - 1
packages/desktop/js/prevent_file_upload.js

@@ -11,7 +11,7 @@
           let target_id = find_real_id(target);
           let target_id = find_real_id(target);
           if (target_id !== null) {
           if (target_id !== null) {
             const send = (event_name) => {
             const send = (event_name) => {
-              const message = window.interpreter.serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), directory: target.getAttribute("webkitdirectory") === "true", multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name });
+              const message = window.interpreter.serializeIpcMessage("file_dialog", { accept: target.getAttribute("accept"), directory: target.getAttribute("webkitdirectory") === "true", multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name });
               window.ipc.postMessage(message);
               window.ipc.postMessage(message);
             };
             };
             send("change&input");
             send("change&input");

+ 1 - 1
packages/desktop/src/ipc.rs

@@ -49,7 +49,7 @@ impl IpcMessage {
     pub(crate) fn method(&self) -> IpcMethod {
     pub(crate) fn method(&self) -> IpcMethod {
         match self.method.as_str() {
         match self.method.as_str() {
             // todo: this is a misspelling, needs to be fixed
             // todo: this is a misspelling, needs to be fixed
-            "file_diolog" => IpcMethod::FileDialog,
+            "file_dialog" => IpcMethod::FileDialog,
             "user_event" => IpcMethod::UserEvent,
             "user_event" => IpcMethod::UserEvent,
             "query" => IpcMethod::Query,
             "query" => IpcMethod::Query,
             "browser_open" => IpcMethod::BrowserOpen,
             "browser_open" => IpcMethod::BrowserOpen,

+ 1 - 1
packages/desktop/src/protocol.rs

@@ -1,5 +1,5 @@
 use crate::{assets::*, edits::EditQueue};
 use crate::{assets::*, edits::EditQueue};
-use dioxus_interpreter_js::unified_bindings::{native_js, SLEDGEHAMMER_JS};
+use dioxus_interpreter_js::unified_bindings::native_js;
 use std::path::{Path, PathBuf};
 use std::path::{Path, PathBuf};
 use wry::{
 use wry::{
     http::{status::StatusCode, Request, Response},
     http::{status::StatusCode, Request, Response},

+ 1 - 1
packages/interpreter/src/js/hash.txt

@@ -1 +1 @@
-4429706825984325407
+3599957386864841107

+ 4 - 1
packages/interpreter/src/js/native.js

@@ -11,13 +11,16 @@ class Interpreter {
     this.handler = handler;
     this.handler = handler;
     this.initialize(root);
     this.initialize(root);
   }
   }
-  initialize(root) {
+  initialize(root, handler = null) {
     this.global = {};
     this.global = {};
     this.local = {};
     this.local = {};
     this.root = root;
     this.root = root;
     this.nodes = [root];
     this.nodes = [root];
     this.stack = [root];
     this.stack = [root];
     this.templates = {};
     this.templates = {};
+    if (handler) {
+      this.handler = handler;
+    }
   }
   }
   createListener(event_name, element, bubbles) {
   createListener(event_name, element, bubbles) {
     if (bubbles) {
     if (bubbles) {

+ 85 - 1
packages/interpreter/src/js/web.js

@@ -11,13 +11,16 @@ class Interpreter {
     this.handler = handler;
     this.handler = handler;
     this.initialize(root);
     this.initialize(root);
   }
   }
-  initialize(root) {
+  initialize(root, handler = null) {
     this.global = {};
     this.global = {};
     this.local = {};
     this.local = {};
     this.root = root;
     this.root = root;
     this.nodes = [root];
     this.nodes = [root];
     this.stack = [root];
     this.stack = [root];
     this.templates = {};
     this.templates = {};
+    if (handler) {
+      this.handler = handler;
+    }
   }
   }
   createListener(event_name, element, bubbles) {
   createListener(event_name, element, bubbles) {
     if (bubbles) {
     if (bubbles) {
@@ -73,6 +76,86 @@ class Interpreter {
   }
   }
 }
 }
 
 
+// src/ts/set_attribute.ts
+function setAttributeInner(node, field, value, ns) {
+  if (ns === "style") {
+    node.style.setProperty(field, value);
+    return;
+  }
+  if (!!ns) {
+    node.setAttributeNS(ns, field, value);
+    return;
+  }
+  switch (field) {
+    case "value":
+      if (node.value !== value) {
+        node.value = value;
+      }
+      break;
+    case "initial_value":
+      node.defaultValue = value;
+      break;
+    case "checked":
+      node.checked = truthy(value);
+      break;
+    case "initial_checked":
+      node.defaultChecked = truthy(value);
+      break;
+    case "selected":
+      node.selected = truthy(value);
+      break;
+    case "initial_selected":
+      node.defaultSelected = truthy(value);
+      break;
+    case "dangerous_inner_html":
+      node.innerHTML = value;
+      break;
+    default:
+      if (!truthy(value) && isBoolAttr(field)) {
+        node.removeAttribute(field);
+      } else {
+        node.setAttribute(field, value);
+      }
+  }
+}
+var truthy = function(val) {
+  return val === "true" || val === true;
+};
+var isBoolAttr = function(field) {
+  switch (field) {
+    case "allowfullscreen":
+    case "allowpaymentrequest":
+    case "async":
+    case "autofocus":
+    case "autoplay":
+    case "checked":
+    case "controls":
+    case "default":
+    case "defer":
+    case "disabled":
+    case "formnovalidate":
+    case "hidden":
+    case "ismap":
+    case "itemscope":
+    case "loop":
+    case "multiple":
+    case "muted":
+    case "nomodule":
+    case "novalidate":
+    case "open":
+    case "playsinline":
+    case "readonly":
+    case "required":
+    case "reversed":
+    case "selected":
+    case "truespeed":
+    case "webkitdirectory":
+      return true;
+    default:
+      return false;
+  }
+};
+
 // src/ts/interpreter_web.ts
 // src/ts/interpreter_web.ts
 class PlatformInterpreter extends Interpreter {
 class PlatformInterpreter extends Interpreter {
   m;
   m;
@@ -126,5 +209,6 @@ class PlatformInterpreter extends Interpreter {
   }
   }
 }
 }
 export {
 export {
+  setAttributeInner,
   PlatformInterpreter
   PlatformInterpreter
 };
 };

+ 6 - 6
packages/interpreter/src/lib.rs

@@ -11,6 +11,12 @@ mod write_native_mutations;
 #[cfg(all(feature = "binary-protocol", feature = "sledgehammer"))]
 #[cfg(all(feature = "binary-protocol", feature = "sledgehammer"))]
 pub use write_native_mutations::*;
 pub use write_native_mutations::*;
 
 
+#[cfg(feature = "sledgehammer")]
+pub mod unified_bindings;
+
+#[cfg(feature = "sledgehammer")]
+pub use unified_bindings::*;
+
 // Common bindings for minimal usage.
 // Common bindings for minimal usage.
 #[cfg(all(feature = "minimal_bindings", feature = "webonly"))]
 #[cfg(all(feature = "minimal_bindings", feature = "webonly"))]
 pub mod minimal_bindings {
 pub mod minimal_bindings {
@@ -28,9 +34,3 @@ pub mod minimal_bindings {
         pub fn collectFormValues(node: JsValue) -> JsValue;
         pub fn collectFormValues(node: JsValue) -> JsValue;
     }
     }
 }
 }
-
-#[cfg(feature = "sledgehammer")]
-pub mod unified_bindings;
-
-#[cfg(feature = "sledgehammer")]
-pub use unified_bindings::*;

+ 5 - 1
packages/interpreter/src/ts/interpreter_core.ts

@@ -28,7 +28,7 @@ export class Interpreter {
     this.initialize(root);
     this.initialize(root);
   }
   }
 
 
-  initialize(root: HTMLElement) {
+  initialize(root: HTMLElement, handler: EventListener | null = null) {
     this.global = {};
     this.global = {};
     this.local = {};
     this.local = {};
     this.root = root;
     this.root = root;
@@ -36,6 +36,10 @@ export class Interpreter {
     this.nodes = [root];
     this.nodes = [root];
     this.stack = [root];
     this.stack = [root];
     this.templates = {};
     this.templates = {};
+
+    if (handler) {
+      this.handler = handler;
+    }
   }
   }
 
 
   createListener(event_name: string, element: HTMLElement, bubbles: boolean) {
   createListener(event_name: string, element: HTMLElement, bubbles: boolean) {

+ 1 - 0
packages/interpreter/src/ts/interpreter_web.ts

@@ -5,6 +5,7 @@
 // We're using sledgehammer directly
 // We're using sledgehammer directly
 
 
 import { Interpreter } from "./interpreter_core";
 import { Interpreter } from "./interpreter_core";
+export { setAttributeInner } from "./set_attribute";
 
 
 export class PlatformInterpreter extends Interpreter {
 export class PlatformInterpreter extends Interpreter {
   m: any;
   m: any;

+ 32 - 28
packages/interpreter/src/unified_bindings.rs

@@ -6,40 +6,13 @@ use wasm_bindgen::prelude::wasm_bindgen;
 
 
 use sledgehammer_bindgen::bindgen;
 use sledgehammer_bindgen::bindgen;
 
 
+/// Combine the interpreter class with the sledgehammer_bindgen generated methods.
 pub fn native_js() -> String {
 pub fn native_js() -> String {
     format!("{}\n{}", include_str!("./js/native.js"), GENERATED_JS,)
     format!("{}\n{}", include_str!("./js/native.js"), GENERATED_JS,)
 }
 }
 
 
 pub const SLEDGEHAMMER_JS: &str = GENERATED_JS;
 pub const SLEDGEHAMMER_JS: &str = GENERATED_JS;
 
 
-/// Extensions to the interpreter that are specific to the web platform.
-#[cfg(feature = "webonly")]
-#[wasm_bindgen(module = "src/js/web.js")]
-extern "C" {
-    pub type WebInterpreter;
-
-    #[wasm_bindgen(method, js_name = "saveTemplate")]
-    pub fn save_template(this: &WebInterpreter, nodes: Vec<Node>, tmpl_id: u16);
-
-    #[wasm_bindgen(method)]
-    pub fn hydrate(this: &WebInterpreter, ids: Vec<u32>);
-
-    #[wasm_bindgen(method, js_name = "getNode")]
-    pub fn get_node(this: &WebInterpreter, id: u32) -> Node;
-}
-
-#[cfg(feature = "webonly")]
-type PlatformInterpreter = WebInterpreter;
-
-#[cfg(feature = "webonly")]
-impl Interpreter {
-    /// Convert the interpreter to a web interpreter, enabling methods like hydrate and save_template.
-    pub fn as_web(&self) -> &WebInterpreter {
-        use wasm_bindgen::prelude::JsCast;
-        &self.js_channel().unchecked_ref()
-    }
-}
-
 #[bindgen(module)]
 #[bindgen(module)]
 mod js {
 mod js {
     /// The interpreter extends the core interpreter which contains the state for the interpreter along with some functions that all platforms use like `AppendChildren`.
     /// The interpreter extends the core interpreter which contains the state for the interpreter along with some functions that all platforms use like `AppendChildren`.
@@ -222,3 +195,34 @@ mod js {
         "{this.els = this.stack.splice(this.stack.length - $n$); let node = this.LoadChild($array$); node.replaceWith(...this.els);}"
         "{this.els = this.stack.splice(this.stack.length - $n$); let node = this.LoadChild($array$); node.replaceWith(...this.els);}"
     }
     }
 }
 }
+
+/// Extensions to the interpreter that are specific to the web platform.
+#[cfg(feature = "webonly")]
+#[wasm_bindgen(module = "src/js/web.js")]
+extern "C" {
+    pub type WebInterpreter;
+
+    #[wasm_bindgen(method)]
+    pub fn initialize(this: &WebInterpreter, root: Node, handler: &js_sys::Function);
+
+    #[wasm_bindgen(method, js_name = "saveTemplate")]
+    pub fn save_template(this: &WebInterpreter, nodes: Vec<Node>, tmpl_id: u16);
+
+    #[wasm_bindgen(method)]
+    pub fn hydrate(this: &WebInterpreter, ids: Vec<u32>);
+
+    #[wasm_bindgen(method, js_name = "getNode")]
+    pub fn get_node(this: &WebInterpreter, id: u32) -> Node;
+}
+
+#[cfg(feature = "webonly")]
+type PlatformInterpreter = WebInterpreter;
+
+#[cfg(feature = "webonly")]
+impl Interpreter {
+    /// Convert the interpreter to a web interpreter, enabling methods like hydrate and save_template.
+    pub fn as_web(&self) -> &WebInterpreter {
+        use wasm_bindgen::prelude::JsCast;
+        &self.js_channel().unchecked_ref()
+    }
+}

+ 0 - 10
packages/interpreter/tsconfig.native.json

@@ -1,10 +0,0 @@
-{
-    // extends the base
-    "extends": "./tsconfig.json",
-    "compilerOptions": {
-        "outFile": "src/js/native.js"
-    },
-    "files": [
-        "src/ts/interpreter_native.ts"
-    ],
-}

+ 0 - 10
packages/interpreter/tsconfig.web.json

@@ -1,10 +0,0 @@
-{
-    // extends the base
-    "extends": "./tsconfig.json",
-    "compilerOptions": {
-        "outFile": "src/js/web.js"
-    },
-    "files": [
-        "src/ts/interpreter_web.ts"
-    ],
-}

+ 1 - 1
packages/liveview/src/lib.rs

@@ -31,7 +31,7 @@ pub enum LiveViewError {
 }
 }
 
 
 fn handle_edits_code() -> String {
 fn handle_edits_code() -> String {
-    use dioxus_interpreter_js::binary_protocol::SLEDGEHAMMER_JS;
+    use dioxus_interpreter_js::unified_bindings::SLEDGEHAMMER_JS;
     use minify_js::{minify, Session, TopLevelMode};
     use minify_js::{minify, Session, TopLevelMode};
 
 
     let serialize_file_uploads = r#"if (
     let serialize_file_uploads = r#"if (

+ 2 - 3
packages/plasmo/src/hooks.rs

@@ -18,7 +18,6 @@ use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers};
 use dioxus_html::input_data::{
 use dioxus_html::input_data::{
     MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons,
     MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons,
 };
 };
-use dioxus_html::FormValue;
 use dioxus_html::{event_bubbles, prelude::*};
 use dioxus_html::{event_bubbles, prelude::*};
 use std::any::Any;
 use std::any::Any;
 use std::collections::HashMap;
 use std::collections::HashMap;
@@ -67,7 +66,7 @@ impl EventData {
 pub struct FormData {
 pub struct FormData {
     pub(crate) value: String,
     pub(crate) value: String,
 
 
-    pub values: HashMap<String, FormValue>,
+    pub values: HashMap<String, String>,
 
 
     pub(crate) files: Option<Files>,
     pub(crate) files: Option<Files>,
 }
 }
@@ -77,7 +76,7 @@ impl HasFormData for FormData {
         self.value.clone()
         self.value.clone()
     }
     }
 
 
-    fn values(&self) -> HashMap<String, FormValue> {
+    fn values(&self) -> HashMap<String, String> {
         self.values.clone()
         self.values.clone()
     }
     }
 
 

+ 3 - 0
packages/web/ric_raf/README.md

@@ -0,0 +1,3 @@
+requestIdleCallback and requestAnimationFrame implemenation
+
+These currently actually slow down our DOM patching and thus are temporarily removed. Technically we can schedule around rIC and rAF but choose not to.

+ 0 - 0
packages/web/src/ric_raf.rs → packages/web/ric_raf/ric_raf.rs


+ 0 - 0
packages/web/src/ricpolyfill.js → packages/web/ric_raf/ricpolyfill.js


+ 38 - 33
packages/web/src/dom.rs

@@ -8,7 +8,7 @@
 
 
 use dioxus_core::ElementId;
 use dioxus_core::ElementId;
 use dioxus_html::PlatformEventData;
 use dioxus_html::PlatformEventData;
-use dioxus_interpreter_js::unified_bindings::{Interpreter, InterpreterInterface};
+use dioxus_interpreter_js::{unified_bindings::Interpreter, WebInterpreter};
 use futures_channel::mpsc;
 use futures_channel::mpsc;
 use rustc_hash::FxHashMap;
 use rustc_hash::FxHashMap;
 use wasm_bindgen::{closure::Closure, JsCast};
 use wasm_bindgen::{closure::Closure, JsCast};
@@ -17,14 +17,16 @@ use web_sys::{Document, Element, Event};
 use crate::{load_document, virtual_event_from_websys_event, Config, WebEventConverter};
 use crate::{load_document, virtual_event_from_websys_event, Config, WebEventConverter};
 
 
 pub struct WebsysDom {
 pub struct WebsysDom {
-    pub(crate) document: Document,
     #[allow(dead_code)]
     #[allow(dead_code)]
     pub(crate) root: Element,
     pub(crate) root: Element,
+    pub(crate) document: Document,
     pub(crate) templates: FxHashMap<String, u16>,
     pub(crate) templates: FxHashMap<String, u16>,
     pub(crate) max_template_id: u16,
     pub(crate) max_template_id: u16,
-    pub(crate) interpreter: InterpreterInterface,
+    pub(crate) interpreter: Interpreter,
+
     #[cfg(feature = "mounted")]
     #[cfg(feature = "mounted")]
     pub(crate) event_channel: mpsc::UnboundedSender<UiEvent>,
     pub(crate) event_channel: mpsc::UnboundedSender<UiEvent>,
+
     #[cfg(feature = "mounted")]
     #[cfg(feature = "mounted")]
     pub(crate) queued_mounted_events: Vec<ElementId>,
     pub(crate) queued_mounted_events: Vec<ElementId>,
 }
 }
@@ -64,7 +66,7 @@ impl WebsysDom {
             }
             }
         };
         };
 
 
-        let interpreter = InterpreterInterface::default();
+        let interpreter = Interpreter::default();
 
 
         let handler: Closure<dyn FnMut(&Event)> = Closure::wrap(Box::new({
         let handler: Closure<dyn FnMut(&Event)> = Closure::wrap(Box::new({
             let event_channel = event_channel.clone();
             let event_channel = event_channel.clone();
@@ -72,42 +74,45 @@ impl WebsysDom {
                 let name = event.type_();
                 let name = event.type_();
                 let element = walk_event_for_id(event);
                 let element = walk_event_for_id(event);
                 let bubbles = dioxus_html::event_bubbles(name.as_str());
                 let bubbles = dioxus_html::event_bubbles(name.as_str());
-                if let Some((element, target)) = element {
-                    let prevent_event;
-                    if let Some(prevent_requests) = target
-                        .get_attribute("dioxus-prevent-default")
-                        .as_deref()
-                        .map(|f| f.split_whitespace())
-                    {
-                        prevent_event = prevent_requests
-                            .map(|f| f.trim_start_matches("on"))
-                            .any(|f| f == name);
-                    } else {
-                        prevent_event = false;
-                    }
 
 
-                    // Prevent forms from submitting and redirecting
-                    if name == "submit" {
-                        // On forms the default behavior is not to submit, if prevent default is set then we submit the form
-                        if !prevent_event {
-                            event.prevent_default();
-                        }
-                    } else if prevent_event {
+                let Some((element, target)) = element else {
+                    return;
+                };
+
+                let prevent_event;
+                if let Some(prevent_requests) = target
+                    .get_attribute("dioxus-prevent-default")
+                    .as_deref()
+                    .map(|f| f.split_whitespace())
+                {
+                    prevent_event = prevent_requests
+                        .map(|f| f.trim_start_matches("on"))
+                        .any(|f| f == name);
+                } else {
+                    prevent_event = false;
+                }
+
+                // Prevent forms from submitting and redirecting
+                if name == "submit" {
+                    // On forms the default behavior is not to submit, if prevent default is set then we submit the form
+                    if !prevent_event {
                         event.prevent_default();
                         event.prevent_default();
                     }
                     }
-
-                    let data = virtual_event_from_websys_event(event.clone(), target);
-                    let _ = event_channel.unbounded_send(UiEvent {
-                        name,
-                        bubbles,
-                        element,
-                        data,
-                    });
+                } else if prevent_event {
+                    event.prevent_default();
                 }
                 }
+
+                let data = virtual_event_from_websys_event(event.clone(), target);
+                let _ = event_channel.unbounded_send(UiEvent {
+                    name,
+                    bubbles,
+                    element,
+                    data,
+                });
             }
             }
         }));
         }));
 
 
-        let _interpreter: &Interpreter = interpreter.as_ref();
+        let _interpreter: &WebInterpreter = interpreter.as_web();
         _interpreter.initialize(
         _interpreter.initialize(
             root.clone().unchecked_into(),
             root.clone().unchecked_into(),
             handler.as_ref().unchecked_ref(),
             handler.as_ref().unchecked_ref(),

+ 8 - 19
packages/web/src/event.rs

@@ -4,7 +4,6 @@ use dioxus_html::{
     point_interaction::{
     point_interaction::{
         InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
         InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
     },
     },
-    prelude::FormValue,
     DragData, FileEngine, FormData, HasDragData, HasFileData, HasFormData, HasImageData,
     DragData, FileEngine, FormData, HasDragData, HasFileData, HasFormData, HasImageData,
     HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, ScrollData,
     HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, ScrollData,
 };
 };
@@ -385,24 +384,14 @@ impl HasFormData for WebFormData {
         .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
         .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
     }
     }
 
 
-    fn values(&self) -> HashMap<String, FormValue> {
+    fn values(&self) -> HashMap<String, String> {
         let mut values = HashMap::new();
         let mut values = HashMap::new();
 
 
-        fn insert_value(map: &mut HashMap<String, FormValue>, key: String, new_value: String) {
-            match map.entry(key) {
-                std::collections::hash_map::Entry::Occupied(mut o) => {
-                    let first_value = match o.get_mut() {
-                        FormValue::Text(data) => std::mem::take(data),
-                        FormValue::VecText(vec) => {
-                            vec.push(new_value);
-                            return;
-                        }
-                    };
-                    let _ = o.insert(FormValue::VecText(vec![first_value, new_value]));
-                }
-                std::collections::hash_map::Entry::Vacant(v) => {
-                    let _ = v.insert(FormValue::Text(new_value));
-                }
+        fn insert_value(map: &mut HashMap<String, String>, key: String, new_value: String) {
+            if let Some(value) = map.get(&key) {
+                map.insert(key, format!("{},{}", value, new_value));
+            } else {
+                map.insert(key, new_value);
             }
             }
         }
         }
 
 
@@ -425,8 +414,8 @@ impl HasFormData for WebFormData {
             }
             }
         } else if let Some(select) = self.element.dyn_ref::<web_sys::HtmlSelectElement>() {
         } else if let Some(select) = self.element.dyn_ref::<web_sys::HtmlSelectElement>() {
             // try to fill in select element values
             // try to fill in select element values
-            let options = get_select_data(select);
-            values.insert("options".to_string(), FormValue::VecText(options));
+            let options = get_select_data(select).join(",");
+            values.insert("options".to_string(), options);
         }
         }
 
 
         values
         values

+ 11 - 11
packages/web/src/lib.rs

@@ -30,16 +30,21 @@ use futures_util::{pin_mut, select, FutureExt, StreamExt};
 
 
 mod cfg;
 mod cfg;
 mod dom;
 mod dom;
-#[cfg(feature = "eval")]
-mod eval;
+
 mod event;
 mod event;
 pub mod launch;
 pub mod launch;
 mod mutations;
 mod mutations;
 pub use event::*;
 pub use event::*;
+
+#[cfg(feature = "eval")]
+mod eval;
+
 #[cfg(feature = "file_engine")]
 #[cfg(feature = "file_engine")]
 mod file_engine;
 mod file_engine;
+
 #[cfg(all(feature = "hot_reload", debug_assertions))]
 #[cfg(all(feature = "hot_reload", debug_assertions))]
 mod hot_reload;
 mod hot_reload;
+
 #[cfg(feature = "hydrate")]
 #[cfg(feature = "hydrate")]
 mod rehydrate;
 mod rehydrate;
 
 
@@ -49,7 +54,7 @@ mod rehydrate;
 ///
 ///
 /// # Example
 /// # Example
 ///
 ///
-/// ```ignore
+/// ```rust, ignore
 /// fn main() {
 /// fn main() {
 ///     let app_fut = dioxus_web::run_with_props(App, RootProps { name: String::from("joe") });
 ///     let app_fut = dioxus_web::run_with_props(App, RootProps { name: String::from("joe") });
 ///     wasm_bindgen_futures::spawn_local(app_fut);
 ///     wasm_bindgen_futures::spawn_local(app_fut);
@@ -61,12 +66,7 @@ pub async fn run(virtual_dom: VirtualDom, web_config: Config) {
     let mut dom = virtual_dom;
     let mut dom = virtual_dom;
 
 
     #[cfg(feature = "eval")]
     #[cfg(feature = "eval")]
-    {
-        // Eval
-        dom.in_runtime(|| {
-            eval::init_eval();
-        });
-    }
+    dom.in_runtime(|| eval::init_eval());
 
 
     #[cfg(feature = "panic_hook")]
     #[cfg(feature = "panic_hook")]
     if web_config.default_panic_hook {
     if web_config.default_panic_hook {
@@ -109,13 +109,12 @@ pub async fn run(virtual_dom: VirtualDom, web_config: Config) {
     websys_dom.mount();
     websys_dom.mount();
 
 
     loop {
     loop {
-        tracing::trace!("waiting for work");
-
         // if virtual dom has nothing, wait for it to have something before requesting idle time
         // if virtual dom has nothing, wait for it to have something before requesting idle time
         // if there is work then this future resolves immediately.
         // if there is work then this future resolves immediately.
         let (mut res, template) = {
         let (mut res, template) = {
             let work = dom.wait_for_work().fuse();
             let work = dom.wait_for_work().fuse();
             pin_mut!(work);
             pin_mut!(work);
+
             let mut rx_next = rx.select_next_some();
             let mut rx_next = rx.select_next_some();
 
 
             #[cfg(all(feature = "hot_reload", debug_assertions))]
             #[cfg(all(feature = "hot_reload", debug_assertions))]
@@ -127,6 +126,7 @@ pub async fn run(virtual_dom: VirtualDom, web_config: Config) {
                     evt = rx_next => (Some(evt), None),
                     evt = rx_next => (Some(evt), None),
                 }
                 }
             }
             }
+
             #[cfg(not(all(feature = "hot_reload", debug_assertions)))]
             #[cfg(not(all(feature = "hot_reload", debug_assertions)))]
             select! {
             select! {
                 _ = work => (None, None),
                 _ = work => (None, None),

+ 32 - 36
packages/web/src/mutations.rs

@@ -5,9 +5,7 @@ use dioxus_core::WriteMutations;
 use dioxus_core::{AttributeValue, ElementId};
 use dioxus_core::{AttributeValue, ElementId};
 use dioxus_html::event_bubbles;
 use dioxus_html::event_bubbles;
 use dioxus_html::PlatformEventData;
 use dioxus_html::PlatformEventData;
-use dioxus_interpreter_js::get_node;
 use dioxus_interpreter_js::minimal_bindings;
 use dioxus_interpreter_js::minimal_bindings;
-use dioxus_interpreter_js::save_template;
 use wasm_bindgen::JsCast;
 use wasm_bindgen::JsCast;
 use wasm_bindgen::JsValue;
 use wasm_bindgen::JsValue;
 
 
@@ -58,19 +56,23 @@ impl WebsysDom {
 
 
     pub fn flush_edits(&mut self) {
     pub fn flush_edits(&mut self) {
         self.interpreter.flush();
         self.interpreter.flush();
-        #[cfg(feature = "mounted")]
+
         // Now that we've flushed the edits and the dom nodes exist, we can send the mounted events.
         // Now that we've flushed the edits and the dom nodes exist, we can send the mounted events.
-        {
-            for id in self.queued_mounted_events.drain(..) {
-                let node = get_node(self.interpreter.js_channel(), id.0 as u32);
-                if let Some(element) = node.dyn_ref::<web_sys::Element>() {
-                    let _ = self.event_channel.unbounded_send(UiEvent {
-                        name: "mounted".to_string(),
-                        bubbles: false,
-                        element: id,
-                        data: PlatformEventData::new(Box::new(element.clone())),
-                    });
-                }
+        #[cfg(feature = "mounted")]
+        self.flush_queued_mounted_events();
+    }
+
+    #[cfg(feature = "mounted")]
+    fn flush_queued_mounted_events(&mut self) {
+        for id in self.queued_mounted_events.drain(..) {
+            let node = self.interpreter.as_web().get_node(id.0 as u32);
+            if let Some(element) = node.dyn_ref::<web_sys::Element>() {
+                let _ = self.event_channel.unbounded_send(UiEvent {
+                    name: "mounted".to_string(),
+                    bubbles: false,
+                    element: id,
+                    data: PlatformEventData::new(Box::new(element.clone())),
+                });
             }
             }
         }
         }
     }
     }
@@ -84,14 +86,14 @@ impl WebsysDom {
 impl WriteMutations for WebsysDom {
 impl WriteMutations for WebsysDom {
     fn register_template(&mut self, template: Template) {
     fn register_template(&mut self, template: Template) {
         let mut roots = vec![];
         let mut roots = vec![];
-
         for root in template.roots {
         for root in template.roots {
             roots.push(self.create_template_node(root))
             roots.push(self.create_template_node(root))
         }
         }
-
         self.templates
         self.templates
             .insert(template.name.to_owned(), self.max_template_id);
             .insert(template.name.to_owned(), self.max_template_id);
-        save_template(self.interpreter.js_channel(), roots, self.max_template_id);
+        self.interpreter
+            .as_web()
+            .save_template(roots, self.max_template_id);
         self.max_template_id += 1
         self.max_template_id += 1
     }
     }
 
 
@@ -184,30 +186,24 @@ impl WriteMutations for WebsysDom {
     }
     }
 
 
     fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
     fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
-        match name {
-            // mounted events are fired immediately after the element is mounted.
-            "mounted" => {
-                #[cfg(feature = "mounted")]
-                self.send_mount_event(id);
-            }
-            _ => {
-                self.interpreter
-                    .new_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
-            }
+        // mounted events are fired immediately after the element is mounted.
+        if name == "mounted" {
+            #[cfg(feature = "mounted")]
+            self.send_mount_event(id);
+            return;
         }
         }
+
+        self.interpreter
+            .new_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
     }
     }
 
 
     fn remove_event_listener(&mut self, name: &'static str, id: ElementId) {
     fn remove_event_listener(&mut self, name: &'static str, id: ElementId) {
-        match name {
-            "mounted" => {}
-            _ => {
-                self.interpreter.remove_event_listener(
-                    name,
-                    id.0 as u32,
-                    event_bubbles(name) as u8,
-                );
-            }
+        if name == "mounted" {
+            return;
         }
         }
+
+        self.interpreter
+            .remove_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
     }
     }
 
 
     fn remove_node(&mut self, id: ElementId) {
     fn remove_node(&mut self, id: ElementId) {

+ 6 - 9
packages/web/src/rehydrate.rs

@@ -3,7 +3,6 @@ use dioxus_core::prelude::*;
 use dioxus_core::AttributeValue;
 use dioxus_core::AttributeValue;
 use dioxus_core::WriteMutations;
 use dioxus_core::WriteMutations;
 use dioxus_core::{DynamicNode, ElementId, ScopeState, TemplateNode, VNode, VirtualDom};
 use dioxus_core::{DynamicNode, ElementId, ScopeState, TemplateNode, VNode, VirtualDom};
-use dioxus_interpreter_js::save_template;
 
 
 #[derive(Debug)]
 #[derive(Debug)]
 pub enum RehydrationError {
 pub enum RehydrationError {
@@ -23,7 +22,7 @@ impl WebsysDom {
         // Recursively rehydrate the dom from the VirtualDom
         // Recursively rehydrate the dom from the VirtualDom
         self.rehydrate_scope(root_scope, dom, &mut ids, &mut to_mount)?;
         self.rehydrate_scope(root_scope, dom, &mut ids, &mut to_mount)?;
 
 
-        dioxus_interpreter_js::hydrate(self.interpreter.js_channel(), ids);
+        self.interpreter.as_web().hydrate(ids);
 
 
         #[cfg(feature = "mounted")]
         #[cfg(feature = "mounted")]
         for id in to_mount {
         for id in to_mount {
@@ -40,8 +39,7 @@ impl WebsysDom {
         ids: &mut Vec<u32>,
         ids: &mut Vec<u32>,
         to_mount: &mut Vec<ElementId>,
         to_mount: &mut Vec<ElementId>,
     ) -> Result<(), RehydrationError> {
     ) -> Result<(), RehydrationError> {
-        let vnode = scope.root_node();
-        self.rehydrate_vnode(dom, vnode, ids, to_mount)
+        self.rehydrate_vnode(dom, scope.root_node(), ids, to_mount)
     }
     }
 
 
     fn rehydrate_vnode(
     fn rehydrate_vnode(
@@ -168,11 +166,10 @@ impl WriteMutations for OnlyWriteTemplates<'_> {
         self.0
         self.0
             .templates
             .templates
             .insert(template.name.to_owned(), self.0.max_template_id);
             .insert(template.name.to_owned(), self.0.max_template_id);
-        save_template(
-            self.0.interpreter.js_channel(),
-            roots,
-            self.0.max_template_id,
-        );
+        self.0
+            .interpreter
+            .as_web()
+            .save_template(roots, self.0.max_template_id);
         self.0.max_template_id += 1
         self.0.max_template_id += 1
     }
     }