mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Fix casing of dynamic tags (#2578)
* fix casing of dynamic tags * add test case for unknown tag names * add lint for non-normalized tags
This commit is contained in:
parent
4bc61b8da9
commit
8978baa45d
@ -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! {
|
||||
@ -375,18 +387,15 @@ 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,
|
||||
);
|
||||
}
|
||||
// convert to lowercase because the runtime checks rely on it.
|
||||
#vtag_name.to_mut().make_ascii_lowercase();
|
||||
::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 ::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 +404,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 +438,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)
|
||||
|
||||
@ -13,5 +13,8 @@ fn main() {
|
||||
let bad_img = html! {
|
||||
<img src="img.jpeg"/>
|
||||
};
|
||||
let misformed_tagname = html! {
|
||||
<tExTAreA />
|
||||
};
|
||||
compile_error!("This macro call exists to deliberately fail the compilation of the test so we can verify output of lints");
|
||||
}
|
||||
|
||||
@ -22,8 +22,14 @@ warning: All `<img>` tags should have an `alt` attribute which provides a human-
|
||||
14 | <img src="img.jpeg"/>
|
||||
| ^^^
|
||||
|
||||
error: This macro call exists to deliberately fail the compilation of the test so we can verify output of lints
|
||||
--> tests/html_lints/fail.rs:16:5
|
||||
warning: The tag 'tExTAreA' is not matching its normalized form 'textarea'. If you want to keep this form, change this to a dynamic tag `@{"tExTAreA"}`.
|
||||
--> tests/html_lints/fail.rs:17:10
|
||||
|
|
||||
16 | compile_error!("This macro call exists to deliberately fail the compilation of the test so we can verify output of lints");
|
||||
17 | <tExTAreA />
|
||||
| ^^^^^^^^
|
||||
|
||||
error: This macro call exists to deliberately fail the compilation of the test so we can verify output of lints
|
||||
--> tests/html_lints/fail.rs:19:5
|
||||
|
|
||||
19 | compile_error!("This macro call exists to deliberately fail the compilation of the test so we can verify output of lints");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@ -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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user