소스 검색

Fix elements and attributes mapping for dx translate

Evan Almloff 1 년 전
부모
커밋
afb258b658

+ 1 - 0
packages/html/Cargo.toml

@@ -68,3 +68,4 @@ mounted = [
 wasm-bind = ["web-sys", "wasm-bindgen"]
 native-bind = ["tokio"]
 hot-reload-context = ["dioxus-rsx"]
+html-to-rsx = []

+ 45 - 3
packages/html/src/elements.rs

@@ -79,6 +79,25 @@ macro_rules! impl_attribute_match {
     };
 }
 
+#[cfg(feature = "html-to-rsx")]
+macro_rules! impl_html_to_rsx_attribute_match {
+    (
+        $attr:ident $fil:ident $name:literal
+    ) => {
+        if $attr == $name {
+            return Some(stringify!($fil));
+        }
+    };
+
+    (
+        $attr:ident $fil:ident $_:tt
+    ) => {
+        if $attr == stringify!($fil) {
+            return Some(stringify!($fil));
+        }
+    };
+}
+
 macro_rules! impl_element {
     (
         $(#[$attr:meta])*
@@ -316,6 +335,30 @@ macro_rules! builder_constructors {
             }
         }
 
+        #[cfg(feature = "html-to-rsx")]
+        pub fn map_html_attribute_to_rsx(html: &str) -> Option<&'static str> {
+            $(
+                $(
+                    impl_html_to_rsx_attribute_match!(
+                        html $fil $extra
+                    );
+                )*
+            )*
+
+            None
+        }
+
+        #[cfg(feature = "html-to-rsx")]
+        pub fn map_html_element_to_rsx(html: &str) -> Option<&'static str> {
+            $(
+                if html == stringify!($name) {
+                    return Some(stringify!($name));
+                }
+            )*
+
+            None
+        }
+
         $(
             impl_element!(
                 $(#[$attr])*
@@ -998,9 +1041,8 @@ builder_constructors! {
         src: Uri DEFAULT,
         text: String DEFAULT,
 
-        // r#async: Bool,
-        // r#type: String, // TODO could be an enum
-        r#type: String "type",
+        r#async: Bool "async",
+        r#type: String "type", // TODO could be an enum
         r#script: String "script",
     };
 

+ 46 - 0
packages/html/src/global_attributes.rs

@@ -33,12 +33,44 @@ macro_rules! trait_method_mapping {
     };
 }
 
+#[cfg(feature = "html-to-rsx")]
+macro_rules! html_to_rsx_attribute_mapping {
+    (
+        $matching:ident;
+        $(#[$attr:meta])*
+        $name:ident;
+    ) => {
+        if $matching == stringify!($name) {
+            return Some(stringify!($name));
+        }
+    };
+    (
+        $matching:ident;
+        $(#[$attr:meta])*
+        $name:ident: $lit:literal;
+    ) => {
+        if $matching == stringify!($lit) {
+            return Some(stringify!($name));
+        }
+    };
+    (
+        $matching:ident;
+        $(#[$attr:meta])*
+        $name:ident: $lit:literal, $ns:literal;
+    ) => {
+        if $matching == stringify!($lit) {
+            return Some(stringify!($name));
+        }
+    };
+}
+
 macro_rules! trait_methods {
     (
         @base
         $(#[$trait_attr:meta])*
         $trait:ident;
         $fn:ident;
+        $fn_html_to_rsx:ident;
         $(
             $(#[$attr:meta])*
             $name:ident $(: $($arg:literal),*)*;
@@ -62,6 +94,18 @@ macro_rules! trait_methods {
             )*
             None
         }
+
+        #[cfg(feature = "html-to-rsx")]
+        #[doc = "Converts an HTML attribute to an RSX attribute"]
+        pub(crate) fn $fn_html_to_rsx(html: &str) -> Option<&'static str> {
+            $(
+                html_to_rsx_attribute_mapping! {
+                    html;
+                    $name$(: $($arg),*)*;
+                }
+            )*
+            None
+        }
     };
 
     // Rename the incoming ident and apply a custom namespace
@@ -79,6 +123,7 @@ trait_methods! {
 
     GlobalAttributes;
     map_global_attributes;
+    map_html_global_attributes_to_rsx;
 
     /// Prevent the default action for this element.
     ///
@@ -1593,6 +1638,7 @@ trait_methods! {
     @base
     SvgAttributes;
     map_svg_attributes;
+    map_html_svg_attributes_to_rsx;
 
     /// Prevent the default action for this element.
     ///

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

@@ -19,6 +19,8 @@
 mod elements;
 #[cfg(feature = "hot-reload-context")]
 pub use elements::HtmlCtx;
+#[cfg(feature = "html-to-rsx")]
+pub use elements::{map_html_attribute_to_rsx, map_html_element_to_rsx};
 pub mod events;
 pub mod geometry;
 mod global_attributes;

+ 1 - 0
packages/rsx-rosetta/Cargo.toml

@@ -15,6 +15,7 @@ keywords = ["dom", "ui", "gui", "react"]
 [dependencies]
 dioxus-autofmt = { workspace = true }
 dioxus-rsx = { workspace = true }
+dioxus-html = { workspace = true, features = ["html-to-rsx"]}
 html_parser.workspace = true
 proc-macro2 = "1.0.49"
 quote = "1.0.23"

+ 32 - 10
packages/rsx-rosetta/src/lib.rs

@@ -2,6 +2,7 @@
 #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
 #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
 
+use dioxus_html::{map_html_element_to_rsx, map_html_attribute_to_rsx};
 use convert_case::{Case, Casing};
 use dioxus_rsx::{
     BodyNode, CallBody, Component, Element, ElementAttr, ElementAttrNamed, ElementName, IfmtInput,
@@ -24,26 +25,47 @@ pub fn rsx_node_from_html(node: &Node) -> Option<BodyNode> {
     match node {
         Node::Text(text) => Some(BodyNode::Text(ifmt_from_text(text))),
         Node::Element(el) => {
-            let el_name = el.name.to_case(Case::Snake);
-            let el_name = ElementName::Ident(Ident::new(el_name.as_str(), Span::call_site()));
+            let el_name = if let Some(name) = map_html_element_to_rsx(&el.name) {
+                ElementName::Ident(Ident::new(name, Span::call_site()))
+            } else {
+                // if we don't recognize it and it has a dash, we assume it's a web component
+                if el.name.contains('-') {
+                    ElementName::Custom(LitStr::new(&el.name, Span::call_site()))
+                } else {
+                    // otherwise, it might be an element that isn't supported yet
+                    ElementName::Ident(Ident::new(
+                        &el.name.to_case(Case::Snake),
+                        Span::call_site(),
+                    ))
+                }
+            };
 
             let mut attributes: Vec<_> = el
                 .attributes
                 .iter()
                 .map(|(name, value)| {
-                    let ident = if matches!(name.as_str(), "for" | "async" | "type" | "as") {
-                        Ident::new_raw(name.as_str(), Span::call_site())
+                    let value = ifmt_from_text(value.as_deref().unwrap_or("false"));
+                    let attr = if let Some(name) = map_html_attribute_to_rsx(name) {
+                        let ident = if let Some(name) = name.strip_prefix("r#") {
+                            Ident::new_raw(name, Span::call_site())
+                        } else {
+                            Ident::new(name, Span::call_site())
+                        };
+                        ElementAttr::AttrText {
+                            value,
+                            name: ident,
+                        }
                     } else {
-                        let new_name = name.to_case(Case::Snake);
-                        Ident::new(new_name.as_str(), Span::call_site())
+                        // If we don't recognize the attribute, we assume it's a custom attribute
+                        ElementAttr::CustomAttrText {
+                            value,
+                            name: LitStr::new(name, Span::call_site()),
+                        }
                     };
 
                     ElementAttrNamed {
                         el_name: el_name.clone(),
-                        attr: ElementAttr::AttrText {
-                            value: ifmt_from_text(value.as_deref().unwrap_or("false")),
-                            name: ident,
-                        },
+                        attr
                     }
                 })
                 .collect();

+ 33 - 0
packages/rsx-rosetta/tests/h-tags.rs

@@ -0,0 +1,33 @@
+use html_parser::Dom;
+
+#[test]
+fn h_tags_translate() {
+    let html = r#"
+    <div>
+        <h1>hello world!</h1>
+        <h2>hello world!</h2>
+        <h3>hello world!</h3>
+        <h4>hello world!</h4>
+        <h5>hello world!</h5>
+        <h6>hello world!</h6>
+    </div>
+    "#
+    .trim();
+
+    let dom = Dom::parse(html).unwrap();
+
+    let body = rsx_rosetta::rsx_from_html(&dom);
+
+    let out = dioxus_autofmt::write_block_out(body).unwrap();
+
+    let expected = r#"
+    div {
+        h1 { "hello world!" }
+        h2 { "hello world!" }
+        h3 { "hello world!" }
+        h4 { "hello world!" }
+        h5 { "hello world!" }
+        h6 { "hello world!" }
+    }"#;
+    pretty_assertions::assert_eq!(&out, &expected);
+}

+ 21 - 0
packages/rsx-rosetta/tests/raw.rs

@@ -0,0 +1,21 @@
+use html_parser::Dom;
+
+#[test]
+fn raw_attribute() {
+    let html = r#"
+    <div>
+        <div unrecognizedattribute="asd">hello world!</div>
+    </div>
+    "#
+    .trim();
+
+    let dom = Dom::parse(html).unwrap();
+
+    let body = rsx_rosetta::rsx_from_html(&dom);
+
+    let out = dioxus_autofmt::write_block_out(body).unwrap();
+
+    let expected = r#"
+    div { div { "unrecognizedattribute": "asd", "hello world!" } }"#;
+    pretty_assertions::assert_eq!(&out, &expected);
+}

+ 0 - 4
packages/rsx-rosetta/tests/simple.rs

@@ -9,8 +9,6 @@ fn simple_elements() {
         <div id="asd">hello world!</div>
         <div for="asd">hello world!</div>
         <div async="asd">hello world!</div>
-        <div LargeThing="asd">hello world!</div>
-        <ai-is-awesome>hello world!</ai-is-awesome>
     </div>
     "#
     .trim();
@@ -28,8 +26,6 @@ fn simple_elements() {
         div { id: "asd", "hello world!" }
         div { r#for: "asd", "hello world!" }
         div { r#async: "asd", "hello world!" }
-        div { large_thing: "asd", "hello world!" }
-        ai_is_awesome { "hello world!" }
     }"#;
     pretty_assertions::assert_eq!(&out, &expected);
 }

+ 21 - 0
packages/rsx-rosetta/tests/web-component.rs

@@ -0,0 +1,21 @@
+use html_parser::Dom;
+
+#[test]
+fn web_components_translate() {
+    let html = r#"
+    <div>
+       <my-component></my-component>
+    </div>
+    "#
+    .trim();
+
+    let dom = Dom::parse(html).unwrap();
+
+    let body = rsx_rosetta::rsx_from_html(&dom);
+
+    let out = dioxus_autofmt::write_block_out(body).unwrap();
+
+    let expected = r#"
+    div { my-component {} }"#;
+    pretty_assertions::assert_eq!(&out, &expected);
+}