~repos /tide-jsx

#rust#proc-macro#jsx

git clone https://pyrossh.dev/repos/tide-jsx.git

Tide + JSX


dca74ba5 Ash Guy

5 years ago
[Fix] Elements Ordering & Struct Field Visibility (#17)
.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  target
2
2
  **/*.rs.bk
3
+ .DS_Store
README.md CHANGED
@@ -11,9 +11,9 @@ XML rendering, but can work with other usages as well, like ReasonML's [`Pastel`
11
11
  A renderable component is a struct that implements the `Render` trait. There
12
12
  are multiple macros that provide a better experience implementing Renderable:
13
13
 
14
- * `#[component]` for defining components using a function
14
+ - `#[component]` for defining components using a function
15
- * `rsx!` for composing elements with JSX ergonomics
15
+ - `rsx!` for composing elements with JSX ergonomics
16
- * `html!` for composing elements and render them to a string
16
+ - `html!` for composing elements and render them to a string
17
17
 
18
18
  ## Why is this different from...
19
19
 
@@ -113,10 +113,37 @@ assert_eq!(rendered_html, r#"<h1 class="title">Hello world!</h1>"#);
113
113
 
114
114
  If you pay close attention, you see that the function `Heading` is:
115
115
 
116
- * declared with an uppercase. Underneath, it generates a struct with the same name, and
116
+ - declared with an uppercase. Underneath, it generates a struct with the same name, and
117
- implements the `Render` trait on it.
117
+ implements the `Render` trait on it.
118
- * does not have a return type. This is because everything is written to a writer, for
118
+ - does not have a return type. This is because everything is written to a writer, for
119
- performance reasons.
119
+ performance reasons.
120
+
121
+ ### Visibility & Component Libraries
122
+
123
+ Often you're going to want to store your components somewhere else in your
124
+ project tree other than the module you're working on (if not in a different
125
+ module entirely!). In these cases, the visibility applied top the function that
126
+ defines your component will flow down into all fields of that struct.
127
+
128
+ For example, if we add "pub" to the front of our Heading component above:
129
+
130
+ ```rust
131
+ #[component]
132
+ pub fn Heading<'title>(title: &'title str) {
133
+ rsx! { <h1 class={"title"}>{title}</h1> }
134
+ }
135
+ ```
136
+
137
+ ...the struct that is generated would look something like...
138
+
139
+ ```rust
140
+ pub struct Heading {
141
+ pub title: &'title str
142
+ }
143
+ ```
144
+
145
+ This is important to understand from a safety point of view when structuring
146
+ your libraries.
120
147
 
121
148
  #### Full example
122
149
 
render_macros/src/children.rs CHANGED
@@ -24,16 +24,19 @@ impl Children {
24
24
  quote! { #child }
25
25
  })
26
26
  .collect();
27
+
27
28
  match children_quotes.len() {
28
29
  0 => quote! { Option::<()>::None },
29
- 1 => quote! { Some(#(#children_quotes)*) },
30
+ 1 => quote! { Some(#(#children_quotes),*) },
30
31
  _ => {
31
32
  let mut iter = children_quotes.iter();
33
+
32
34
  let first = iter.next().unwrap();
33
35
  let second = iter.next().unwrap();
36
+
34
37
  let tuple_of_tuples = iter.fold(
35
38
  quote!((#first, #second)),
36
- |renderable, current| quote!((#current, #renderable)),
39
+ |renderable, current| quote!((#renderable, #current)),
37
40
  );
38
41
 
39
42
  quote! { Some(#tuple_of_tuples) }
render_macros/src/function_component.rs CHANGED
@@ -10,7 +10,11 @@ pub fn create_function_component(f: syn::ItemFn) -> TokenStream {
10
10
  let vis = f.vis;
11
11
 
12
12
  let inputs_block = if inputs.len() > 0 {
13
+ let input_names: Vec<_> = inputs
14
+ .iter()
15
+ .collect();
16
+
13
- quote!({ #inputs })
17
+ quote!({ #(#vis #input_names),* })
14
18
  } else {
15
19
  quote!(;)
16
20
  };
render_tests/src/lib.rs CHANGED
@@ -20,6 +20,36 @@ pub fn works_with_raw() {
20
20
  assert_eq!(actual, "<div><Hello /></div>");
21
21
  }
22
22
 
23
+ #[test]
24
+ pub fn element_ordering() {
25
+ use pretty_assertions::assert_eq;
26
+ use render::{html, raw};
27
+
28
+ let actual = html! {
29
+ <ul>
30
+ <li>{"1"}</li>
31
+ <li>{"2"}</li>
32
+ <li>{"3"}</li>
33
+ </ul>
34
+ };
35
+
36
+ assert_eq!(actual, "<ul><li>1</li><li>2</li><li>3</li></ul>");
37
+
38
+ let deep = html! {
39
+ <div>
40
+ <h1>{"A list"}</h1>
41
+ <hr />
42
+ <ul>
43
+ <li>{"1"}</li>
44
+ <li>{"2"}</li>
45
+ <li>{"3"}</li>
46
+ </ul>
47
+ </div>
48
+ };
49
+
50
+ assert_eq!(deep, "<div><h1>A list</h1><hr/><ul><li>1</li><li>2</li><li>3</li></ul></div>");
51
+ }
52
+
23
53
  mod kaki {
24
54
  // A simple HTML 5 doctype declaration
25
55
  use render::html::HTML5Doctype;
@@ -67,4 +97,57 @@ mod kaki {
67
97
  );
68
98
  assert_eq!(actual, expected);
69
99
  }
100
+
101
+ #[test]
102
+ fn externals_test() {
103
+ use pretty_assertions::assert_eq;
104
+ use crate::other::ExternalPage;
105
+
106
+ let actual = render::html! {
107
+ <ExternalPage title={"Home"} subtitle={"Foo"}>
108
+ {format!("Welcome, {}", "Gal")}
109
+ </ExternalPage>
110
+ };
111
+
112
+ let expected = concat!(
113
+ "<!DOCTYPE html>",
114
+ "<html>",
115
+ "<head><title>Home</title></head>",
116
+ "<body>",
117
+ "<h1>Foo</h1>",
118
+ "Welcome, Gal",
119
+ "</body>",
120
+ "</html>"
121
+ );
122
+ assert_eq!(actual, expected);
123
+ }
70
124
  }
125
+
126
+ /// ## Other
127
+ ///
128
+ /// Module for testing component visibility when imported from other modules.
129
+
130
+ mod other {
131
+ use render::html::HTML5Doctype;
132
+ use render::{ component, rsx, Render };
133
+
134
+ #[component]
135
+ pub fn ExternalPage<'title, 'subtitle, Children: Render>(
136
+ title: &'title str,
137
+ subtitle: &'subtitle str,
138
+ children: Children
139
+ ) {
140
+ rsx! {
141
+ <>
142
+ <HTML5Doctype />
143
+ <html>
144
+ <head><title>{title}</title></head>
145
+ <body>
146
+ <h1>{subtitle}</h1>
147
+ {children}
148
+ </body>
149
+ </html>
150
+ </>
151
+ }
152
+ }
153
+ }