tide-jsx v0.4.0

#rust#proc-macro#jsx

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

Tide + JSX


553f893 — Naitik Shah 2020-06-11T14:00:09+04:00
Prepare for running in stable Rust (#27)

Generated files not shown: Cargo.lock

.github/workflows/rust.yml CHANGED
@@ -12,7 +12,7 @@ jobs:
12
12
  steps:
13
13
  - uses: hecrj/setup-rust-action@v1
14
14
  with:
15
- rust-version: nightly
15
+ rust-version: beta
16
16
  - uses: actions/checkout@v1
17
17
  - name: Build
18
18
  run: cargo build --verbose
README.md CHANGED
@@ -34,22 +34,12 @@ ergonomics or speed.
34
34
 
35
35
  ## Usage
36
36
 
37
- > Note: `render` needs the `nightly` Rust compiler, for now, so it will have hygienic macros.
38
-
39
- This means you will need to add the following feature flag in the root of your `lib.rs`/`main.rs`:
40
-
41
- ```rust
42
- #![feature(proc_macro_hygiene)]
43
- ```
44
-
45
37
  ### Simple HTML rendering
46
38
 
47
39
  In order to render a simple HTML fragment into a `String`, use the `rsx!` macro to generate a
48
40
  component tree, and call `render` on it:
49
41
 
50
42
  ```rust
51
- #![feature(proc_macro_hygiene)]
52
-
53
43
  use render::{rsx, Render};
54
44
 
55
45
  let tree = rsx! {
@@ -72,8 +62,6 @@ use un-escaped values so you can dangerously insert raw HTML, use the `raw!` mac
72
62
  string:
73
63
 
74
64
  ```rust
75
- #![feature(proc_macro_hygiene)]
76
-
77
65
  use render::{html, raw};
78
66
 
79
67
  let tree = html! {
@@ -95,8 +83,6 @@ In order to build up components from other components or HTML nodes, you can use
95
83
  macro, which generates a `Render` component tree:
96
84
 
97
85
  ```rust
98
- #![feature(proc_macro_hygiene)]
99
-
100
86
  use render::{component, rsx, html};
101
87
 
102
88
  #[component]
@@ -148,8 +134,6 @@ your libraries.
148
134
  #### Full example
149
135
 
150
136
  ```rust
151
- #![feature(proc_macro_hygiene)]
152
-
153
137
  // A simple HTML 5 doctype declaration
154
138
  use render::html::HTML5Doctype;
155
139
  use render::{
render/src/fragment.rs CHANGED
@@ -7,7 +7,6 @@ use std::fmt::{Result, Write};
7
7
  /// in a RSX fashion
8
8
  ///
9
9
  /// ```rust
10
- /// # #![feature(proc_macro_hygiene)]
11
10
  /// # use pretty_assertions::assert_eq;
12
11
  /// # use render_macros::html;
13
12
  /// let result = html! {
render/src/html.rs CHANGED
@@ -6,7 +6,6 @@ use std::fmt::{Result, Write};
6
6
  /// HTML 5 doctype declaration
7
7
  ///
8
8
  /// ```rust
9
- /// # #![feature(proc_macro_hygiene)]
10
9
  /// # use pretty_assertions::assert_eq;
11
10
  /// # use render::html::HTML5Doctype;
12
11
  /// # use render::html;
render/src/lib.rs CHANGED
@@ -32,21 +32,12 @@
32
32
  //!
33
33
  //! # Usage
34
34
  //!
35
- //! > Note: `render` needs the `nightly` Rust compiler, for now, so it will have hygienic macros.
36
- //!
37
- //! This means you will need to add the following feature flag in the root of your `lib.rs`/`main.rs`:
38
- //!
39
- //! ```rust
40
- //! #![feature(proc_macro_hygiene)]
41
- //! ```
42
- //!
43
35
  //! ## Simple HTML rendering
44
36
  //!
45
37
  //! In order to render a simple HTML fragment into a `String`, use the `rsx!` macro to generate a
46
38
  //! component tree, and call `render` on it:
47
39
  //!
48
40
  //! ```rust
49
- //! #![feature(proc_macro_hygiene)]
50
41
  //! # use pretty_assertions::assert_eq;
51
42
  //!
52
43
  //! use render::{rsx, Render};
@@ -71,7 +62,6 @@
71
62
  //! string:
72
63
  //!
73
64
  //! ```rust
74
- //! #![feature(proc_macro_hygiene)]
75
65
  //! # use pretty_assertions::assert_eq;
76
66
  //!
77
67
  //! use render::{html, raw};
@@ -95,7 +85,6 @@
95
85
  //! macro, which generates a `Render` component tree:
96
86
  //!
97
87
  //! ```rust
98
- //! #![feature(proc_macro_hygiene)]
99
88
  //! # use pretty_assertions::assert_eq;
100
89
  //!
101
90
  //! use render::{component, rsx, html};
@@ -122,8 +111,6 @@
122
111
  //! ### Full example
123
112
  //!
124
113
  //! ```rust
125
- //! #![feature(proc_macro_hygiene)]
126
- //!
127
114
  //! // A simple HTML 5 doctype declaration
128
115
  //! use render::html::HTML5Doctype;
129
116
  //! use render::{
@@ -177,8 +164,6 @@
177
164
  //! # assert_eq!(actual, expected);
178
165
  //! ```
179
166
 
180
- #![feature(proc_macro_hygiene)]
181
-
182
167
  pub mod fragment;
183
168
  pub mod html;
184
169
  pub mod html_escaping;
render_macros/Cargo.toml CHANGED
@@ -19,6 +19,7 @@ proc-macro = true
19
19
  syn = { version = "1.0", features = ["full"] }
20
20
  quote = "1.0"
21
21
  proc-macro2 = "1.0"
22
+ proc-macro-error = "1.0"
22
23
 
23
24
  [dev-dependencies]
24
25
  render = { path = "../render", version = "0.3" }
render_macros/src/element_attributes.rs CHANGED
@@ -1,5 +1,6 @@
1
1
  use crate::children::Children;
2
2
  use crate::element_attribute::ElementAttribute;
3
+ use proc_macro_error::emit_error;
3
4
  use quote::{quote, ToTokens};
4
5
  use std::collections::HashSet;
5
6
  use syn::ext::IdentExt;
@@ -43,7 +44,7 @@ impl ElementAttributes {
43
44
  .filter_map(|attribute| match attribute.validate(is_custom_element) {
44
45
  Ok(x) => Some(x),
45
46
  Err(err) => {
46
- err.span().unwrap().error(err.to_string()).emit();
47
+ emit_error!(err.span(), "Invalid attribute: {}", err);
47
48
  None
48
49
  }
49
50
  })
@@ -60,11 +61,11 @@ impl Parse for ElementAttributes {
60
61
  let attribute = input.parse::<ElementAttribute>()?;
61
62
  let ident = attribute.ident();
62
63
  if attributes.contains(&attribute) {
63
- let error_message = format!(
64
+ emit_error!(
65
+ ident.span(),
64
66
  "There is a previous definition of the {} attribute",
65
67
  quote!(#ident)
66
68
  );
67
- ident.span().unwrap().warning(error_message).emit();
68
69
  }
69
70
  attributes.insert(attribute);
70
71
  }
render_macros/src/function_component.rs CHANGED
@@ -1,4 +1,5 @@
1
1
  use proc_macro::TokenStream;
2
+ use proc_macro_error::emit_error;
2
3
  use quote::quote;
3
4
  use syn::spanned::Spanned;
4
5
 
@@ -25,7 +26,7 @@ pub fn create_function_component(f: syn::ItemFn) -> TokenStream {
25
26
  .filter_map(|argument| match argument {
26
27
  syn::FnArg::Typed(typed) => Some(typed),
27
28
  syn::FnArg::Receiver(rec) => {
28
- rec.span().unwrap().error("Don't use `self` on components");
29
+ emit_error!(rec.span(), "Don't use `self` on components");
29
30
  None
30
31
  }
31
32
  })
render_macros/src/lib.rs CHANGED
@@ -1,5 +1,3 @@
1
- #![feature(proc_macro_diagnostic)]
2
-
3
1
  extern crate proc_macro;
4
2
 
5
3
  mod child;
@@ -12,6 +10,7 @@ mod tags;
12
10
 
13
11
  use element::Element;
14
12
  use proc_macro::TokenStream;
13
+ use proc_macro_error::proc_macro_error;
15
14
  use quote::quote;
16
15
  use syn::parse_macro_input;
17
16
 
@@ -22,7 +21,6 @@ use syn::parse_macro_input;
22
21
  /// ### Simple HTML elements start with a lowercase
23
22
  ///
24
23
  /// ```rust
25
- /// # #![feature(proc_macro_hygiene)]
26
24
  /// # use pretty_assertions::assert_eq;
27
25
  /// # use render_macros::html;
28
26
  /// let rendered = html! { <div id={"main"}>{"Hello"}</div> };
@@ -32,7 +30,6 @@ use syn::parse_macro_input;
32
30
  /// ### Custom components start with an uppercase
33
31
  ///
34
32
  /// ```rust
35
- /// # #![feature(proc_macro_hygiene)]
36
33
  /// # use pretty_assertions::assert_eq;
37
34
  /// # use render_macros::{html, rsx};
38
35
  /// use render::Render;
@@ -54,7 +51,6 @@ use syn::parse_macro_input;
54
51
  /// ### Values are always surrounded by curly braces
55
52
  ///
56
53
  /// ```rust
57
- /// # #![feature(proc_macro_hygiene)]
58
54
  /// # use render_macros::html;
59
55
  /// # use pretty_assertions::assert_eq;
60
56
  /// let rendered = html! {
@@ -67,7 +63,6 @@ use syn::parse_macro_input;
67
63
  /// ### HTML entities can accept dashed-separated value
68
64
  ///
69
65
  /// ```rust
70
- /// # #![feature(proc_macro_hygiene)]
71
66
  /// # use render_macros::html;
72
67
  /// # use pretty_assertions::assert_eq;
73
68
  /// let rendered = html! {
@@ -80,7 +75,6 @@ use syn::parse_macro_input;
80
75
  /// ### Custom components can't accept dashed-separated values
81
76
  ///
82
77
  /// ```compile_fail
83
- /// # #![feature(proc_macro_hygiene)]
84
78
  /// # use render_macros::html;
85
79
  /// // This will fail the compilation:
86
80
  /// let rendered = html! {
@@ -93,7 +87,6 @@ use syn::parse_macro_input;
93
87
  /// `value={value}` like Rust's punning
94
88
  ///
95
89
  /// ```rust
96
- /// # #![feature(proc_macro_hygiene)]
97
90
  /// # use render_macros::html;
98
91
  /// # use pretty_assertions::assert_eq;
99
92
  /// let class = "someclass";
@@ -108,7 +101,6 @@ use syn::parse_macro_input;
108
101
  /// ### Punning is not supported for dashed-delimited attributes
109
102
  ///
110
103
  /// ```compile_fail
111
- /// # #![feature(proc_macro_hygiene)]
112
104
  /// # use render_macros::html;
113
105
  ///
114
106
  /// let rendered = html! {
@@ -118,6 +110,7 @@ use syn::parse_macro_input;
118
110
  /// assert_eq!(rendered, r#"<div class="some_class"/>"#);
119
111
  /// ```
120
112
  #[proc_macro]
113
+ #[proc_macro_error]
121
114
  pub fn html(input: TokenStream) -> TokenStream {
122
115
  let el = proc_macro2::TokenStream::from(rsx(input));
123
116
  let result = quote! { ::render::Render::render(#el) };
@@ -126,6 +119,7 @@ pub fn html(input: TokenStream) -> TokenStream {
126
119
 
127
120
  /// Generate a renderable component tree, before rendering it
128
121
  #[proc_macro]
122
+ #[proc_macro_error]
129
123
  pub fn rsx(input: TokenStream) -> TokenStream {
130
124
  let el = parse_macro_input!(input as Element);
131
125
  let result = quote! { #el };
@@ -139,7 +133,6 @@ pub fn rsx(input: TokenStream) -> TokenStream {
139
133
  /// [`String`](std::string::String):
140
134
  ///
141
135
  /// ```rust
142
- /// # #![feature(proc_macro_hygiene)]
143
136
  /// # use render_macros::{component, rsx};
144
137
  /// #
145
138
  /// #[component]
@@ -151,7 +144,6 @@ pub fn rsx(input: TokenStream) -> TokenStream {
151
144
  /// Practically, this is exactly the same as using the [Render](../render/trait.Render.html) trait:
152
145
  ///
153
146
  /// ```rust
154
- /// # #![feature(proc_macro_hygiene)]
155
147
  /// # use render_macros::{component, rsx, html};
156
148
  /// # use render::Render;
157
149
  /// # use pretty_assertions::assert_eq;
@@ -181,6 +173,7 @@ pub fn rsx(input: TokenStream) -> TokenStream {
181
173
  /// # assert_eq!(from_fn, from_struct);
182
174
  /// ```
183
175
  #[proc_macro_attribute]
176
+ #[proc_macro_error]
184
177
  pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream {
185
178
  let f = parse_macro_input!(item as syn::ItemFn);
186
179
  function_component::create_function_component(f)
render_macros/src/tags.rs CHANGED
@@ -1,4 +1,5 @@
1
1
  use crate::element_attributes::ElementAttributes;
2
+ use proc_macro_error::abort;
2
3
  use quote::quote;
3
4
  use syn::parse::{Parse, ParseStream, Result};
4
5
  use syn::spanned::Spanned;
@@ -55,8 +56,11 @@ impl ClosingTag {
55
56
  let self_path = &self.name;
56
57
  let self_path_str = quote!(#self_path).to_string();
57
58
  if self_path_str != open_tag_path_str {
59
+ abort!(
60
+ self.name.span(),
58
- let error_message = format!("Expected closing tag for: <{}>", &open_tag_path_str);
61
+ "Expected closing tag for: <{}>",
59
- self.name.span().unwrap().error(error_message).emit();
62
+ &open_tag_path_str
63
+ );
60
64
  }
61
65
  }
62
66
  }
render_tests/Cargo.toml CHANGED
@@ -13,4 +13,4 @@ render = { path = "../render" }
13
13
 
14
14
  [dev-dependencies]
15
15
  pretty_assertions = "0.6"
16
-
16
+ trybuild = "1.0"
render_tests/src/lib.rs CHANGED
@@ -1,4 +1,8 @@
1
+ #[test]
2
+ fn ui() {
1
- #![feature(proc_macro_hygiene)]
3
+ let t = trybuild::TestCases::new();
4
+ t.compile_fail("ui/fail/*.rs");
5
+ }
2
6
 
3
7
  #[test]
4
8
  pub fn works_with_dashes() {
render_tests/ui/fail/unclosed-tag-complex.rs ADDED
@@ -0,0 +1,14 @@
1
+ use render::html;
2
+
3
+ fn main() {
4
+ html! {
5
+ <div>
6
+ <h1>{"A list"}</h1>
7
+ <hr />
8
+ <ul>
9
+ <li>{"1"}</li>
10
+ <li>
11
+ </ul>
12
+ </div>
13
+ };
14
+ }
render_tests/ui/fail/unclosed-tag-complex.stderr ADDED
@@ -0,0 +1,5 @@
1
+ error: Expected closing tag for: <li>
2
+ --> $DIR/unclosed-tag-complex.rs:11:11
3
+ |
4
+ 11 | </ul>
5
+ | ^^
render_tests/ui/fail/unclosed-tag.rs ADDED
@@ -0,0 +1,9 @@
1
+ use render::html;
2
+
3
+ fn main() {
4
+ html! {
5
+ <ul>
6
+ <li>
7
+ </ul>
8
+ };
9
+ }
render_tests/ui/fail/unclosed-tag.stderr ADDED
@@ -0,0 +1,5 @@
1
+ error: Expected closing tag for: <li>
2
+ --> $DIR/unclosed-tag.rs:7:9
3
+ |
4
+ 7 | </ul>
5
+ | ^^
render_tests/ui/fail/unexpected-attribute.rs ADDED
@@ -0,0 +1,10 @@
1
+ use render::{component, html, rsx};
2
+
3
+ #[component]
4
+ fn Heading<'title>(title: &'title str) {
5
+ rsx! { <h1>{title}</h1> }
6
+ }
7
+
8
+ fn main() {
9
+ html! { <Heading t={"Hello world!"} /> };
10
+ }
render_tests/ui/fail/unexpected-attribute.stderr ADDED
@@ -0,0 +1,7 @@
1
+ error[E0560]: struct `Heading<'_>` has no field named `t`
2
+ --> $DIR/unexpected-attribute.rs:9:22
3
+ |
4
+ 9 | html! { <Heading t={"Hello world!"} /> };
5
+ | ^ `Heading<'_>` does not have this field
6
+ |
7
+ = note: available fields are: `title`