This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew_macro/
lib.rs

1#![cfg_attr(nightly_yew, feature(proc_macro_span))]
2
3//! This crate provides Yew's procedural macro `html!` which allows using JSX-like syntax
4//! for generating html and the `Properties` derive macro for deriving the `Properties` trait
5//! for components.
6//!
7//! ```
8//! use yew::prelude::*;
9//!
10//! struct Component;
11//!
12//! #[derive(Properties, PartialEq)]
13//! struct Props {
14//!     prop: String,
15//! }
16//!
17//! # enum Msg { Submit }
18//! #
19//! # impl yew::Component for Component {
20//! #     type Message = Msg;
21//! #     type Properties = Props;
22//! #     fn create(_ctx: &Context<Self>) -> Self {
23//! #         unimplemented!()
24//! #     }
25//! #
26//! #
27//! #     fn view(&self, ctx: &Context<Self>) -> Html {
28//! #
29//! // ...
30//!
31//! html! {
32//!   <div>
33//!     <button onclick={ctx.link().callback(|_| Msg::Submit)}>
34//!       { "Submit" }
35//!     </button>
36//!     <>
37//!       <Component prop="first" />
38//!       <Component prop="second" />
39//!     </>
40//!   </div>
41//! }
42//! #
43//! #     }
44//! # }
45//! #
46//! # fn main() {}
47//! ```
48//!
49//! Please refer to [https://github.com/yewstack/yew](https://github.com/yewstack/yew) for how to set this up.
50
51mod classes;
52mod derive_props;
53mod function_component;
54mod hook;
55mod html_tree;
56mod props;
57mod stringify;
58mod use_prepared_state;
59mod use_transitive_state;
60
61use derive_props::DerivePropsInput;
62use function_component::{function_component_impl, FunctionComponent, FunctionComponentName};
63use hook::{hook_impl, HookFn};
64use html_tree::{HtmlRoot, HtmlRootVNode};
65use proc_macro::TokenStream;
66use quote::ToTokens;
67use syn::buffer::Cursor;
68use syn::parse_macro_input;
69use use_prepared_state::PreparedState;
70use use_transitive_state::TransitiveState;
71
72trait Peek<'a, T> {
73    fn peek(cursor: Cursor<'a>) -> Option<(T, Cursor<'a>)>;
74}
75
76trait PeekValue<T> {
77    fn peek(cursor: Cursor) -> Option<T>;
78}
79
80fn non_capitalized_ascii(string: &str) -> bool {
81    if !string.is_ascii() {
82        false
83    } else if let Some(c) = string.bytes().next() {
84        c.is_ascii_lowercase()
85    } else {
86        false
87    }
88}
89
90/// Combine multiple `syn` errors into a single one.
91/// Returns `Result::Ok` if the given iterator is empty
92fn join_errors(mut it: impl Iterator<Item = syn::Error>) -> syn::Result<()> {
93    it.next().map_or(Ok(()), |mut err| {
94        for other in it {
95            err.combine(other);
96        }
97        Err(err)
98    })
99}
100
101fn is_ide_completion() -> bool {
102    match std::env::var_os("RUST_IDE_PROC_MACRO_COMPLETION_DUMMY_IDENTIFIER") {
103        None => false,
104        Some(dummy_identifier) => !dummy_identifier.is_empty(),
105    }
106}
107
108#[proc_macro_derive(Properties, attributes(prop_or, prop_or_else, prop_or_default))]
109pub fn derive_props(input: TokenStream) -> TokenStream {
110    let mut input = parse_macro_input!(input as DerivePropsInput);
111    input.normalise();
112    TokenStream::from(input.into_token_stream())
113}
114
115#[proc_macro_error::proc_macro_error]
116#[proc_macro]
117pub fn html_nested(input: TokenStream) -> TokenStream {
118    let root = parse_macro_input!(input as HtmlRoot);
119    TokenStream::from(root.into_token_stream())
120}
121
122#[proc_macro_error::proc_macro_error]
123#[proc_macro]
124pub fn html(input: TokenStream) -> TokenStream {
125    let root = parse_macro_input!(input as HtmlRootVNode);
126    TokenStream::from(root.into_token_stream())
127}
128
129#[proc_macro]
130pub fn props(input: TokenStream) -> TokenStream {
131    let props = parse_macro_input!(input as props::PropsMacroInput);
132    TokenStream::from(props.into_token_stream())
133}
134
135#[proc_macro]
136pub fn classes(input: TokenStream) -> TokenStream {
137    let classes = parse_macro_input!(input as classes::Classes);
138    TokenStream::from(classes.into_token_stream())
139}
140
141#[proc_macro_error::proc_macro_error]
142#[proc_macro_attribute]
143pub fn function_component(attr: TokenStream, item: TokenStream) -> proc_macro::TokenStream {
144    let item = parse_macro_input!(item as FunctionComponent);
145    let attr = parse_macro_input!(attr as FunctionComponentName);
146
147    function_component_impl(attr, item)
148        .unwrap_or_else(|err| err.to_compile_error())
149        .into()
150}
151
152#[proc_macro_error::proc_macro_error]
153#[proc_macro_attribute]
154pub fn hook(attr: TokenStream, item: TokenStream) -> proc_macro::TokenStream {
155    let item = parse_macro_input!(item as HookFn);
156
157    if let Some(m) = proc_macro2::TokenStream::from(attr).into_iter().next() {
158        return syn::Error::new_spanned(m, "hook attribute does not accept any arguments")
159            .into_compile_error()
160            .into();
161    }
162
163    hook_impl(item)
164        .unwrap_or_else(|err| err.to_compile_error())
165        .into()
166}
167
168#[proc_macro]
169pub fn use_prepared_state_with_closure(input: TokenStream) -> TokenStream {
170    let prepared_state = parse_macro_input!(input as PreparedState);
171    prepared_state.to_token_stream_with_closure().into()
172}
173
174#[proc_macro]
175pub fn use_prepared_state_without_closure(input: TokenStream) -> TokenStream {
176    let prepared_state = parse_macro_input!(input as PreparedState);
177    prepared_state.to_token_stream_without_closure().into()
178}
179
180#[proc_macro]
181pub fn use_transitive_state_with_closure(input: TokenStream) -> TokenStream {
182    let transitive_state = parse_macro_input!(input as TransitiveState);
183    transitive_state.to_token_stream_with_closure().into()
184}
185
186#[proc_macro]
187pub fn use_transitive_state_without_closure(input: TokenStream) -> TokenStream {
188    let transitive_state = parse_macro_input!(input as TransitiveState);
189    transitive_state.to_token_stream_without_closure().into()
190}