diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 8e0f7213ea8..793bca68fbc 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -137,7 +137,7 @@ jobs: - name: Run website code snippet tests run: | - cd packages/website-test + cd tools/website-test cargo test integration_tests: diff --git a/Cargo.toml b/Cargo.toml index 6b6177b5b9c..de23a7e9246 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,9 @@ [workspace] members = [ + # Packages "packages/yew", "packages/yew-macro", - "packages/yew-validation", "packages/yew-agent", - - # Router "packages/yew-router", "packages/yew-router-macro", @@ -35,9 +33,10 @@ members = [ "examples/webgl", "examples/web_worker_fib", - # Release tools - "packages/changelog", + # Tools + "tools/changelog", ] exclude = [ - "packages/website-test", + # Tools + "tools/website-test", ] diff --git a/Makefile.toml b/Makefile.toml index 076d45a8daf..7f4a7ea2670 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -97,7 +97,7 @@ args = ["test", "--doc"] [tasks.website-test] script = [ """ - cd packages/website-test + cd tools/website-test cargo test """ ] diff --git a/packages/yew-macro/src/derive_props/builder.rs b/packages/yew-macro/src/derive_props/builder.rs index 69ee96df282..02c1622ffba 100644 --- a/packages/yew-macro/src/derive_props/builder.rs +++ b/packages/yew-macro/src/derive_props/builder.rs @@ -8,7 +8,7 @@ use super::generics::{to_arguments, with_param_bounds, GenericArguments}; use super::{DerivePropsInput, PropField}; use proc_macro2::{Ident, Span}; -use quote::{quote, ToTokens}; +use quote::{format_ident, quote, ToTokens}; use syn::Attribute; pub struct PropsBuilder<'a> { @@ -126,9 +126,10 @@ impl PropsBuilder<'_> { .filter(|pf| pf.is_required()) .map(|pf| pf.to_step_name(prefix)) .collect(); - step_names.push(Ident::new( - &format!("{}PropsBuilder", prefix), - prefix.span(), + step_names.push(format_ident!( + "{}PropsBuilder", + prefix, + span = prefix.span(), )); step_names } diff --git a/packages/yew-macro/src/derive_props/field.rs b/packages/yew-macro/src/derive_props/field.rs index 1014e900472..d299e083cf9 100644 --- a/packages/yew-macro/src/derive_props/field.rs +++ b/packages/yew-macro/src/derive_props/field.rs @@ -1,7 +1,7 @@ use super::generics::GenericArguments; use super::should_preserve_attr; use proc_macro2::{Ident, Span}; -use quote::{quote, quote_spanned}; +use quote::{format_ident, quote, quote_spanned}; use std::cmp::{Ord, Ordering, PartialEq, PartialOrd}; use std::convert::TryFrom; use syn::parse::Result; @@ -34,9 +34,11 @@ impl PropField { /// This step name is descriptive to help a developer realize they missed a required prop pub fn to_step_name(&self, props_name: &Ident) -> Ident { - Ident::new( - &format!("{}_missing_required_prop_{}", props_name, self.name), - Span::call_site(), + format_ident!( + "{}_missing_required_prop_{}", + props_name, + self.name, + span = Span::call_site(), ) } @@ -189,7 +191,7 @@ impl PropField { Ok(PropAttr::Option) } else { let ident = named_field.ident.as_ref().unwrap(); - let wrapped_name = Ident::new(&format!("{}_wrapper", ident), Span::call_site()); + let wrapped_name = format_ident!("{}_wrapper", ident, span = Span::call_site()); Ok(PropAttr::Required { wrapped_name }) } } diff --git a/packages/yew-macro/src/derive_props/mod.rs b/packages/yew-macro/src/derive_props/mod.rs index 65875b1e860..1c214fb2c64 100644 --- a/packages/yew-macro/src/derive_props/mod.rs +++ b/packages/yew-macro/src/derive_props/mod.rs @@ -6,7 +6,7 @@ mod wrapper; use builder::PropsBuilder; use field::PropField; use proc_macro2::{Ident, Span}; -use quote::{quote, ToTokens}; +use quote::{format_ident, quote, ToTokens}; use std::convert::TryInto; use syn::parse::{Parse, ParseStream, Result}; use syn::{Attribute, DeriveInput, Generics, Visibility}; @@ -80,7 +80,7 @@ impl ToTokens for DerivePropsInput { } = self; // The wrapper is a new struct which wraps required props in `Option` - let wrapper_name = Ident::new(&format!("{}Wrapper", props_name), Span::call_site()); + let wrapper_name = format_ident!("{}Wrapper", props_name, span = Span::call_site()); let wrapper = PropsWrapper::new( &wrapper_name, generics, @@ -90,8 +90,8 @@ impl ToTokens for DerivePropsInput { tokens.extend(wrapper.into_token_stream()); // The builder will only build if all required props have been set - let builder_name = Ident::new(&format!("{}Builder", props_name), Span::call_site()); - let builder_step = Ident::new(&format!("{}BuilderStep", props_name), Span::call_site()); + let builder_name = format_ident!("{}Builder", props_name, span = Span::call_site()); + let builder_step = format_ident!("{}BuilderStep", props_name, span = Span::call_site()); let builder = PropsBuilder::new( &builder_name, &builder_step, diff --git a/packages/yew-macro/src/html_tree/html_element.rs b/packages/yew-macro/src/html_tree/html_element.rs index d0a0c84d397..9b8ba908651 100644 --- a/packages/yew-macro/src/html_tree/html_element.rs +++ b/packages/yew-macro/src/html_tree/html_element.rs @@ -298,10 +298,9 @@ impl ToTokens for HtmlElement { TagName::Lit(name) => { let name_span = name.span(); let name = name.to_ascii_lowercase_string(); - match &*name { + let node = match &*name { "input" => { - quote_spanned! {name_span=> - #[allow(clippy::redundant_clone, unused_braces)] + quote! { ::std::convert::Into::<::yew::virtual_dom::VNode>::into( ::yew::virtual_dom::VTag::__new_input( #value, @@ -315,8 +314,7 @@ impl ToTokens for HtmlElement { } } "textarea" => { - quote_spanned! {name_span=> - #[allow(clippy::redundant_clone, unused_braces)] + quote! { ::std::convert::Into::<::yew::virtual_dom::VNode>::into( ::yew::virtual_dom::VTag::__new_textarea( #value, @@ -329,8 +327,7 @@ impl ToTokens for HtmlElement { } } _ => { - quote_spanned! {name_span=> - #[allow(clippy::redundant_clone, unused_braces)] + quote! { ::std::convert::Into::<::yew::virtual_dom::VNode>::into( ::yew::virtual_dom::VTag::__new_other( ::std::borrow::Cow::<'static, ::std::primitive::str>::Borrowed(#name), @@ -343,6 +340,16 @@ impl ToTokens for HtmlElement { ) } } + }; + // the return value can be inlined without the braces when this is stable: + // https://github.com/rust-lang/rust/issues/15701 + quote_spanned!{ + name_span => + { + #[allow(clippy::redundant_clone, unused_braces)] + let node = #node; + node + } } } TagName::Expr(name) => { diff --git a/packages/yew-macro/tests/derive_props/pass.rs b/packages/yew-macro/tests/derive_props/pass.rs index 7026736b6b3..f6b62410bb3 100644 --- a/packages/yew-macro/tests/derive_props/pass.rs +++ b/packages/yew-macro/tests/derive_props/pass.rs @@ -260,6 +260,14 @@ mod t13 { create_message: ::std::option::Option, NonSnakeCase: u32, } +} + +mod raw_field_names { + #[derive(::yew::Properties, ::std::cmp::PartialEq)] + pub struct Props { + r#true: u32, + r#pointless_raw_name: u32, + } } diff --git a/packages/yew-macro/tests/html_macro/component-pass.rs b/packages/yew-macro/tests/html_macro/component-pass.rs index 8caa8be6e7b..3d43236802a 100644 --- a/packages/yew-macro/tests/html_macro/component-pass.rs +++ b/packages/yew-macro/tests/html_macro/component-pass.rs @@ -95,6 +95,8 @@ impl ::std::convert::Into<::yew::virtual_dom::VNode> for ChildrenVariants { pub struct ChildProperties { #[prop_or_default] pub string: ::std::string::String, + #[prop_or_default] + pub r#fn: ::std::primitive::i32, pub int: ::std::primitive::i32, #[prop_or_default] pub opt_str: ::std::option::Option<::std::string::String>, @@ -159,6 +161,7 @@ mod scoped { fn compile_pass() { ::yew::html! { }; + ::yew::html! { }; ::yew::html! { <> diff --git a/packages/yew-macro/tests/html_macro_test.rs b/packages/yew-macro/tests/html_macro_test.rs index 92dca13b234..ca19982635c 100644 --- a/packages/yew-macro/tests/html_macro_test.rs +++ b/packages/yew-macro/tests/html_macro_test.rs @@ -1,4 +1,4 @@ -use yew::html; +use yew::{html, html_nested}; #[allow(dead_code)] #[rustversion::attr(stable(1.51), test)] @@ -28,3 +28,15 @@ fn dynamic_tags_catch_non_ascii() { <@{"❤"}/> }; } + +/// test that compilation on html elements pass +/// fixes: https://github.com/yewstack/yew/issues/2268 +#[test] +fn html_nested_macro_on_html_element() { + let _node = html_nested! { +
+ }; + let _node = html_nested! { + + }; +} diff --git a/packages/yew-macro/tests/props_macro/props-pass.rs b/packages/yew-macro/tests/props_macro/props-pass.rs index 1dd62c89dca..d5b91abe0cd 100644 --- a/packages/yew-macro/tests/props_macro/props-pass.rs +++ b/packages/yew-macro/tests/props_macro/props-pass.rs @@ -43,10 +43,20 @@ struct Props { b: ::std::primitive::usize, } +#[derive(::yew::Properties, ::std::cmp::PartialEq)] +pub struct RawIdentProps { + r#true: ::std::primitive::usize, + #[prop_or_default] + r#pointless_raw_name: ::std::primitive::usize, +} + fn compile_pass() { ::yew::props!(Props { a: 5 }); let (a, b) = (3, 5); ::yew::props!(Props { a, b }); + ::yew::props!(RawIdentProps { r#true: 5 }); + let (r#true, r#pointless_raw_name) = (3, 5); + ::yew::props!(RawIdentProps { r#true, r#pointless_raw_name }); } fn main() {} diff --git a/packages/yew-router/README.md b/packages/yew-router/README.md index 9473b15410a..0cb6b97c3a2 100644 --- a/packages/yew-router/README.md +++ b/packages/yew-router/README.md @@ -1,80 +1,4 @@ # yew-router A routing library for the [Yew](https://github.com/yewstack/yew) frontend framework. - -### Example - -```rust - use yew::prelude::*; -use yew_functional::*; -use yew_router::prelude::*; - -#[derive(Debug, Clone, Copy, PartialEq, Routable)] -enum Route { - #[at("/")] - Home, - #[at("/secure")] - Secure, - #[not_found] - #[at("/404")] - NotFound, -} - -fn switch(routes: &Route) -> Html { - let onclick_callback = Callback::from(|_| yew_router::service::push(Route::Home, None)); - match routes { - Route::Home => html! {

{ "Home" }

}, - Route::Secure => html! { -
-

{ "Secure" }

- -
- }, - Route::NotFound => html! {

{ "404" }

}, - } -} - -// Component's `view` method -html! { - render={Router::render(switch)} /> -} -``` - -### How it works -This library works by getting the url location from the browser and uses it to instantiate a type that implements Switch. -Simply using `` tags to go to your route will not work out of the box, and are inefficient because the server will return the whole app bundle again at best, and at worst just return a 404 message if the server isn't configured properly. -Using this library's RouteService, RouteAgent, RouterButton, and RouterLink to set the location via `history.push_state()` will change the route without retrieving the whole app again. -#### Server configuration -In order for an external link to your webapp to work, the server must be configured to return the `index.html` file for any GET request that would otherwise return a `404` for any conceivable client-side route. -It can't be a `3xx` redirect to `index.html`, as that will change the url in the browser, causing the routing to fail - it must be a `200` response containing the content of `index.html`. -Once the content of `index.html` loads, it will in turn load the rest of your assets as expected and your app will start, the router will detect the current route, and set your application state accordingly. - -If you choose to serve the app from the same server as your api, it is recommended to mount your api under `/api` and mount your assets under `/` and have `/` return the content of `index.html`. - -Look at https://webpack.js.org/configuration/dev-server/#devserverhistoryapifallback for info on how to configure a webpack dev server to have this behavior. - - -### How to Include -You can use the released version by adding these to your dependencies. -```toml -[dependencies] -yew-router = "0.14.0" -yew = "0.17.0" -``` - -You can use the in-development version in your project by adding it to your dependencies like so: -```toml -[dependencies] -yew-router = { git = "https://github.com/yewstack/yew", branch="master" } -yew = {git = "https://github.com/yewstack/yew", branch = "master"} -``` - - -#### Minimum rustc -Currently, this library targets rustc 1.39.0, but development is done on the latest stable release. -This library aims to track Yew`s minimum supported rustc version. - ------ -### Contributions/Requests - -If you have any questions, suggestions, or want to contribute, please open an Issue or PR and we will get back to you in a timely manner. +[Read on how to use it on yew.rs](https://yew.rs/docs/concepts/router) \ No newline at end of file diff --git a/packages/yew-validation/Cargo.toml b/packages/yew-validation/Cargo.toml deleted file mode 100644 index 29f90876ee4..00000000000 --- a/packages/yew-validation/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "yew-validation" -version = "0.1.0" -authors = ["Philip Peterson "] -edition = "2018" -license = "MIT OR Apache-2.0" -keywords = ["web", "yew", "validation"] -categories = ["text-processing", "parsing", "web-programming"] -description = "Utilities for Yew to validate tag names and attributes" -repository = "https://github.com/yewstack/yew" - -[dependencies] diff --git a/packages/yew-validation/src/lib.rs b/packages/yew-validation/src/lib.rs deleted file mode 100644 index b1cd8245d55..00000000000 --- a/packages/yew-validation/src/lib.rs +++ /dev/null @@ -1,232 +0,0 @@ -//! Utility library for the Yew frontend web framework to handle validating strings relating -//! to HTML/SVG/MathML tags. - -/// Returns true when the character provided is a "control" as defined -/// in [the WhatWG spec](https://infra.spec.whatwg.org/#control) -fn is_control(c: char) -> bool { - match c { - '\u{007F}'..='\u{009F}' => true, - _ => is_c0_control(c), - } -} - -/// Returns true when the character provided is a "c0 control" as defined -/// in [the WhatWG spec](https://infra.spec.whatwg.org/#c0-control) -fn is_c0_control(c: char) -> bool { - matches!(c, '\u{0000}'..='\u{001F}') -} - -/// Returns true when the string provided is a "noncharacter" as defined -/// in [the WhatWG spec](https://infra.spec.whatwg.org/#noncharacter) -fn is_noncharacter(c: char) -> bool { - matches!( - c, - '\u{FDD0}' - ..='\u{FDEF}' - | '\u{FFFE}' - | '\u{FFFF}' - | '\u{1FFFE}' - | '\u{1FFFF}' - | '\u{2FFFE}' - | '\u{2FFFF}' - | '\u{3FFFE}' - | '\u{3FFFF}' - | '\u{4FFFE}' - | '\u{4FFFF}' - | '\u{5FFFE}' - | '\u{5FFFF}' - | '\u{6FFFE}' - | '\u{6FFFF}' - | '\u{7FFFE}' - | '\u{7FFFF}' - | '\u{8FFFE}' - | '\u{8FFFF}' - | '\u{9FFFE}' - | '\u{9FFFF}' - | '\u{AFFFE}' - | '\u{AFFFF}' - | '\u{BFFFE}' - | '\u{BFFFF}' - | '\u{CFFFE}' - | '\u{CFFFF}' - | '\u{DFFFE}' - | '\u{DFFFF}' - | '\u{EFFFE}' - | '\u{EFFFF}' - | '\u{FFFFE}' - | '\u{FFFFF}' - | '\u{10FFFE}' - | '\u{10FFFF}' - ) -} - -/// Returns true when the string provided is a valid "attribute name" as defined -/// in [the WhatWG spec](https://html.spec.whatwg.org/multipage/syntax.html#syntax-attribute-name) -pub fn is_valid_html_attribute_name(attr: &str) -> bool { - for c in attr.chars() { - if is_noncharacter(c) - || is_control(c) - || c == '\u{0020}' - || c == '\u{0022}' - || c == '\u{0027}' - || c == '\u{003E}' - || c == '\u{002F}' - || c == '\u{003D}' - { - return false; - } - } - true -} - -/// Returns true when the character provided is a valid PCENChar as defined -/// in [the WhatWG spec](https://html.spec.whatwg.org/multipage/custom-elements.html#prod-pcenchar) -fn is_pcen_char(c: char) -> bool { - matches!(c, '-' | '.' | '0'..='9' | 'a'..='z' | '_' - | '\u{B7}' - | '\u{C0}'..='\u{D6}' - | '\u{D8}'..='\u{F6}' - | '\u{F8}'..='\u{37D}' - | '\u{37F}'..='\u{1FFF}' - | '\u{200C}'..='\u{200D}' - | '\u{203F}'..='\u{2040}' - | '\u{2070}'..='\u{218F}' - | '\u{2C00}'..='\u{2FEF}' - | '\u{3001}'..='\u{D7FF}' - | '\u{F900}'..='\u{FDCF}' - | '\u{FDF0}'..='\u{FFFD}' - | '\u{10000}'..='\u{EFFFF}' - ) -} - -/// Returns true when the tag name provided would be a valid "custom element" per -/// [the WhatWG spec](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name). -/// Only technically returns correct results if called with a string that is not one of the following: -/// - annotation-xml -/// - color-profile -/// - font-face -/// - font-face-src -/// - font-face-uri -/// - font-face-format -/// - font-face-name -/// - missing-glyph -/// But, given the way it is used in this file, as of this writing, this limitation does not affect the -/// behavior of the program. -fn is_valid_html_custom_element_name(tag: &str) -> bool { - let mut chars = tag.chars(); - let first_char = chars.next(); - - match first_char { - None => false, - Some(first_char) => { - // must begin with [a-z] - if !('a'..='z').contains(&first_char) { - return false; - } - - let mut seen_hyphen = false; - for c in chars { - if c == '-' { - seen_hyphen = true - } - - // all characters must be valid PCENChar's - if !is_pcen_char(c) { - return false; - } - } - - // must contain at least one hyphen - seen_hyphen - } - } -} - -/// Returns true when the tag name provided looks like a valid non-custom HTML element or valid SVG element. -/// There's no official spec here, it's just arbitrary. -fn resembles_standard_html_element_name(tag: &str) -> bool { - // must contain at least one character - if tag.is_empty() { - return false; - } - - let mut saw_non_hyphen = false; - for c in tag.chars() { - match c { - 'a'..='z' | 'A'..='Z' | '0'..='9' => saw_non_hyphen = true, - '-' => {} - _ => { - return false; - } - } - } - - saw_non_hyphen -} - -/// Returns true when you could validly construct a tag using this name in an HTML document -pub fn is_valid_sgml_tag(tag: &str) -> bool { - resembles_standard_html_element_name(tag) || is_valid_html_custom_element_name(tag) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn valid_custom_element() { - assert!(is_valid_html_custom_element_name("foo-bar")); - assert!(is_valid_html_custom_element_name("foo-")); - assert!(is_valid_html_custom_element_name("bar-baz")); - } - - #[test] - fn invalid_custom_element() { - assert!(!is_valid_html_custom_element_name("foobar")); - assert!(!is_valid_html_custom_element_name("-bar")); - assert!(!is_valid_html_custom_element_name("foo bar")); - assert!(!is_valid_html_custom_element_name("")); - assert!(!is_valid_html_custom_element_name("foo\nbar")); - assert!(!is_valid_html_custom_element_name("-")); - } - - #[test] - fn valid_html_element() { - assert!(resembles_standard_html_element_name("section")); - assert!(resembles_standard_html_element_name("h2")); - assert!(resembles_standard_html_element_name("applet")); - assert!(resembles_standard_html_element_name("appLET")); - assert!(resembles_standard_html_element_name("aPPlet")); - assert!(resembles_standard_html_element_name("foo-bar")); - } - - #[test] - fn invalid_html_element() { - assert!(!resembles_standard_html_element_name(" foo")); - assert!(!resembles_standard_html_element_name("foo ")); - assert!(!resembles_standard_html_element_name("-")); - assert!(!resembles_standard_html_element_name("!doctype")); - } - - #[test] - fn valid_html_attribute() { - assert!(is_valid_html_attribute_name("-foo-bar")); - assert!(is_valid_html_attribute_name("data-foobar")); - assert!(is_valid_html_attribute_name("foobar")); - } - - #[test] - fn invalid_sgml_tag() { - assert!(!is_valid_sgml_tag("f>bar")); - assert!(!is_valid_sgml_tag("f")); - } -} diff --git a/packages/changelog/Cargo.toml b/tools/changelog/Cargo.toml similarity index 100% rename from packages/changelog/Cargo.toml rename to tools/changelog/Cargo.toml diff --git a/packages/changelog/Makefile.toml b/tools/changelog/Makefile.toml similarity index 100% rename from packages/changelog/Makefile.toml rename to tools/changelog/Makefile.toml diff --git a/packages/changelog/src/main.rs b/tools/changelog/src/main.rs similarity index 100% rename from packages/changelog/src/main.rs rename to tools/changelog/src/main.rs diff --git a/packages/website-test/Cargo.toml b/tools/website-test/Cargo.toml similarity index 100% rename from packages/website-test/Cargo.toml rename to tools/website-test/Cargo.toml diff --git a/packages/website-test/Makefile.toml b/tools/website-test/Makefile.toml similarity index 100% rename from packages/website-test/Makefile.toml rename to tools/website-test/Makefile.toml diff --git a/packages/website-test/build.rs b/tools/website-test/build.rs similarity index 100% rename from packages/website-test/build.rs rename to tools/website-test/build.rs diff --git a/packages/website-test/src/agents.rs b/tools/website-test/src/agents.rs similarity index 100% rename from packages/website-test/src/agents.rs rename to tools/website-test/src/agents.rs diff --git a/packages/website-test/src/lib.rs b/tools/website-test/src/lib.rs similarity index 100% rename from packages/website-test/src/lib.rs rename to tools/website-test/src/lib.rs diff --git a/packages/website-test/src/tutorial.rs b/tools/website-test/src/tutorial.rs similarity index 100% rename from packages/website-test/src/tutorial.rs rename to tools/website-test/src/tutorial.rs