Skip to content

Commit

Permalink
don't add global fields in literally every element props struct
Browse files Browse the repository at this point in the history
  • Loading branch information
ranile committed Feb 22, 2022
1 parent f51003c commit 741c29b
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 28 deletions.
11 changes: 10 additions & 1 deletion packages/yew-macro/src/html_tree/html_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,23 @@ impl ToTokens for HtmlComponent {
props,
children,
} = self;
// incredibly jank but works ¯\_(ツ)_/¯
let is_element = {
let ty_str = ty.to_token_stream().to_string();
let ty_str = ty_str.split("::").filter_map(|it| {
let trimmed = it.trim();
if trimmed.is_empty() { None } else { Some(trimmed) }
}).take(3).collect::<Vec<_>>();
ty_str == vec!["yew", "virtual_dom", "typings"]
};

let props_ty = quote_spanned!(ty.span()=> <#ty as ::yew::html::BaseComponent>::Properties);
let children_renderer = if children.is_empty() {
None
} else {
Some(quote! { ::yew::html::ChildrenRenderer::new(#children) })
};
let build_props = props.build_properties_tokens(&props_ty, children_renderer);
let build_props = props.build_properties_tokens(&props_ty, children_renderer, is_element);

let special_props = props.special();
let node_ref = if let Some(node_ref) = &special_props.node_ref {
Expand Down
5 changes: 5 additions & 0 deletions packages/yew-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,8 @@ pub fn generate_element(input: proc_macro::TokenStream) -> proc_macro::TokenStre

input.to_token_stream().into()
}

#[proc_macro]
pub fn globals(_: TokenStream) -> TokenStream {
TokenStream::from(typed_vdom::globals_macro::globals_impl())
}
42 changes: 35 additions & 7 deletions packages/yew-macro/src/props/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use syn::{
token::Dot2,
Expr,
};
use crate::typed_vdom::all_shared_attributes_as_string;

struct BaseExpr {
pub dot2: Dot2,
Expand Down Expand Up @@ -50,7 +51,9 @@ impl ComponentProps {
self.props.get_by_label(CHILDREN_LABEL)
}

fn prop_validation_tokens(&self, props_ty: impl ToTokens, has_children: bool) -> TokenStream {
fn prop_validation_tokens(&self, props_ty: impl ToTokens, has_children: bool, is_element_component: bool) -> TokenStream {
let shared = all_shared_attributes_as_string();

let check_children = if has_children {
Some(quote_spanned! {props_ty.span()=> __yew_props.children; })
} else {
Expand All @@ -60,7 +63,15 @@ impl ComponentProps {
let check_props: TokenStream = self
.props
.iter()
.map(|Prop { label, .. }| quote_spanned! ( label.span()=> __yew_props.#label; ))
.map(|Prop { label, .. }| {
let should_check = {
let label = label.to_string();
label == "__globals" || (is_element_component && shared.contains(&label.to_string()))
};
if should_check {
quote! {}
} else { quote_spanned!( label.span()=> __yew_props.#label; ) }
})
.chain(self.base_expr.iter().map(|expr| {
quote_spanned! {props_ty.span()=>
let _: #props_ty = #expr;
Expand All @@ -83,16 +94,32 @@ impl ComponentProps {
&self,
props_ty: impl ToTokens,
children_renderer: Option<CR>,
is_element: bool
) -> TokenStream {
let validate_props = self.prop_validation_tokens(&props_ty, children_renderer.is_some());
let validate_props = self.prop_validation_tokens(&props_ty, children_renderer.is_some(), is_element);
let shared = all_shared_attributes_as_string();
let build_props = match &self.base_expr {
None => {
let set_props = self.props.iter().map(|Prop { label, value, .. }| {
quote_spanned! {value.span()=>
.#label(#value)
let mut set_props = vec![];
let mut global_props = vec![];
for Prop { label, value, .. } in self.props.iter() {
if shared.contains(&label.to_string()) {
global_props.push(quote_spanned! {value.span()=>
.#label(#value)
})
} else {
set_props.push(quote_spanned! {value.span()=>
.#label(#value)
})
}
}
let globals_setter = is_element.then(|| quote! {
.__globals(
<::yew::virtual_dom::typings::globals::Globals as ::yew::html::Properties>::builder()
#(#global_props)*
.build()
)
});

let set_children = children_renderer.map(|children| {
quote_spanned! {props_ty.span()=>
.children(#children)
Expand All @@ -103,6 +130,7 @@ impl ComponentProps {
<#props_ty as ::yew::html::Properties>::builder()
#(#set_props)*
#set_children
#globals_setter
.build()
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/yew-macro/src/props/prop_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,6 @@ impl ToTokens for PropsMacroInput {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self { ty, props } = self;

tokens.extend(props.build_properties_tokens(ty, None::<TokenStream>))
tokens.extend(props.build_properties_tokens(ty, None::<TokenStream>, false))
}
}
41 changes: 27 additions & 14 deletions packages/yew-macro/src/typed_vdom/generate_element.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::typed_vdom::globals::{global_attributes, listeners};
use crate::typed_vdom::{all_aria_labels, kw, others, AttributePropDefinition};
use crate::typed_vdom::globals::{listeners};
use crate::typed_vdom::{kw, AttributePropDefinition, all_shared_attributes};
use convert_case::{Case, Casing};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, ToTokens};
Expand Down Expand Up @@ -38,24 +38,21 @@ impl Parse for GenerateElement {
impl ToTokens for GenerateElement {
fn to_tokens(&self, tokens: &mut TokenStream) {
let element_name = &self.element_name;
let prop_definitions = {
let mut prop_definitions = self.props.clone();
prop_definitions.extend(global_attributes());
prop_definitions.extend(all_aria_labels());
prop_definitions.extend(others());
prop_definitions
let prop_definitions = &self.props;
let all_prop_definitions = {
let mut all_prop_definitions = all_shared_attributes();
all_prop_definitions.extend(self.props.clone());
all_prop_definitions
};

let props_ident = format_ident!(
"{}Props",
element_name.to_string().to_case(Case::Pascal),
span = element_name.span()
);
let props = prop_definitions.iter().map(|it| it.build_fields());
let attr_if_lets = prop_definitions.iter().map(|it| it.build_if_lets());
let attr_if_lets = all_prop_definitions.iter().map(|it| it.build_if_lets());

let all_listeners = listeners();
let listeners = all_listeners.iter().map(|it| it.build_fields());
let listeners_if_lets = all_listeners.iter().map(|it| it.build_if_lets());

let vtag_init = match element_name.to_string().trim() {
Expand Down Expand Up @@ -87,21 +84,37 @@ impl ToTokens for GenerateElement {

#[derive(::std::default::Default, ::std::clone::Clone, ::std::fmt::Debug, ::yew::html::Properties, ::std::cmp::PartialEq)]
pub struct #props_ident {
#[prop_or_default]
pub __globals: ::yew::virtual_dom::typings::globals::Globals,
#[prop_or_default]
pub node_ref: ::std::option::Option::<::yew::NodeRef>,
#[prop_or_default]
pub key: ::std::option::Option::<::yew::virtual_dom::Key>,
#[prop_or_default]
pub children: ::yew::Children,
#(#props)*
#(#listeners)*
}

impl std::ops::Deref for #props_ident {
type Target = ::yew::virtual_dom::typings::globals::Globals;

fn deref(&self) -> &Self::Target {
&self.__globals
}
}


impl std::ops::DerefMut for #props_ident {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.__globals
}
}

impl #props_ident {
fn into_data(self) -> ::yew::virtual_dom::typings::ElementData {

::yew::virtual_dom::typings::ElementData {
node_ref: ::std::option::Option::unwrap_or_default(self.node_ref),
node_ref: ::std::option::Option::unwrap_or_default(self.node_ref.clone()),
attributes: {
let mut attrs = ::std::collections::HashMap::new();
#(#attr_if_lets)*
Expand All @@ -112,7 +125,7 @@ impl ToTokens for GenerateElement {
#(#listeners_if_lets)*
listeners
},
key: self.key,
key: self.key.clone(),
children: self.children.into_iter().collect(),
}
}
Expand Down
36 changes: 36 additions & 0 deletions packages/yew-macro/src/typed_vdom/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,39 @@ pub fn others() -> [AttributePropDefinition; 1] {
parse_quote! { ::yew::virtual_dom::AttrValue },
)]
}

pub fn all_shared_attributes_as_string() -> Vec<String> {
let mut attrs = all_shared_attributes().iter().map(|it| it.name.to_string()).collect::<Vec<String>>();
let listeners = listeners().iter().map(|it| it.ident().to_string()).collect::<Vec<String>>();
attrs.extend(listeners);
attrs
}
pub fn all_shared_attributes() -> Vec<AttributePropDefinition> {
let mut prop_definitions = Vec::new();
prop_definitions.extend(global_attributes());
prop_definitions.extend(all_aria_labels());
prop_definitions.extend(others());
prop_definitions
}

pub mod globals_macro {
use proc_macro2::TokenStream;
use quote::quote;
use super::*;

pub fn globals_impl() -> TokenStream {
let prop_definitions = all_shared_attributes();
let props = prop_definitions.iter().map(|it| it.build_fields());

let all_listeners = listeners();
let listeners = all_listeners.iter().map(|it| it.build_fields());

quote! {
#[derive(Debug, PartialEq, ::yew::Properties, Clone, Default)]
pub struct Globals {
#(#props)*
#(#listeners)*
}
}
}
}
10 changes: 5 additions & 5 deletions packages/yew-macro/src/typed_vdom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ pub use globals::*;

#[derive(Clone)]
pub struct AttributePropDefinition {
name: Ident,
ty: Type,
pub name: Ident,
pub ty: Type,
}

impl Parse for AttributePropDefinition {
Expand Down Expand Up @@ -45,8 +45,8 @@ impl AttributePropDefinition {
let name = self.name.to_string().replace('_', "-");
let name = LitStr::new(&name, self.name.span());
quote! {
if let ::std::option::Option::Some(val) = self.#ident {
attrs.insert(#name, val);
if let ::std::option::Option::Some(val) = self.#ident.as_ref() {
attrs.insert(#name, val.clone());
}
}
}
Expand Down Expand Up @@ -82,7 +82,7 @@ impl ListenerPropDefinition {
fn build_if_lets(&self) -> TokenStream {
let ident = self.ident();
quote! {
if let Some(value) = self.#ident {
if let Some(value) = self.#ident.as_ref() {
listeners.push(::yew::html::#ident::Wrapper::__macro_new(value));
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/yew/src/virtual_dom/typings/globals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
yew_macro::globals!();

Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#![allow(missing_docs)]
//! This module contains the items required for a statically typed VDOM
pub mod globals;

use std::collections::HashMap;
use std::rc::Rc;

Expand Down

0 comments on commit 741c29b

Please sign in to comment.