1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
#![doc = include_str!("../README.md")]
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
use proc_macro::TokenStream;
use quote::ToTokens;
use rsx::RenderCallBody;
use syn::parse::Parser;
use syn::punctuated::Punctuated;
use syn::{parse_macro_input, Path, Token};
mod component_body;
mod component_body_deserializers;
mod props;
mod utils;
// mod rsx;
use crate::component_body::ComponentBody;
use crate::component_body_deserializers::component::ComponentDeserializerArgs;
use crate::component_body_deserializers::inline_props::InlinePropsDeserializerArgs;
use dioxus_rsx as rsx;
#[proc_macro]
pub fn format_args_f(input: TokenStream) -> TokenStream {
use rsx::*;
format_args_f_impl(parse_macro_input!(input as IfmtInput))
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
#[proc_macro_derive(Props, attributes(props))]
pub fn derive_typed_builder(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as syn::DeriveInput);
match props::impl_my_derive(&input) {
Ok(output) => output.into(),
Err(error) => error.to_compile_error().into(),
}
}
/// The rsx! macro makes it easy for developers to write jsx-style markup in their components.
#[proc_macro]
pub fn rsx(tokens: TokenStream) -> TokenStream {
match syn::parse::<rsx::CallBody>(tokens) {
Err(err) => err.to_compile_error().into(),
Ok(body) => RenderCallBody(body).into_token_stream().into(),
}
}
/// The rsx! macro makes it easy for developers to write jsx-style markup in their components.
///
/// The render macro automatically renders rsx - making it unhygienic.
#[deprecated(note = "Use `rsx!` instead.")]
#[proc_macro]
pub fn render(tokens: TokenStream) -> TokenStream {
rsx(tokens)
}
/// Derive props for a component within the component definition.
///
/// This macro provides a simple transformation from `Scope<{}>` to `Scope<P>`,
/// removing some boilerplate when defining props.
///
/// You don't *need* to use this macro at all, but it can be helpful in cases where
/// you would be repeating a lot of the usual Rust boilerplate.
///
/// # Example
/// ```rust,ignore
/// #[inline_props]
/// fn app(bob: String) -> Element {
/// rsx!("hello, {bob}"))
/// }
///
/// // is equivalent to
///
/// #[derive(PartialEq, Props)]
/// struct AppProps {
/// bob: String,
/// }
///
/// fn app(cx: Scope<AppProps>) -> Element {
/// rsx!("hello, {bob}"))
/// }
/// ```
#[proc_macro_attribute]
#[deprecated(note = "Use `#[component]` instead.")]
pub fn inline_props(_args: TokenStream, s: TokenStream) -> TokenStream {
let comp_body = parse_macro_input!(s as ComponentBody);
match comp_body.deserialize(InlinePropsDeserializerArgs {}) {
Err(e) => e.to_compile_error().into(),
Ok(output) => output.to_token_stream().into(),
}
}
pub(crate) const COMPONENT_ARG_CASE_CHECK_OFF: &str = "no_case_check";
/// Streamlines component creation.
/// This is the recommended way of creating components,
/// though you might want lower-level control with more advanced uses.
///
/// # Arguments
/// * `no_case_check` - Doesn't enforce `PascalCase` on your component names.
/// **This will be removed/deprecated in a future update in favor of a more complete Clippy-backed linting system.**
/// The reasoning behind this is that Clippy allows more robust and powerful lints, whereas
/// macros are extremely limited.
///
/// # Features
/// This attribute:
/// * Enforces that your component uses `PascalCase`.
/// No warnings are generated for the `PascalCase`
/// function name, but everything else will still raise a warning if it's incorrectly `PascalCase`.
/// Does not disable warnings anywhere else, so if you, for example,
/// accidentally don't use `snake_case`
/// for a variable name in the function, the compiler will still warn you.
/// * Automatically uses `#[inline_props]` if there's more than 1 parameter in the function.
/// * Verifies the validity of your component.
/// E.g. if it has a [`Scope`](dioxus_core::Scope) argument.
/// Notes:
/// * This doesn't work 100% of the time, because of macro limitations.
/// * Provides helpful messages if your component is not correct.
/// Possible bugs (please, report these!):
/// * There might be bugs where it incorrectly *denies* validity.
/// This is bad as it means that you can't use the attribute or you have to change the component.
/// * There might be bugs where it incorrectly *confirms* validity.
/// You will still know if the component is invalid once you use it,
/// but the error might be less helpful.
///
/// # Examples
/// * Without props:
/// ```rust,ignore
/// #[component]
/// fn GreetBob() -> Element {
/// rsx! { "hello, bob" }
/// }
///
/// // is equivalent to
///
/// #[allow(non_snake_case)]
/// fn GreetBob() -> Element {
/// #[warn(non_snake_case)]
/// #[inline(always)]
/// fn __dx_inner_comp() -> Element {
/// rsx! { "hello, bob" }
/// }
/// // There's no function call overhead since __dx_inner_comp has the #[inline(always)] attribute,
/// // so don't worry about performance.
/// __dx_inner_comp(cx)
/// }
/// ```
/// * With props:
/// ```rust,ignore
/// #[component(no_case_check)]
/// fn GreetPerson(person: String) -> Element {
/// rsx! { "hello, {person}" }
/// }
///
/// // is equivalent to
///
/// #[derive(Props, PartialEq)]
/// #[allow(non_camel_case_types)]
/// struct GreetPersonProps {
/// person: String,
/// }
///
/// #[allow(non_snake_case)]
/// fn GreetPerson(props: GreetPersonProps>) -> Element {
/// #[warn(non_snake_case)]
/// #[inline(always)]
/// fn __dx_inner_comp(props: GreetPersonProps>e) -> Element {
/// let GreetPersonProps { person } = &cx.props;
/// {
/// rsx! { "hello, {person}" }
/// }
/// }
///
/// __dx_inner_comp(cx)
/// }
/// ```
// TODO: Maybe add an option to input a custom component name through the args.
// I think that's unnecessary, but there might be some scenario where it could be useful.
#[proc_macro_attribute]
pub fn component(args: TokenStream, input: TokenStream) -> TokenStream {
let component_body = parse_macro_input!(input as ComponentBody);
let case_check = match Punctuated::<Path, Token![,]>::parse_terminated.parse(args) {
Err(e) => return e.to_compile_error().into(),
Ok(args) => {
if let Some(first) = args.first() {
!first.is_ident(COMPONENT_ARG_CASE_CHECK_OFF)
} else {
true
}
}
};
match component_body.deserialize(ComponentDeserializerArgs { case_check }) {
Err(e) => e.to_compile_error().into(),
Ok(output) => output.to_token_stream().into(),
}
}