Bläddra i källkod

Web works with the js structure

Jonathan Kelley 1 år sedan
förälder
incheckning
b751674354

+ 0 - 2
Cargo.lock

@@ -8527,7 +8527,6 @@ dependencies = [
 [[package]]
 name = "sledgehammer_bindgen"
 version = "0.4.0"
-source = "git+https://github.com/ealmloff/sledgehammer_bindgen#6f90290e1655014f22623eb8b2ea41fb098e1164"
 dependencies = [
  "sledgehammer_bindgen_macro",
  "wasm-bindgen",
@@ -8536,7 +8535,6 @@ dependencies = [
 [[package]]
 name = "sledgehammer_bindgen_macro"
 version = "0.4.0"
-source = "git+https://github.com/ealmloff/sledgehammer_bindgen#6f90290e1655014f22623eb8b2ea41fb098e1164"
 dependencies = [
  "quote",
  "syn 2.0.51",

+ 2 - 1
packages/interpreter/Cargo.toml

@@ -17,7 +17,8 @@ web-sys = { version = "0.3.56", optional = true, features = [
     "Element",
     "Node",
 ] }
-sledgehammer_bindgen = { git = "https://github.com/ealmloff/sledgehammer_bindgen", default-features = false, optional = true }
+sledgehammer_bindgen = { path = "../../../Tinkering/sledgehammer_bindgen", default-features = false, optional = true }
+# sledgehammer_bindgen = { git = "https://github.com/ealmloff/sledgehammer_bindgen", default-features = false, optional = true }
 sledgehammer_utils = { version = "0.2", optional = true }
 serde = { version = "1.0", features = ["derive"], optional = true }
 

+ 3 - 2
packages/interpreter/build.rs

@@ -20,8 +20,9 @@ fn main() {
 
     // Otherwise, generate the bindings and write the new hash to disk
     // Generate the bindings for both native and web
-    gen_bindings("interpreter_native", "native");
-    gen_bindings("interpreter_web", "web");
+    gen_bindings("common", "common");
+    gen_bindings("native", "native");
+    gen_bindings("core", "core");
 
     std::fs::write("src/js/hash.txt", hash.to_string()).unwrap();
 }

+ 99 - 0
packages/interpreter/src/js/common.js

@@ -0,0 +1,99 @@
+// 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/form.ts
+function retrieveFormValues(form) {
+  const formData = new FormData(form);
+  const contents = {};
+  formData.forEach((value, key) => {
+    if (contents[key]) {
+      contents[key] += "," + value;
+    } else {
+      contents[key] = value;
+    }
+  });
+  return {
+    valid: form.checkValidity(),
+    values: contents
+  };
+}
+export {
+  setAttributeInner,
+  retrieveFormValues
+};

+ 79 - 84
packages/interpreter/src/js/web.js → packages/interpreter/src/js/core.js

@@ -1,81 +1,3 @@
-// src/ts/interpreter_core.ts
-class Interpreter {
-  global;
-  local;
-  root;
-  handler;
-  nodes;
-  stack;
-  templates;
-  constructor(root, handler) {
-    this.handler = handler;
-    this.initialize(root);
-  }
-  initialize(root, handler = null) {
-    this.global = {};
-    this.local = {};
-    this.root = root;
-    this.nodes = [root];
-    this.stack = [root];
-    this.templates = {};
-    if (handler) {
-      this.handler = handler;
-    }
-  }
-  createListener(event_name, element, bubbles) {
-    if (bubbles) {
-      if (this.global[event_name] === undefined) {
-        this.global[event_name] = { active: 1, callback: this.handler };
-        this.root.addEventListener(event_name, this.handler);
-      } else {
-        this.global[event_name].active++;
-      }
-    } else {
-      const id = element.getAttribute("data-dioxus-id");
-      if (!this.local[id]) {
-        this.local[id] = {};
-      }
-      element.addEventListener(event_name, this.handler);
-    }
-  }
-  removeListener(element, event_name, bubbles) {
-    if (bubbles) {
-      this.removeBubblingListener(event_name);
-    } else {
-      this.removeNonBubblingListener(element, event_name);
-    }
-  }
-  removeBubblingListener(event_name) {
-    this.global[event_name].active--;
-    if (this.global[event_name].active === 0) {
-      this.root.removeEventListener(event_name, this.global[event_name].callback);
-      delete this.global[event_name];
-    }
-  }
-  removeNonBubblingListener(element, event_name) {
-    const id = element.getAttribute("data-dioxus-id");
-    delete this.local[id][event_name];
-    if (Object.keys(this.local[id]).length === 0) {
-      delete this.local[id];
-    }
-    element.removeEventListener(event_name, this.handler);
-  }
-  removeAllNonBubblingListeners(element) {
-    const id = element.getAttribute("data-dioxus-id");
-    delete this.local[id];
-  }
-  getNode(id) {
-    return this.nodes[id];
-  }
-  appendChildren(id, many) {
-    const root = this.nodes[id];
-    const els = this.stack.splice(this.stack.length - many);
-    for (let k = 0;k < many; k++) {
-      root.appendChild(els[k]);
-    }
-  }
-}
-
 // src/ts/set_attribute.ts
 function setAttributeInner(node, field, value, ns) {
   if (ns === "style") {
@@ -156,13 +78,84 @@ var isBoolAttr = function(field) {
   }
 };
 
-// src/ts/interpreter_web.ts
-class PlatformInterpreter extends Interpreter {
+// src/ts/core.ts
+class BaseInterpreter {
+  global;
+  local;
+  root;
+  handler;
+  nodes;
+  stack;
+  templates;
   m;
   constructor(root, handler) {
-    super(root, handler);
+    this.handler = handler;
+    this.initialize(root);
+  }
+  initialize(root, handler = null) {
+    this.global = {};
+    this.local = {};
+    this.root = root;
+    this.nodes = [root];
+    this.stack = [root];
+    this.templates = {};
+    if (handler) {
+      this.handler = handler;
+    }
+  }
+  createListener(event_name, element, bubbles) {
+    if (bubbles) {
+      if (this.global[event_name] === undefined) {
+        this.global[event_name] = { active: 1, callback: this.handler };
+        this.root.addEventListener(event_name, this.handler);
+      } else {
+        this.global[event_name].active++;
+      }
+    } else {
+      const id = element.getAttribute("data-dioxus-id");
+      if (!this.local[id]) {
+        this.local[id] = {};
+      }
+      element.addEventListener(event_name, this.handler);
+    }
   }
-  LoadChild(ptr, len) {
+  removeListener(element, event_name, bubbles) {
+    if (bubbles) {
+      this.removeBubblingListener(event_name);
+    } else {
+      this.removeNonBubblingListener(element, event_name);
+    }
+  }
+  removeBubblingListener(event_name) {
+    this.global[event_name].active--;
+    if (this.global[event_name].active === 0) {
+      this.root.removeEventListener(event_name, this.global[event_name].callback);
+      delete this.global[event_name];
+    }
+  }
+  removeNonBubblingListener(element, event_name) {
+    const id = element.getAttribute("data-dioxus-id");
+    delete this.local[id][event_name];
+    if (Object.keys(this.local[id]).length === 0) {
+      delete this.local[id];
+    }
+    element.removeEventListener(event_name, this.handler);
+  }
+  removeAllNonBubblingListeners(element) {
+    const id = element.getAttribute("data-dioxus-id");
+    delete this.local[id];
+  }
+  getNode(id) {
+    return this.nodes[id];
+  }
+  appendChildren(id, many) {
+    const root = this.nodes[id];
+    const els = this.stack.splice(this.stack.length - many);
+    for (let k = 0;k < many; k++) {
+      root.appendChild(els[k]);
+    }
+  }
+  loadChild(ptr, len) {
     let node = this.stack[this.stack.length - 1];
     let ptr_end = ptr + len;
     for (;ptr < ptr_end; ptr++) {
@@ -207,8 +200,10 @@ class PlatformInterpreter extends Interpreter {
       currentNode = treeWalker.nextNode();
     }
   }
+  setAttributeInner(node, field, value, ns) {
+    setAttributeInner(node, field, value, ns);
+  }
 }
 export {
-  setAttributeInner,
-  PlatformInterpreter
+  BaseInterpreter
 };

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

@@ -1 +1 @@
-3599957386864841107
+7906913262979346544

+ 133 - 84
packages/interpreter/src/js/native.js

@@ -1,5 +1,85 @@
-// src/ts/interpreter_core.ts
-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/core.ts
+class BaseInterpreter {
   global;
   local;
   root;
@@ -7,6 +87,7 @@ class Interpreter {
   nodes;
   stack;
   templates;
+  m;
   constructor(root, handler) {
     this.handler = handler;
     this.initialize(root);
@@ -74,6 +155,54 @@ class Interpreter {
       root.appendChild(els[k]);
     }
   }
+  loadChild(ptr, len) {
+    let node = this.stack[this.stack.length - 1];
+    let ptr_end = ptr + len;
+    for (;ptr < ptr_end; ptr++) {
+      let end = this.m.getUint8(ptr);
+      for (node = node.firstChild;end > 0; end--) {
+        node = node.nextSibling;
+      }
+    }
+    return node;
+  }
+  saveTemplate(nodes, tmpl_id) {
+    this.templates[tmpl_id] = nodes;
+  }
+  hydrateRoot(ids) {
+    const hydrateNodes = document.querySelectorAll("[data-node-hydration]");
+    for (let i = 0;i < hydrateNodes.length; i++) {
+      const hydrateNode = hydrateNodes[i];
+      const hydration = hydrateNode.getAttribute("data-node-hydration");
+      const split = hydration.split(",");
+      const id = ids[parseInt(split[0])];
+      this.nodes[id] = hydrateNode;
+      if (split.length > 1) {
+        hydrateNode.listening = split.length - 1;
+        hydrateNode.setAttribute("data-dioxus-id", id.toString());
+        for (let j = 1;j < split.length; j++) {
+          const listener = split[j];
+          const split2 = listener.split(":");
+          const event_name = split2[0];
+          const bubbles = split2[1] === "1";
+          this.createListener(event_name, hydrateNode, bubbles);
+        }
+      }
+    }
+    const treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT);
+    let currentNode = treeWalker.nextNode();
+    while (currentNode) {
+      const id = currentNode.textContent;
+      const split = id.split("node-id");
+      if (split.length > 1) {
+        this.nodes[ids[parseInt(split[1])]] = currentNode.nextSibling;
+      }
+      currentNode = treeWalker.nextNode();
+    }
+  }
+  setAttributeInner(node, field, value, ns) {
+    setAttributeInner(node, field, value, ns);
+  }
 }
 
 // src/ts/form.ts
@@ -291,87 +420,7 @@ var serializeDragEvent = function(event) {
   };
 };
 
-// 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_native.ts
+// src/ts/native.ts
 var targetId = function(target) {
   if (!(target instanceof Node)) {
     return null;
@@ -390,7 +439,7 @@ var targetId = function(target) {
   return parseInt(realId);
 };
 
-class PlatformInterpreter extends Interpreter {
+class PlatformInterpreter extends BaseInterpreter {
   intercept_link_redirects;
   ipc;
   liveview;

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

@@ -2,8 +2,11 @@
 #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
 #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
 
-pub static INTERPRETER_JS: &str = include_str!("./js/native.js");
-pub static COMMON_JS: &str = include_str!("./js/web.js");
+/// The base class that the JS channel will extend
+pub static INTERPRETER_JS: &str = include_str!("./js/core.js");
+
+/// The code explicitly for desktop/liveview that bridges the eval gap between the two
+pub static NATIVE_JS: &str = include_str!("./js/native.js");
 
 #[cfg(all(feature = "binary-protocol", feature = "sledgehammer"))]
 mod write_native_mutations;
@@ -25,7 +28,7 @@ pub mod minimal_bindings {
     /// Some useful snippets that we use to share common functionality between the different platforms we support.
     ///
     /// This maintains some sort of consistency between web, desktop, and liveview
-    #[wasm_bindgen(module = "/src/js/web.js")]
+    #[wasm_bindgen(module = "/src/js/common.js")]
     extern "C" {
         /// Set the attribute of the node
         pub fn setAttributeInner(node: JsValue, name: &str, value: JsValue, ns: Option<&str>);

+ 2 - 0
packages/interpreter/src/ts/common.ts

@@ -0,0 +1,2 @@
+export { setAttributeInner } from "./set_attribute";
+export { retrieveFormValues } from "./form";

+ 73 - 1
packages/interpreter/src/ts/interpreter_core.ts → packages/interpreter/src/ts/core.ts

@@ -1,9 +1,11 @@
 // The root interpreter class that holds state about the mapping between DOM and VirtualDom
 // This always lives in the JS side of things, and is extended by the native and web interpreters
 
+import { setAttributeInner } from "./set_attribute";
+
 export type NodeId = number;
 
-export class Interpreter {
+export class BaseInterpreter {
   // non bubbling events listen at the element the listener was created at
   global: {
     [key: string]: { active: number, callback: EventListener }
@@ -23,6 +25,9 @@ export class Interpreter {
     [key: string]: Node[]
   };
 
+  // sledgehammer is generating this...
+  m: any;
+
   constructor(root: HTMLElement, handler: EventListener) {
     this.handler = handler;
     this.initialize(root);
@@ -100,5 +105,72 @@ export class Interpreter {
       root.appendChild(els[k]);
     }
   }
+
+  loadChild(ptr: number, len: number): Node {
+    // iterate through each number and get that child
+    let node = this.stack[this.stack.length - 1] as Node;
+    let ptr_end = ptr + len;
+
+    for (; ptr < ptr_end; ptr++) {
+      let end = this.m.getUint8(ptr);
+      for (node = node.firstChild; end > 0; end--) {
+        node = node.nextSibling;
+      }
+    }
+
+    return node;
+  }
+
+  saveTemplate(nodes: HTMLElement[], tmpl_id: string) {
+    this.templates[tmpl_id] = nodes;
+  }
+
+  hydrateRoot(ids: { [key: number]: number }) {
+    const hydrateNodes = document.querySelectorAll('[data-node-hydration]');
+
+    for (let i = 0; i < hydrateNodes.length; i++) {
+      const hydrateNode = hydrateNodes[i] as HTMLElement;
+      const hydration = hydrateNode.getAttribute('data-node-hydration');
+      const split = hydration!.split(',');
+      const id = ids[parseInt(split[0])];
+
+      this.nodes[id] = hydrateNode;
+
+      if (split.length > 1) {
+        // @ts-ignore
+        hydrateNode.listening = split.length - 1;
+        hydrateNode.setAttribute('data-dioxus-id', id.toString());
+        for (let j = 1; j < split.length; j++) {
+          const listener = split[j];
+          const split2 = listener.split(':');
+          const event_name = split2[0];
+          const bubbles = split2[1] === '1';
+          this.createListener(event_name, hydrateNode, bubbles);
+        }
+      }
+    }
+
+    const treeWalker = document.createTreeWalker(
+      document.body,
+      NodeFilter.SHOW_COMMENT,
+    );
+
+    let currentNode = treeWalker.nextNode();
+
+    while (currentNode) {
+      const id = currentNode.textContent!;
+      const split = id.split('node-id');
+
+      if (split.length > 1) {
+        this.nodes[ids[parseInt(split[1])]] = currentNode.nextSibling;
+      }
+
+      currentNode = treeWalker.nextNode();
+    }
+  }
+
+  setAttributeInner(node: HTMLElement, field: string, value: string, ns: string) {
+    setAttributeInner(node, field, value, ns);
+  }
 }
 

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

@@ -1,79 +0,0 @@
-// The JS<->Rust bridge
-// This file is thin, suitable just for the web
-// If you want the more full-featured intrepreter, look at the native interpreter which extends this with additional functionality
-//
-// We're using sledgehammer directly
-
-import { Interpreter } from "./interpreter_core";
-export { setAttributeInner } from "./set_attribute";
-
-export class PlatformInterpreter extends Interpreter {
-  m: any;
-
-  constructor(root: HTMLElement, handler: EventListener) {
-    super(root, handler);
-  }
-
-  LoadChild(ptr: number, len: number): Node {
-    // iterate through each number and get that child
-    let node = this.stack[this.stack.length - 1] as Node;
-    let ptr_end = ptr + len;
-
-    for (; ptr < ptr_end; ptr++) {
-      let end = this.m.getUint8(ptr);
-      for (node = node.firstChild; end > 0; end--) {
-        node = node.nextSibling;
-      }
-    }
-
-    return node;
-  }
-
-  saveTemplate(nodes: HTMLElement[], tmpl_id: string) {
-    this.templates[tmpl_id] = nodes;
-  }
-
-  hydrateRoot(ids: { [key: number]: number }) {
-    const hydrateNodes = document.querySelectorAll('[data-node-hydration]');
-
-    for (let i = 0; i < hydrateNodes.length; i++) {
-      const hydrateNode = hydrateNodes[i] as HTMLElement;
-      const hydration = hydrateNode.getAttribute('data-node-hydration');
-      const split = hydration!.split(',');
-      const id = ids[parseInt(split[0])];
-
-      this.nodes[id] = hydrateNode;
-
-      if (split.length > 1) {
-        // @ts-ignore
-        hydrateNode.listening = split.length - 1;
-        hydrateNode.setAttribute('data-dioxus-id', id.toString());
-        for (let j = 1; j < split.length; j++) {
-          const listener = split[j];
-          const split2 = listener.split(':');
-          const event_name = split2[0];
-          const bubbles = split2[1] === '1';
-          this.createListener(event_name, hydrateNode, bubbles);
-        }
-      }
-    }
-
-    const treeWalker = document.createTreeWalker(
-      document.body,
-      NodeFilter.SHOW_COMMENT,
-    );
-
-    let currentNode = treeWalker.nextNode();
-
-    while (currentNode) {
-      const id = currentNode.textContent!;
-      const split = id.split('node-id');
-
-      if (split.length > 1) {
-        this.nodes[ids[parseInt(split[1])]] = currentNode.nextSibling;
-      }
-
-      currentNode = treeWalker.nextNode();
-    }
-  }
-}

+ 2 - 2
packages/interpreter/src/ts/interpreter_native.ts → packages/interpreter/src/ts/native.ts

@@ -3,11 +3,11 @@
 // This file lives on the renderer, not the host. It's basically a polyfill over functionality that the host can't
 // provide since it doesn't have access to the dom.
 
-import { Interpreter, NodeId } from "./interpreter_core";
+import { BaseInterpreter, NodeId } from "./core";
 import { SerializedEvent, serializeEvent } from "./serialize";
 import { setAttributeInner } from "./set_attribute";
 
-export class PlatformInterpreter extends Interpreter {
+export class PlatformInterpreter extends BaseInterpreter {
   intercept_link_redirects: boolean;
   ipc: any;
 

+ 60 - 56
packages/interpreter/src/unified_bindings.rs

@@ -1,32 +1,54 @@
 #[cfg(feature = "webonly")]
 use web_sys::Node;
 
+pub const SLEDGEHAMMER_JS: &str = GENERATED_JS;
+
 #[cfg(feature = "webonly")]
-use wasm_bindgen::prelude::wasm_bindgen;
+#[wasm_bindgen::prelude::wasm_bindgen]
+extern "C" {
+    pub type BaseInterpreter;
 
-use sledgehammer_bindgen::bindgen;
+    #[wasm_bindgen(method)]
+    pub fn initialize(this: &BaseInterpreter, root: Node, handler: &js_sys::Function);
 
-/// Combine the interpreter class with the sledgehammer_bindgen generated methods.
-pub fn native_js() -> String {
-    format!("{}\n{}", include_str!("./js/native.js"), GENERATED_JS,)
+    #[wasm_bindgen(method, js_name = "saveTemplate")]
+    pub fn save_template(this: &BaseInterpreter, nodes: Vec<Node>, tmpl_id: u16);
+
+    #[wasm_bindgen(method)]
+    pub fn hydrate(this: &BaseInterpreter, ids: Vec<u32>);
+
+    #[wasm_bindgen(method, js_name = "getNode")]
+    pub fn get_node(this: &BaseInterpreter, id: u32) -> Node;
 }
 
-pub const SLEDGEHAMMER_JS: &str = GENERATED_JS;
+// Note that this impl is for the sledgehammer interpreter to allow us dropping down to the base interpreter
+// During hydration and initialization we need to the base interpreter methods
+#[cfg(feature = "webonly")]
+impl Interpreter {
+    /// Convert the interpreter to its baseclass, giving
+    pub fn base(&self) -> &BaseInterpreter {
+        use wasm_bindgen::prelude::JsCast;
+        &self.js_channel().unchecked_ref()
+    }
+}
 
-#[bindgen(module)]
+#[sledgehammer_bindgen::bindgen(module)]
 mod js {
+    // Extend the web base class
+    const BASE: &str = "./src/js/core.js";
+
     /// The interpreter extends the core interpreter which contains the state for the interpreter along with some functions that all platforms use like `AppendChildren`.
-    #[extends(PlatformInterpreter)]
+    #[extends(BaseInterpreter)]
     pub struct Interpreter;
 
     fn mount_to_root() {
-        "{this.AppendChildren(this.root, this.stack.length-1);}"
+        "{this.appendChildren(this.root, this.stack.length-1);}"
     }
     fn push_root(root: u32) {
         "{this.stack.push(this.nodes[$root$]);}"
     }
     fn append_children(id: u32, many: u16) {
-        "{this.AppendChildren($id$, $many$);}"
+        "{this.appendChildren($id$, $many$);}"
     }
     fn pop_root() {
         "{this.stack.pop();}"
@@ -93,11 +115,11 @@ mod js {
         }"#
     }
     fn assign_id(ptr: u32, len: u8, id: u32) {
-        "{this.nodes[$id$] = this.LoadChild($ptr$, $len$);}"
+        "{this.nodes[$id$] = this.loadChild($ptr$, $len$);}"
     }
     fn hydrate_text(ptr: u32, len: u8, value: &str, id: u32) {
         r#"{
-            let node = this.LoadChild($ptr$, $len$);
+            let node = this.loadChild($ptr$, $len$);
             if (node.nodeType == node.TEXT_NODE) {
                 node.textContent = value;
             } else {
@@ -109,20 +131,13 @@ mod js {
         }"#
     }
     fn replace_placeholder(ptr: u32, len: u8, n: u16) {
-        "{this.els = this.stack.splice(this.stack.length - $n$); let node = this.LoadChild($ptr$, $len$); node.replaceWith(...this.els);}"
+        "{this.els = this.stack.splice(this.stack.length - $n$); let node = this.loadChild($ptr$, $len$); node.replaceWith(...this.els);}"
     }
     fn load_template(tmpl_id: u16, index: u16, id: u32) {
         "{let node = this.templates[$tmpl_id$][$index$].cloneNode(true); this.nodes[$id$] = node; this.stack.push(node);}"
     }
 
-    /*
-    Binary protocol methods only!
-
-    These methods let us support binary packing mutations for use on boundaries like desktop where we prefer to send
-    binary data instead of JSON.
-
-    We're using native types in a number of places
-    */
+    #[cfg(feature = "binary-protocol")]
     fn append_children_to_top(many: u16) {
         "{
         let root = this.stack[this.stack.length-many-1];
@@ -130,23 +145,35 @@ mod js {
         for (let k = 0; k < many; k++) {
             root.appendChild(this.els[k]);
         }
-    }"
+        }"
     }
+
+    #[cfg(feature = "binary-protocol")]
     fn set_top_attribute(field: &str<u8, attr>, value: &str, ns: &str<u8, ns_cache>) {
         "{this.setAttributeInner(this.stack[this.stack.length-1], $field$, $value$, $ns$);}"
     }
+
+    #[cfg(feature = "binary-protocol")]
     fn add_placeholder() {
         "{let node = document.createElement('pre'); node.hidden = true; this.stack.push(node);}"
     }
+
+    #[cfg(feature = "binary-protocol")]
     fn create_element(element: &'static str<u8, el>) {
         "{this.stack.push(document.createElement($element$))}"
     }
+
+    #[cfg(feature = "binary-protocol")]
     fn create_element_ns(element: &'static str<u8, el>, ns: &'static str<u8, namespace>) {
         "{this.stack.push(document.createElementNS($ns$, $element$))}"
     }
+
+    #[cfg(feature = "binary-protocol")]
     fn add_templates(tmpl_id: u16, len: u16) {
         "{this.templates[$tmpl_id$] = this.stack.splice(this.stack.length-$len$);}"
     }
+
+    #[cfg(feature = "binary-protocol")]
     fn foreign_event_listener(event: &str<u8, evt>, id: u32, bubbles: u8) {
         r#"
     bubbles = bubbles == 1;
@@ -175,12 +202,18 @@ mod js {
         });
     }"#
     }
+
+    /// Assign the ID
+    #[cfg(feature = "binary-protocol")]
     fn assign_id_ref(array: &[u8], id: u32) {
-        "{this.nodes[$id$] = this.LoadChild($array$);}"
+        "{this.nodes[$id$] = this.loadChild($array$);}"
     }
+
+    /// The coolest ID ever!
+    #[cfg(feature = "binary-protocol")]
     fn hydrate_text_ref(array: &[u8], value: &str, id: u32) {
         r#"{
-        let node = this.LoadChild($array$);
+        let node = this.loadChild($array$);
         if (node.nodeType == node.TEXT_NODE) {
             node.textContent = value;
         } else {
@@ -191,38 +224,9 @@ mod js {
         this.nodes[$id$] = node;
     }"#
     }
-    fn replace_placeholder_ref(array: &[u8], n: u16) {
-        "{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()
+    #[cfg(feature = "binary-protocol")]
+    fn replace_placeholder_ref(array: &[u8], n: u16) {
+        "{this.els = this.stack.splice(this.stack.length - $n$); let node = this.loadChild($array$); node.replaceWith(...this.els);}"
     }
 }

+ 2 - 2
packages/web/src/dom.rs

@@ -8,7 +8,7 @@
 
 use dioxus_core::ElementId;
 use dioxus_html::PlatformEventData;
-use dioxus_interpreter_js::{unified_bindings::Interpreter, WebInterpreter};
+use dioxus_interpreter_js::unified_bindings::Interpreter;
 use futures_channel::mpsc;
 use rustc_hash::FxHashMap;
 use wasm_bindgen::{closure::Closure, JsCast};
@@ -112,7 +112,7 @@ impl WebsysDom {
             }
         }));
 
-        let _interpreter: &WebInterpreter = interpreter.as_web();
+        let _interpreter = interpreter.base();
         _interpreter.initialize(
             root.clone().unchecked_into(),
             handler.as_ref().unchecked_ref(),

+ 2 - 2
packages/web/src/mutations.rs

@@ -65,7 +65,7 @@ impl WebsysDom {
     #[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);
+            let node = self.interpreter.base().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(),
@@ -92,7 +92,7 @@ impl WriteMutations for WebsysDom {
         self.templates
             .insert(template.name.to_owned(), self.max_template_id);
         self.interpreter
-            .as_web()
+            .base()
             .save_template(roots, self.max_template_id);
         self.max_template_id += 1
     }

+ 2 - 2
packages/web/src/rehydrate.rs

@@ -22,7 +22,7 @@ impl WebsysDom {
         // Recursively rehydrate the dom from the VirtualDom
         self.rehydrate_scope(root_scope, dom, &mut ids, &mut to_mount)?;
 
-        self.interpreter.as_web().hydrate(ids);
+        self.interpreter.base().hydrate(ids);
 
         #[cfg(feature = "mounted")]
         for id in to_mount {
@@ -168,7 +168,7 @@ impl WriteMutations for OnlyWriteTemplates<'_> {
             .insert(template.name.to_owned(), self.0.max_template_id);
         self.0
             .interpreter
-            .as_web()
+            .base()
             .save_template(roots, self.0.max_template_id);
         self.0.max_template_id += 1
     }