浏览代码

feat: make the interpreter as its own crate

Jonathan Kelley 3 年之前
父节点
当前提交
527434b9f9

+ 2 - 0
packages/desktop/Cargo.toml

@@ -31,6 +31,8 @@ dioxus-core-macro = { path = "../core-macro", version = "^0.1.7" }
 dioxus-html = { path = "../html", features = ["serialize"], version = "^0.1.6" }
 webbrowser = "0.5.5"
 mime_guess = "2.0.3"
+dioxus-interpreter-js = { path = "../interpreter", version = "^0.0.0" }
+
 
 [features]
 default = ["tokio_runtime"]

+ 4 - 4
packages/desktop/examples/async.rs

@@ -10,14 +10,14 @@ fn main() {
 }
 
 fn app(cx: Scope) -> Element {
-    let count = use_state(&cx, || 0);
+    let (count, set_count) = use_state(&cx, || 0);
 
     use_future(&cx, || {
-        let count = count.for_async();
+        let set_count = set_count.clone();
         async move {
             loop {
                 tokio::time::sleep(Duration::from_millis(1000)).await;
-                *count.modify() += 1;
+                set_count.modify(|f| f + 1)
             }
         }
     });
@@ -26,7 +26,7 @@ fn app(cx: Scope) -> Element {
         div {
             h1 { "High-Five counter: {count}" }
             button {
-                onclick: move |_| count.set(0),
+                onclick: move |_| set_count(0),
                 "Click me!"
             }
         }

+ 0 - 550
packages/desktop/src/interpreter.js

@@ -1,550 +0,0 @@
-export function main() {
-  let root = window.document.getElementById("main");
-  if (root != null) {
-    window.interpreter = new Interpreter(root);
-    window.rpc.call("initialize");
-  }
-}
-export class Interpreter {
-  root;
-  stack;
-  listeners;
-  handlers;
-  lastNodeWasText;
-  nodes;
-  constructor(root) {
-    this.root = root;
-    this.stack = [root];
-    this.listeners = {};
-    this.handlers = {};
-    this.lastNodeWasText = false;
-    this.nodes = [root];
-  }
-  top() {
-    return this.stack[this.stack.length - 1];
-  }
-  pop() {
-    return this.stack.pop();
-  }
-  PushRoot(root) {
-    const node = this.nodes[root];
-    this.stack.push(node);
-  }
-  AppendChildren(many) {
-    let root = this.stack[this.stack.length - (1 + many)];
-    let to_add = this.stack.splice(this.stack.length - many);
-    for (let i = 0; i < many; i++) {
-      root.appendChild(to_add[i]);
-    }
-  }
-  ReplaceWith(root_id, m) {
-    let root = this.nodes[root_id];
-    let els = this.stack.splice(this.stack.length - m);
-    root.replaceWith(...els);
-  }
-  InsertAfter(root, n) {
-    let old = this.nodes[root];
-    let new_nodes = this.stack.splice(this.stack.length - n);
-    old.after(...new_nodes);
-  }
-  InsertBefore(root, n) {
-    let old = this.nodes[root];
-    let new_nodes = this.stack.splice(this.stack.length - n);
-    old.before(...new_nodes);
-  }
-  Remove(root) {
-    let node = this.nodes[root];
-    if (node !== undefined) {
-      node.remove();
-    }
-  }
-  CreateTextNode(text, root) {
-    // todo: make it so the types are okay
-    const node = document.createTextNode(text);
-    this.nodes[root] = node;
-    this.stack.push(node);
-  }
-  CreateElement(tag, root) {
-    const el = document.createElement(tag);
-    // el.setAttribute("data-dioxus-id", `${root}`);
-    this.nodes[root] = el;
-    this.stack.push(el);
-  }
-  CreateElementNs(tag, root, ns) {
-    let el = document.createElementNS(ns, tag);
-    this.stack.push(el);
-    this.nodes[root] = el;
-  }
-  CreatePlaceholder(root) {
-    let el = document.createElement("pre");
-    el.hidden = true;
-    this.stack.push(el);
-    this.nodes[root] = el;
-  }
-  NewEventListener(event_name, root, handler) {
-    const element = this.nodes[root];
-    element.setAttribute("data-dioxus-id", `${root}`);
-    if (this.listeners[event_name] === undefined) {
-      this.listeners[event_name] = 0;
-      this.handlers[event_name] = handler;
-      this.root.addEventListener(event_name, handler);
-    } else {
-      this.listeners[event_name]++;
-    }
-  }
-  RemoveEventListener(root, event_name) {
-    const element = this.nodes[root];
-    element.removeAttribute(`data-dioxus-id`);
-    this.listeners[event_name]--;
-    if (this.listeners[event_name] === 0) {
-      this.root.removeEventListener(event_name, this.handlers[event_name]);
-      delete this.listeners[event_name];
-      delete this.handlers[event_name];
-    }
-  }
-  SetText(root, text) {
-    this.nodes[root].textContent = text;
-  }
-  SetAttribute(root, field, value, ns) {
-    const name = field;
-    const node = this.nodes[root];
-    if (ns == "style") {
-      // @ts-ignore
-      node.style[name] = value;
-    } else if (ns != null || ns != undefined) {
-      node.setAttributeNS(ns, name, value);
-    } else {
-      switch (name) {
-        case "value":
-          if (value != node.value) {
-            node.value = value;
-          }
-          break;
-        case "checked":
-          node.checked = value === "true";
-          break;
-        case "selected":
-          node.selected = value === "true";
-          break;
-        case "dangerous_inner_html":
-          node.innerHTML = value;
-          break;
-        default:
-          // https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364
-          if (value == "false" && bool_attrs.hasOwnProperty(name)) {
-            node.removeAttribute(name);
-          } else {
-            node.setAttribute(name, value);
-          }
-      }
-    }
-  }
-  RemoveAttribute(root, name) {
-    const node = this.nodes[root];
-    node.removeAttribute(name);
-    if (name === "value") {
-      node.value = "";
-    }
-    if (name === "checked") {
-      node.checked = false;
-    }
-    if (name === "selected") {
-      node.selected = false;
-    }
-  }
-  handleEdits(edits) {
-    this.stack.push(this.root);
-    for (let edit of edits) {
-      this.handleEdit(edit);
-    }
-  }
-  handleEdit(edit) {
-    switch (edit.type) {
-      case "PushRoot":
-        this.PushRoot(edit.root);
-        break;
-      case "AppendChildren":
-        this.AppendChildren(edit.many);
-        break;
-      case "ReplaceWith":
-        this.ReplaceWith(edit.root, edit.m);
-        break;
-      case "InsertAfter":
-        this.InsertAfter(edit.root, edit.n);
-        break;
-      case "InsertBefore":
-        this.InsertBefore(edit.root, edit.n);
-        break;
-      case "Remove":
-        this.Remove(edit.root);
-        break;
-      case "CreateTextNode":
-        this.CreateTextNode(edit.text, edit.root);
-        break;
-      case "CreateElement":
-        this.CreateElement(edit.tag, edit.root);
-        break;
-      case "CreateElementNs":
-        this.CreateElementNs(edit.tag, edit.root, edit.ns);
-        break;
-      case "CreatePlaceholder":
-        this.CreatePlaceholder(edit.root);
-        break;
-      case "RemoveEventListener":
-        this.RemoveEventListener(edit.root, edit.event_name);
-        break;
-      case "NewEventListener":
-        // this handler is only provided on desktop implementations since this
-        // method is not used by the web implementation
-        let handler = (event) => {
-          let target = event.target;
-          if (target != null) {
-            let realId = target.getAttribute(`data-dioxus-id`);
-            let shouldPreventDefault = target.getAttribute(
-              `dioxus-prevent-default`
-            );
-
-            if (event.type == "click") {
-              event.preventDefault();
-              if (shouldPreventDefault !== `onclick`) {
-                console.log("click", event);
-                console.log("clickeded", event.target);
-                console.log("clickeded", event.target.tagName);
-                if (target.tagName == "A") {
-                  const href = target.getAttribute("href");
-                  if (href !== "" && href !== null && href !== undefined) {
-                    window.rpc.call("browser_open", { href });
-                  }
-                }
-              }
-            }
-
-            // walk the tree to find the real element
-            while (realId == null && target.parentElement != null) {
-              target = target.parentElement;
-              realId = target.getAttribute(`data-dioxus-id`);
-            }
-
-            shouldPreventDefault = target.getAttribute(
-              `dioxus-prevent-default`
-            );
-
-            let contents = serialize_event(event);
-            if (shouldPreventDefault === `on${event.type}`) {
-              event.preventDefault();
-            }
-            if (event.type == "submit") {
-              event.preventDefault();
-            }
-            if (realId == null) {
-              return;
-            }
-            window.rpc.call("user_event", {
-              event: edit.event_name,
-              mounted_dom_id: parseInt(realId),
-              contents: contents,
-            });
-          }
-        };
-        this.NewEventListener(edit.event_name, edit.root, handler);
-        break;
-      case "SetText":
-        this.SetText(edit.root, edit.text);
-        break;
-      case "SetAttribute":
-        this.SetAttribute(edit.root, edit.field, edit.value, edit.ns);
-        break;
-      case "RemoveAttribute":
-        this.RemoveAttribute(edit.root, edit.name);
-        break;
-    }
-  }
-}
-function serialize_event(event) {
-  switch (event.type) {
-    case "copy":
-    case "cut":
-    case "past": {
-      return {};
-    }
-    case "compositionend":
-    case "compositionstart":
-    case "compositionupdate": {
-      let { data } = event;
-      return {
-        data,
-      };
-    }
-    case "keydown":
-    case "keypress":
-    case "keyup": {
-      let {
-        charCode,
-        key,
-        altKey,
-        ctrlKey,
-        metaKey,
-        keyCode,
-        shiftKey,
-        location,
-        repeat,
-        which,
-      } = event;
-      return {
-        char_code: charCode,
-        key: key,
-        alt_key: altKey,
-        ctrl_key: ctrlKey,
-        meta_key: metaKey,
-        key_code: keyCode,
-        shift_key: shiftKey,
-        location: location,
-        repeat: repeat,
-        which: which,
-        locale: "locale",
-      };
-    }
-    case "focus":
-    case "blur": {
-      return {};
-    }
-    case "change": {
-      let target = event.target;
-      let value;
-      if (target.type === "checkbox" || target.type === "radio") {
-        value = target.checked ? "true" : "false";
-      } else {
-        value = target.value ?? target.textContent;
-      }
-      return {
-        value: value,
-      };
-    }
-    case "input":
-    case "invalid":
-    case "reset":
-    case "submit": {
-      let target = event.target;
-      let value = target.value ?? target.textContent;
-      if (target.type == "checkbox") {
-        value = target.checked ? "true" : "false";
-      }
-      return {
-        value: value,
-      };
-    }
-    case "click":
-    case "contextmenu":
-    case "doubleclick":
-    case "drag":
-    case "dragend":
-    case "dragenter":
-    case "dragexit":
-    case "dragleave":
-    case "dragover":
-    case "dragstart":
-    case "drop":
-    case "mousedown":
-    case "mouseenter":
-    case "mouseleave":
-    case "mousemove":
-    case "mouseout":
-    case "mouseover":
-    case "mouseup": {
-      const {
-        altKey,
-        button,
-        buttons,
-        clientX,
-        clientY,
-        ctrlKey,
-        metaKey,
-        pageX,
-        pageY,
-        screenX,
-        screenY,
-        shiftKey,
-      } = event;
-      return {
-        alt_key: altKey,
-        button: button,
-        buttons: buttons,
-        client_x: clientX,
-        client_y: clientY,
-        ctrl_key: ctrlKey,
-        meta_key: metaKey,
-        page_x: pageX,
-        page_y: pageY,
-        screen_x: screenX,
-        screen_y: screenY,
-        shift_key: shiftKey,
-      };
-    }
-    case "pointerdown":
-    case "pointermove":
-    case "pointerup":
-    case "pointercancel":
-    case "gotpointercapture":
-    case "lostpointercapture":
-    case "pointerenter":
-    case "pointerleave":
-    case "pointerover":
-    case "pointerout": {
-      const {
-        altKey,
-        button,
-        buttons,
-        clientX,
-        clientY,
-        ctrlKey,
-        metaKey,
-        pageX,
-        pageY,
-        screenX,
-        screenY,
-        shiftKey,
-        pointerId,
-        width,
-        height,
-        pressure,
-        tangentialPressure,
-        tiltX,
-        tiltY,
-        twist,
-        pointerType,
-        isPrimary,
-      } = event;
-      return {
-        alt_key: altKey,
-        button: button,
-        buttons: buttons,
-        client_x: clientX,
-        client_y: clientY,
-        ctrl_key: ctrlKey,
-        meta_key: metaKey,
-        page_x: pageX,
-        page_y: pageY,
-        screen_x: screenX,
-        screen_y: screenY,
-        shift_key: shiftKey,
-        pointer_id: pointerId,
-        width: width,
-        height: height,
-        pressure: pressure,
-        tangential_pressure: tangentialPressure,
-        tilt_x: tiltX,
-        tilt_y: tiltY,
-        twist: twist,
-        pointer_type: pointerType,
-        is_primary: isPrimary,
-      };
-    }
-    case "select": {
-      return {};
-    }
-    case "touchcancel":
-    case "touchend":
-    case "touchmove":
-    case "touchstart": {
-      const { altKey, ctrlKey, metaKey, shiftKey } = event;
-      return {
-        // changed_touches: event.changedTouches,
-        // target_touches: event.targetTouches,
-        // touches: event.touches,
-        alt_key: altKey,
-        ctrl_key: ctrlKey,
-        meta_key: metaKey,
-        shift_key: shiftKey,
-      };
-    }
-    case "scroll": {
-      return {};
-    }
-    case "wheel": {
-      const { deltaX, deltaY, deltaZ, deltaMode } = event;
-      return {
-        delta_x: deltaX,
-        delta_y: deltaY,
-        delta_z: deltaZ,
-        delta_mode: deltaMode,
-      };
-    }
-    case "animationstart":
-    case "animationend":
-    case "animationiteration": {
-      const { animationName, elapsedTime, pseudoElement } = event;
-      return {
-        animation_name: animationName,
-        elapsed_time: elapsedTime,
-        pseudo_element: pseudoElement,
-      };
-    }
-    case "transitionend": {
-      const { propertyName, elapsedTime, pseudoElement } = event;
-      return {
-        property_name: propertyName,
-        elapsed_time: elapsedTime,
-        pseudo_element: pseudoElement,
-      };
-    }
-    case "abort":
-    case "canplay":
-    case "canplaythrough":
-    case "durationchange":
-    case "emptied":
-    case "encrypted":
-    case "ended":
-    case "error":
-    case "loadeddata":
-    case "loadedmetadata":
-    case "loadstart":
-    case "pause":
-    case "play":
-    case "playing":
-    case "progress":
-    case "ratechange":
-    case "seeked":
-    case "seeking":
-    case "stalled":
-    case "suspend":
-    case "timeupdate":
-    case "volumechange":
-    case "waiting": {
-      return {};
-    }
-    case "toggle": {
-      return {};
-    }
-    default: {
-      return {};
-    }
-  }
-}
-const bool_attrs = {
-  allowfullscreen: true,
-  allowpaymentrequest: true,
-  async: true,
-  autofocus: true,
-  autoplay: true,
-  checked: true,
-  controls: true,
-  default: true,
-  defer: true,
-  disabled: true,
-  formnovalidate: true,
-  hidden: true,
-  ismap: true,
-  itemscope: true,
-  loop: true,
-  multiple: true,
-  muted: true,
-  nomodule: true,
-  novalidate: true,
-  open: true,
-  playsinline: true,
-  readonly: true,
-  required: true,
-  reversed: true,
-  selected: true,
-  truespeed: true,
-};

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

@@ -224,7 +224,7 @@ pub fn launch_with_props<P: 'static + Send>(
                         } else if trimmed == "index.js" {
                             wry::http::ResponseBuilder::new()
                                 .mimetype("text/javascript")
-                                .body(include_bytes!("./interpreter.js").to_vec())
+                                .body(dioxus_interpreter_js::INTERPRTER_JS.as_bytes().to_vec())
                         } else {
                             // Read the file content from file path
                             use std::fs::read;

+ 3 - 0
packages/web/Cargo.toml

@@ -29,6 +29,9 @@ gloo-timers = { version = "0.2.1", features = ["futures"] }
 futures-util = "0.3.15"
 smallstr = "0.2.0"
 
+[build-dependencies]
+dioxus-interpreter-js = { path = "../interpreter", version = "^0.0.0" }
+
 [dependencies.web-sys]
 version = "0.3.51"
 features = [

+ 8 - 0
packages/web/build.rs

@@ -0,0 +1,8 @@
+use std::{fs::File, io::Write};
+
+fn main() {
+    // write the interpreter code to a local file
+    let mut file = File::create("interpreter.js").unwrap();
+    file.write_all(dioxus_interpreter_js::INTERPRTER_JS.as_bytes())
+        .unwrap();
+}

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

@@ -198,7 +198,7 @@ export class Interpreter {
                 this.RemoveEventListener(edit.root, edit.event_name);
                 break;
             case "NewEventListener":
-                // this handler is only provided on desktop implementations since this 
+                // this handler is only provided on desktop implementations since this
                 // method is not used by the web implementation
                 let handler = (event) => {
                     let target = event.target;

+ 1 - 1
packages/web/src/bindings.rs

@@ -2,7 +2,7 @@ use js_sys::Function;
 use wasm_bindgen::prelude::*;
 use web_sys::{Element, Node};
 
-#[wasm_bindgen(module = "/src/interpreter.js")]
+#[wasm_bindgen(module = "/interpreter.js")]
 extern "C" {
     pub type Interpreter;