tide-jsx v0.4.0
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 +1 -1
- README.md +0 -16
- render/src/fragment.rs +0 -1
- render/src/html.rs +0 -1
- render/src/lib.rs +0 -15
- render_macros/Cargo.toml +1 -0
- render_macros/src/element_attributes.rs +4 -3
- render_macros/src/function_component.rs +2 -1
- render_macros/src/lib.rs +4 -11
- render_macros/src/tags.rs +6 -2
- render_tests/Cargo.toml +1 -1
- render_tests/src/lib.rs +5 -1
- render_tests/ui/fail/unclosed-tag-complex.rs +14 -0
- render_tests/ui/fail/unclosed-tag-complex.stderr +5 -0
- render_tests/ui/fail/unclosed-tag.rs +9 -0
- render_tests/ui/fail/unclosed-tag.stderr +5 -0
- render_tests/ui/fail/unexpected-attribute.rs +10 -0
- render_tests/ui/fail/unexpected-attribute.stderr +7 -0
.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:
|
|
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()
|
|
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
|
-
|
|
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()
|
|
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
|
-
|
|
61
|
+
"Expected closing tag for: <{}>",
|
|
59
|
-
|
|
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
|
-
|
|
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`
|