From 49fb6c08d067f350f82015d06b18907800cb3055 Mon Sep 17 00:00:00 2001 From: WorldSEnder Date: Tue, 5 Apr 2022 04:43:01 +0200 Subject: [PATCH 1/3] fix casing of dynamic tags --- .../yew-macro/src/html_tree/html_element.rs | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/packages/yew-macro/src/html_tree/html_element.rs b/packages/yew-macro/src/html_tree/html_element.rs index 2b96125285e..ff926fa7281 100644 --- a/packages/yew-macro/src/html_tree/html_element.rs +++ b/packages/yew-macro/src/html_tree/html_element.rs @@ -381,12 +381,10 @@ impl ToTokens for HtmlElement { #vtag_name, ); } - // convert to lowercase because the runtime checks rely on it. - #vtag_name.to_mut().make_ascii_lowercase(); #[allow(clippy::redundant_clone, unused_braces, clippy::let_and_return)] - let mut #vtag = match ::std::convert::AsRef::<::std::primitive::str>::as_ref(&#vtag_name) { - "input" => { + let mut #vtag = match () { + _ if "input".eq_ignore_ascii_case(::std::convert::AsRef::<::std::primitive::str>::as_ref(&#vtag_name)) => { ::yew::virtual_dom::VTag::__new_textarea( #value, #node_ref, @@ -395,7 +393,7 @@ impl ToTokens for HtmlElement { #listeners, ) } - "textarea" => { + _ if "textarea".eq_ignore_ascii_case(::std::convert::AsRef::<::std::primitive::str>::as_ref(&#vtag_name)) => { ::yew::virtual_dom::VTag::__new_textarea( #value, #node_ref, @@ -429,17 +427,14 @@ impl ToTokens for HtmlElement { // // check void element if !#vtag.children().is_empty() { - match #vtag.tag() { - "area" | "base" | "br" | "col" | "embed" | "hr" | "img" | "input" - | "link" | "meta" | "param" | "source" | "track" | "wbr" - => { - ::std::panic!( - "a dynamic tag tried to create a `<{0}>` tag with children. `<{0}>` is a void element which can't have any children.", - #vtag.tag(), - ); - } - _ => {} - } + ::std::debug_assert!( + !::std::matches!(#vtag.tag().to_ascii_lowercase().as_str(), + "area" | "base" | "br" | "col" | "embed" | "hr" | "img" | "input" + | "link" | "meta" | "param" | "source" | "track" | "wbr" + ), + "a dynamic tag tried to create a `<{0}>` tag with children. `<{0}>` is a void element which can't have any children.", + #vtag.tag(), + ); } ::std::convert::Into::<::yew::virtual_dom::VNode>::into(#vtag) From d4be78efcd41f65039832167f6cd49d3c97af050 Mon Sep 17 00:00:00 2001 From: WorldSEnder Date: Tue, 5 Apr 2022 05:43:45 +0200 Subject: [PATCH 2/3] add test case for unknown tag names --- packages/yew-macro/src/html_tree/html_element.rs | 11 +++++------ packages/yew/src/dom_bundle/btag/mod.rs | 11 +++++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/yew-macro/src/html_tree/html_element.rs b/packages/yew-macro/src/html_tree/html_element.rs index ff926fa7281..9b8f7dd072b 100644 --- a/packages/yew-macro/src/html_tree/html_element.rs +++ b/packages/yew-macro/src/html_tree/html_element.rs @@ -375,12 +375,11 @@ impl ToTokens for HtmlElement { let mut #vtag_name = ::std::convert::Into::< ::std::borrow::Cow::<'static, ::std::primitive::str> >::into(#expr); - if !#vtag_name.is_ascii() { - ::std::panic!( - "a dynamic tag returned a tag name containing non ASCII characters: `{}`", - #vtag_name, - ); - } + ::std::debug_assert!( + #vtag_name.is_ascii(), + "a dynamic tag returned a tag name containing non ASCII characters: `{}`", + #vtag_name, + ); #[allow(clippy::redundant_clone, unused_braces, clippy::let_and_return)] let mut #vtag = match () { diff --git a/packages/yew/src/dom_bundle/btag/mod.rs b/packages/yew/src/dom_bundle/btag/mod.rs index 5f995bb99b2..df9f9deb975 100644 --- a/packages/yew/src/dom_bundle/btag/mod.rs +++ b/packages/yew/src/dom_bundle/btag/mod.rs @@ -845,9 +845,20 @@ mod tests { <@{"tExTAREa"}/> }; let vtag = assert_vtag_ref(&el); + // textarea is a special element, so it gets normalized assert_eq!(vtag.tag(), "textarea"); } + #[test] + fn dynamic_tags_allow_custom_capitalization() { + let el = html! { + <@{"clipPath"}/> + }; + let vtag = assert_vtag_ref(&el); + // no special treatment for elements not recognized e.g. clipPath + assert_eq!(vtag.tag(), "clipPath"); + } + #[test] fn reset_node_ref() { let (root, scope, parent) = setup_parent(); From 1e418e2ad1b8034bec8e4f9d208dca45044dd779 Mon Sep 17 00:00:00 2001 From: WorldSEnder Date: Tue, 5 Apr 2022 16:19:08 +0200 Subject: [PATCH 3/3] add lint for non-normalized tags --- .../yew-macro/src/html_tree/html_element.rs | 18 +++++++++++++++--- packages/yew-macro/tests/html_lints/fail.rs | 3 +++ .../yew-macro/tests/html_lints/fail.stderr | 10 ++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/yew-macro/src/html_tree/html_element.rs b/packages/yew-macro/src/html_tree/html_element.rs index 9b8f7dd072b..fe7e7a8a6ff 100644 --- a/packages/yew-macro/src/html_tree/html_element.rs +++ b/packages/yew-macro/src/html_tree/html_element.rs @@ -4,6 +4,7 @@ use crate::stringify::{Stringify, Value}; use crate::{non_capitalized_ascii, Peek, PeekValue}; use boolinator::Boolinator; use proc_macro2::{Delimiter, TokenStream}; +use proc_macro_error::emit_warning; use quote::{quote, quote_spanned, ToTokens}; use syn::buffer::Cursor; use syn::parse::{Parse, ParseStream}; @@ -295,9 +296,20 @@ impl ToTokens for HtmlElement { }; tokens.extend(match &name { - TagName::Lit(name) => { - let name_span = name.span(); - let name = name.to_ascii_lowercase_string(); + TagName::Lit(dashedname) => { + let name_span = dashedname.span(); + let name = dashedname.to_ascii_lowercase_string(); + if name != dashedname.to_string() { + emit_warning!( + dashedname.span(), + format!( + "The tag '{0}' is not matching its normalized form '{1}'. If you want \ + to keep this form, change this to a dynamic tag `@{{\"{0}\"}}`.", + dashedname, + name, + ) + ) + } let node = match &*name { "input" => { quote! { diff --git a/packages/yew-macro/tests/html_lints/fail.rs b/packages/yew-macro/tests/html_lints/fail.rs index 36493825ced..c37088e2ca8 100644 --- a/packages/yew-macro/tests/html_lints/fail.rs +++ b/packages/yew-macro/tests/html_lints/fail.rs @@ -13,5 +13,8 @@ fn main() { let bad_img = html! { }; + let misformed_tagname = html! { +