leptos_macro/
lib.rs

1//! Macros for use with the Leptos framework.
2
3#![cfg_attr(feature = "nightly", feature(proc_macro_span))]
4#![forbid(unsafe_code)]
5// to prevent warnings from popping up when a nightly feature is stabilized
6#![allow(stable_features)]
7// FIXME? every use of quote! {} is warning here -- false positive?
8#![allow(unknown_lints)]
9#![allow(private_macro_use)]
10#![deny(missing_docs)]
11
12#[macro_use]
13extern crate proc_macro_error2;
14
15use component::DummyModel;
16use proc_macro::TokenStream;
17use proc_macro2::{Span, TokenTree};
18use quote::{quote, ToTokens};
19use std::str::FromStr;
20use syn::{parse_macro_input, spanned::Spanned, token::Pub, Visibility};
21
22mod params;
23mod view;
24use crate::component::unmodified_fn_name_from_fn_name;
25mod component;
26mod lazy;
27mod memo;
28mod slice;
29mod slot;
30
31/// The `view` macro uses RSX (like JSX, but Rust!) It follows most of the
32/// same rules as HTML, with the following differences:
33///
34/// 1. Text content should be provided as a Rust string, i.e., double-quoted:
35/// ```rust
36/// # use leptos::prelude::*;
37/// # fn test() -> impl IntoView {
38/// view! { <p>"Here’s some text"</p> }
39/// # }
40/// ```
41///
42/// 2. Self-closing tags need an explicit `/` as in XML/XHTML
43/// ```rust,compile_fail
44/// # use leptos::prelude::*;
45///
46/// # fn test() -> impl IntoView {
47/// // ❌ not like this
48/// view! { <input type="text" name="name"> }
49/// # ;
50/// # }
51/// ```
52/// ```rust
53/// # use leptos::prelude::*;
54/// # fn test() -> impl IntoView {
55/// // ✅ add that slash
56/// view! { <input type="text" name="name" /> }
57/// # }
58/// ```
59///
60/// 3. Components (functions annotated with `#[component]`) can be inserted as camel-cased tags. (Generics
61///    on components are specified as `<Component<T>/>`, not the turbofish `<Component::<T>/>`.)
62/// ```rust
63/// # use leptos::prelude::*;
64///
65/// # #[component]
66/// # fn Counter(initial_value: i32) -> impl IntoView { view! { <p></p>} }
67/// # fn test() -> impl IntoView {
68/// view! { <div><Counter initial_value=3 /></div> }
69/// # ;
70/// # }
71/// ```
72///
73/// 4. Dynamic content can be wrapped in curly braces (`{ }`) to insert text nodes, elements, or set attributes.
74///    If you insert a signal here, Leptos will create an effect to update the DOM whenever the value changes.
75///    *(“Signal” here means `Fn() -> T` where `T` is the appropriate type for that node: a `String` in case
76///    of text nodes, a `bool` for `class:` attributes, etc.)*
77///
78///    Attributes can take a wide variety of primitive types that can be converted to strings. They can also
79///    take an `Option`, in which case `Some` sets the attribute and `None` removes the attribute.
80///
81///    Note that in some cases, rust-analyzer support may be better if attribute values are surrounded with braces (`{}`).
82///    Unlike in JSX, attribute values are not required to be in braces, but braces can be used and may improve this LSP support.
83///
84/// ```rust,ignore
85/// # use leptos::prelude::*;
86///
87/// # fn test() -> impl IntoView {
88/// let (count, set_count) = create_signal(0);
89///
90/// view! {
91///   // ❌ not like this: `count.get()` returns an `i32`, not a function
92///   <p>{count.get()}</p>
93///   // ✅ this is good: Leptos sees the function and knows it's a dynamic value
94///   <p>{move || count.get()}</p>
95///   // 🔥 with the `nightly` feature, `count` is a function, so `count` itself can be passed directly into the view
96///   <p>{count}</p>
97/// }
98/// # ;
99/// # };
100/// ```
101///
102/// 5. Event handlers can be added with `on:` attributes. In most cases, the events are given the correct type
103///    based on the event name.
104/// ```rust
105/// # use leptos::prelude::*;
106/// # fn test() -> impl IntoView {
107/// view! {
108///   <button on:click=|ev| {
109///     log::debug!("click event: {ev:#?}");
110///   }>
111///     "Click me"
112///   </button>
113/// }
114/// # }
115/// ```
116///
117/// 6. DOM properties can be set with `prop:` attributes, which take any primitive type or `JsValue` (or a signal
118///    that returns a primitive or JsValue). They can also take an `Option`, in which case `Some` sets the property
119///    and `None` deletes the property.
120/// ```rust
121/// # use leptos::prelude::*;
122/// # fn test() -> impl IntoView {
123/// let (name, set_name) = create_signal("Alice".to_string());
124///
125/// view! {
126///   <input
127///     type="text"
128///     name="user_name"
129///     value={move || name.get()} // this only sets the default value!
130///     prop:value={move || name.get()} // here's how you update values. Sorry, I didn’t invent the DOM.
131///     on:click=move |ev| set_name.set(event_target_value(&ev)) // `event_target_value` is a useful little Leptos helper
132///   />
133/// }
134/// # }
135/// ```
136///
137/// 7. Classes can be toggled with `class:` attributes, which take a `bool` (or a signal that returns a `bool`).
138/// ```rust
139/// # use leptos::prelude::*;
140/// # fn test() -> impl IntoView {
141/// let (count, set_count) = create_signal(2);
142/// view! { <div class:hidden-div={move || count.get() < 3}>"Now you see me, now you don’t."</div> }
143/// # }
144/// ```
145///
146/// Class names can include dashes, and since v0.5.0 can include a dash-separated segment of only numbers.
147/// ```rust
148/// # use leptos::prelude::*;
149/// # fn test() -> impl IntoView {
150/// let (count, set_count) = create_signal(2);
151/// view! { <div class:hidden-div-25={move || count.get() < 3}>"Now you see me, now you don’t."</div> }
152/// # }
153/// ```
154///
155/// Class names cannot include special symbols.
156/// ```rust,compile_fail
157/// # use leptos::prelude::*;
158/// # fn test() -> impl IntoView {
159/// let (count, set_count) = create_signal(2);
160/// // class:hidden-[div]-25 is invalid attribute name
161/// view! { <div class:hidden-[div]-25={move || count.get() < 3}>"Now you see me, now you don’t."</div> }
162/// # }
163/// ```
164///
165/// However, you can pass arbitrary class names using the syntax `class=("name", value)`.
166/// ```rust
167/// # use leptos::prelude::*;
168/// # fn test() -> impl IntoView {
169/// let (count, set_count) = create_signal(2);
170/// // this allows you to use CSS frameworks that include complex class names
171/// view! {
172///   <div
173///     class=("is-[this_-_really]-necessary-42", move || count.get() < 3)
174///   >
175///     "Now you see me, now you don’t."
176///   </div>
177/// }
178/// # }
179/// ```
180///
181/// 8. Individual styles can also be set with `style:` or `style=("property-name", value)` syntax.
182/// ```rust
183/// # use leptos::prelude::*;
184///
185/// # fn test() -> impl IntoView {
186/// let (x, set_x) = create_signal(0);
187/// let (y, set_y) = create_signal(0);
188/// view! {
189///   <div
190///     style="position: absolute"
191///     style:left=move || format!("{}px", x.get())
192///     style:top=move || format!("{}px", y.get())
193///     style=("background-color", move || format!("rgb({}, {}, 100)", x.get(), y.get()))
194///   >
195///     "Moves when coordinates change"
196///   </div>
197/// }
198/// # }
199/// ```
200///
201/// 9. You can use the `node_ref` or `_ref` attribute to store a reference to its DOM element in a
202///    [NodeRef](https://docs.rs/leptos/latest/leptos/struct.NodeRef.html) to use later.
203/// ```rust
204/// # use leptos::prelude::*;
205///
206/// # fn test() -> impl IntoView {
207/// use leptos::html::Input;
208///
209/// let (value, set_value) = signal(0);
210/// let my_input = NodeRef::<Input>::new();
211/// view! { <input type="text" node_ref=my_input/> }
212/// // `my_input` now contains an `Element` that we can use anywhere
213/// # ;
214/// # };
215/// ```
216///
217/// 10. You can add the same class to every element in the view by passing in a special
218///    `class = {/* ... */},` argument after ``. This is useful for injecting a class
219///    provided by a scoped styling library.
220/// ```rust
221/// # use leptos::prelude::*;
222///
223/// # fn test() -> impl IntoView {
224/// let class = "mycustomclass";
225/// view! { class = class,
226///   <div> // will have class="mycustomclass"
227///     <p>"Some text"</p> // will also have class "mycustomclass"
228///   </div>
229/// }
230/// # }
231/// ```
232///
233/// 11. You can set any HTML element’s `innerHTML` with the `inner_html` attribute on an
234///     element. Be careful: this HTML will not be escaped, so you should ensure that it
235///     only contains trusted input.
236/// ```rust
237/// # use leptos::prelude::*;
238/// # fn test() -> impl IntoView {
239/// let html = "<p>This HTML will be injected.</p>";
240/// view! {
241///   <div inner_html=html/>
242/// }
243/// # }
244/// ```
245///
246/// Here’s a simple example that shows off several of these features, put together
247/// ```rust
248/// # use leptos::prelude::*;
249/// pub fn SimpleCounter() -> impl IntoView {
250///     // create a reactive signal with the initial value
251///     let (value, set_value) = create_signal(0);
252///
253///     // create event handlers for our buttons
254///     // note that `value` and `set_value` are `Copy`, so it's super easy to move them into closures
255///     let clear = move |_ev| set_value.set(0);
256///     let decrement = move |_ev| set_value.update(|value| *value -= 1);
257///     let increment = move |_ev| set_value.update(|value| *value += 1);
258///
259///     view! {
260///         <div>
261///             <button on:click=clear>"Clear"</button>
262///             <button on:click=decrement>"-1"</button>
263///             <span>"Value: " {move || value.get().to_string()} "!"</span>
264///             <button on:click=increment>"+1"</button>
265///         </div>
266///     }
267/// }
268/// ```
269#[proc_macro_error2::proc_macro_error]
270#[proc_macro]
271#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
272pub fn view(tokens: TokenStream) -> TokenStream {
273    view_macro_impl(tokens, false)
274}
275
276/// The `template` macro behaves like [`view`](view!), except that it wraps the entire tree in a
277/// [`ViewTemplate`](https://docs.rs/leptos/0.7.0-gamma3/leptos/prelude/struct.ViewTemplate.html). This optimizes creation speed by rendering
278/// most of the view into a `<template>` tag with HTML rendered at compile time, then hydrating it.
279/// In exchange, there is a small binary size overhead.
280#[proc_macro_error2::proc_macro_error]
281#[proc_macro]
282#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
283pub fn template(tokens: TokenStream) -> TokenStream {
284    view_macro_impl(tokens, true)
285}
286
287fn view_macro_impl(tokens: TokenStream, template: bool) -> TokenStream {
288    let tokens: proc_macro2::TokenStream = tokens.into();
289    let mut tokens = tokens.into_iter();
290
291    let first = tokens.next();
292    let second = tokens.next();
293    let third = tokens.next();
294    let fourth = tokens.next();
295    let global_class = match (&first, &second) {
296        (Some(TokenTree::Ident(first)), Some(TokenTree::Punct(eq)))
297            if *first == "class" && eq.as_char() == '=' =>
298        {
299            match &fourth {
300                Some(TokenTree::Punct(comma)) if comma.as_char() == ',' => {
301                    third.clone()
302                }
303                _ => {
304                    abort!(
305                        second, "To create a scope class with the view! macro you must put a comma `,` after the value";
306                        help = r#"e.g., view!{ class="my-class", <div>...</div>}"#
307                    )
308                }
309            }
310        }
311        _ => None,
312    };
313    let tokens = if global_class.is_some() {
314        tokens.collect::<proc_macro2::TokenStream>()
315    } else {
316        [first, second, third, fourth]
317            .into_iter()
318            .flatten()
319            .chain(tokens)
320            .collect()
321    };
322    let config = rstml::ParserConfig::default().recover_block(true);
323    let parser = rstml::Parser::new(config);
324    let (mut nodes, errors) = parser.parse_recoverable(tokens).split_vec();
325    let errors = errors.into_iter().map(|e| e.emit_as_expr_tokens());
326    let nodes_output = view::render_view(
327        &mut nodes,
328        global_class.as_ref(),
329        normalized_call_site(proc_macro::Span::call_site()),
330        template,
331    );
332
333    // The allow lint needs to be put here instead of at the expansion of
334    // view::attribute_value(). Adding this next to the expanded expression
335    // seems to break rust-analyzer, but it works when the allow is put here.
336    let output = quote! {
337        {
338            #[allow(unused_braces)]
339            {
340                #(#errors;)*
341                #nodes_output
342            }
343        }
344    };
345
346    if template {
347        quote! {
348            ::leptos::prelude::ViewTemplate::new(#output)
349        }
350    } else {
351        output
352    }
353    .into()
354}
355
356fn normalized_call_site(site: proc_macro::Span) -> Option<String> {
357    cfg_if::cfg_if! {
358        if #[cfg(all(debug_assertions, feature = "nightly"))] {
359            Some(leptos_hot_reload::span_to_stable_id(
360                site.file(),
361                site.start().line()
362            ))
363        } else {
364            _ = site;
365            None
366        }
367    }
368}
369
370/// This behaves like the [`view`](view!) macro, but loads the view from an external file instead of
371/// parsing it inline.
372///
373/// This is designed to allow editing views in a separate file, if this improves a user's workflow.
374///
375/// The file is loaded and parsed during proc-macro execution, and its path is resolved relative to
376/// the crate root rather than relative to the file from which it is called.
377#[proc_macro_error2::proc_macro_error]
378#[proc_macro]
379pub fn include_view(tokens: TokenStream) -> TokenStream {
380    let file_name = syn::parse::<syn::LitStr>(tokens).unwrap_or_else(|_| {
381        abort!(
382            Span::call_site(),
383            "the only supported argument is a string literal"
384        );
385    });
386    let file =
387        std::fs::read_to_string(file_name.value()).unwrap_or_else(|_| {
388            abort!(Span::call_site(), "could not open file");
389        });
390    let tokens = proc_macro2::TokenStream::from_str(&file)
391        .unwrap_or_else(|e| abort!(Span::call_site(), e));
392    view(tokens.into())
393}
394
395/// Annotates a function so that it can be used with your template as a Leptos `<Component/>`.
396///
397/// The `#[component]` macro allows you to annotate plain Rust functions as components
398/// and use them within your Leptos [view](crate::view!) as if they were custom HTML elements. The
399/// component function takes any number of other arguments. When you use the component somewhere else,
400/// the names of its arguments are the names of the properties you use in the [view](crate::view!) macro.
401///
402/// Every component function should have the return type `-> impl IntoView`.
403///
404/// You can add Rust doc comments to component function arguments and the macro will use them to
405/// generate documentation for the component.
406///
407/// Here’s how you would define and use a simple Leptos component which can accept custom properties for a name and age:
408/// ```rust
409/// # use leptos::prelude::*;
410/// use std::time::Duration;
411///
412/// #[component]
413/// fn HelloComponent(
414///     /// The user's name.
415///     name: String,
416///     /// The user's age.
417///     age: u8,
418/// ) -> impl IntoView {
419///     // create the signals (reactive values) that will update the UI
420///     let (age, set_age) = create_signal(age);
421///     // increase `age` by 1 every second
422///     set_interval(
423///         move || set_age.update(|age| *age += 1),
424///         Duration::from_secs(1),
425///     );
426///
427///     // return the user interface, which will be automatically updated
428///     // when signal values change
429///     view! {
430///       <p>"Your name is " {name} " and you are " {move || age.get()} " years old."</p>
431///     }
432/// }
433///
434/// #[component]
435/// fn App() -> impl IntoView {
436///     view! {
437///       <main>
438///         <HelloComponent name="Greg".to_string() age=32/>
439///       </main>
440///     }
441/// }
442/// ```
443///
444/// Here are some important details about how Leptos components work within the framework:
445/// * **The component function only runs once.** Your component function is not a “render” function
446///    that re-runs whenever changes happen in the state. It’s a “setup” function that runs once to
447///    create the user interface, and sets up a reactive system to update it. This means it’s okay
448///    to do relatively expensive work within the component function, as it will only happen once,
449///    not on every state change.
450///
451/// * Component names are usually in `PascalCase`. If you use a `snake_case` name, then the generated
452///    component's name will still be in `PascalCase`. This is how the framework recognizes that
453///    a particular tag is a component, not an HTML element.
454///
455/// ```
456/// # use leptos::prelude::*;
457///
458/// // PascalCase: Generated component will be called MyComponent
459/// #[component]
460/// fn MyComponent() -> impl IntoView {}
461///
462/// // snake_case: Generated component will be called MySnakeCaseComponent
463/// #[component]
464/// fn my_snake_case_component() -> impl IntoView {}
465/// ```
466///
467/// 5. You can access the children passed into the component with the `children` property, which takes
468///    an argument of the type `Children`. This is an alias for `Box<dyn FnOnce() -> AnyView<_>>`.
469///    If you need `children` to be a `Fn` or `FnMut`, you can use the `ChildrenFn` or `ChildrenFnMut`
470///    type aliases. If you want to iterate over the children, you can take `ChildrenFragment`.
471///
472/// ```
473/// # use leptos::prelude::*;
474/// #[component]
475/// fn ComponentWithChildren(children: ChildrenFragment) -> impl IntoView {
476///     view! {
477///       <ul>
478///         {children()
479///           .nodes
480///           .into_iter()
481///           .map(|child| view! { <li>{child}</li> })
482///           .collect::<Vec<_>>()}
483///       </ul>
484///     }
485/// }
486///
487/// #[component]
488/// fn WrapSomeChildren() -> impl IntoView {
489///     view! {
490///       <ComponentWithChildren>
491///         "Ooh, look at us!"
492///         <span>"We're being projected!"</span>
493///       </ComponentWithChildren>
494///     }
495/// }
496/// ```
497///
498/// ## Customizing Properties
499/// You can use the `#[prop]` attribute on individual component properties (function arguments) to
500/// customize the types that component property can receive. You can use the following attributes:
501/// * `#[prop(into)]`: This will call `.into()` on any value passed into the component prop. (For example,
502///   you could apply `#[prop(into)]` to a prop that takes
503///   [Signal](https://docs.rs/leptos/latest/leptos/struct.Signal.html), which would
504///   allow users to pass a [ReadSignal](https://docs.rs/leptos/latest/leptos/struct.ReadSignal.html) or
505///   [RwSignal](https://docs.rs/leptos/latest/leptos/struct.RwSignal.html)
506///   and automatically convert it.)
507/// * `#[prop(optional)]`: If the user does not specify this property when they use the component,
508///   it will be set to its default value. If the property type is `Option<T>`, values should be passed
509///   as `name=T` and will be received as `Some(T)`.
510/// * `#[prop(optional_no_strip)]`: The same as `optional`, but requires values to be passed as `None` or
511///   `Some(T)` explicitly. This means that the optional property can be omitted (and be `None`), or explicitly
512///   specified as either `None` or `Some(T)`.
513/// ```rust
514/// # use leptos::prelude::*;
515///
516/// #[component]
517/// pub fn MyComponent(
518///     #[prop(into)] name: String,
519///     #[prop(optional)] optional_value: Option<i32>,
520///     #[prop(optional_no_strip)] optional_no_strip: Option<i32>,
521/// ) -> impl IntoView {
522///     // whatever UI you need
523/// }
524///
525/// #[component]
526/// pub fn App() -> impl IntoView {
527///     view! {
528///       <MyComponent
529///         name="Greg" // automatically converted to String with `.into()`
530///         optional_value=42 // received as `Some(42)`
531///         optional_no_strip=Some(42) // received as `Some(42)`
532///       />
533///       <MyComponent
534///         name="Bob" // automatically converted to String with `.into()`
535///         // optional values can both be omitted, and received as `None`
536///       />
537///     }
538/// }
539/// ```
540#[proc_macro_error2::proc_macro_error]
541#[proc_macro_attribute]
542pub fn component(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
543    let is_transparent = if !args.is_empty() {
544        let transparent = parse_macro_input!(args as syn::Ident);
545
546        if transparent != "transparent" {
547            abort!(
548                transparent,
549                "only `transparent` is supported";
550                help = "try `#[component(transparent)]` or `#[component]`"
551            );
552        }
553
554        true
555    } else {
556        false
557    };
558
559    component_macro(s, is_transparent, None)
560}
561
562/// Defines a component as an interactive island when you are using the
563/// `islands` feature of Leptos. Apart from the macro name,
564/// the API is the same as the [`component`](macro@component) macro.
565///
566/// When you activate the `islands` feature, every `#[component]`
567/// is server-only by default. This "default to server" behavior is important:
568/// you opt into shipping code to the client, rather than opting out. You can
569/// opt into client-side interactivity for any given component by changing from
570///  `#[component]` to `#[island]`—the two macros are otherwise identical.
571///
572/// Everything that is included inside an island will be compiled to WASM and
573/// shipped to the browser. So the key to really benefiting from this architecture
574/// is to make islands as small as possible, and include only the minimal
575/// required amount of functionality in islands themselves.
576///
577/// Only code included in an island itself is compiled to WASM. This means:
578/// 1. `children` can be provided from a server `#[component]` to an `#[island]`
579/// without the island needing to be able to hydrate them.
580/// 2. Props can be passed from the server to an island.
581///
582/// ## Present Limitations
583/// A few noteworthy limitations, at the moment:
584/// 1. `children` are completely opaque in islands. You can't iterate over `children`;
585/// in fact they're all bundled into a single `<leptos-children>` HTML element.
586/// 2. Similarly, `children` need to be used in the HTML rendered on the server.
587/// If they need to be displayed conditionally, they should be included in the HTML
588/// and rendered or not using `display: none` rather than `<Show>` or ordinary control flow.
589/// This is because the children aren't serialized at all, other than as HTML: if that
590/// HTML isn't present in the DOM, even if hidden, it is never sent and not available
591/// to the client at all.
592///
593/// ## Example
594/// ```rust,ignore
595/// use leptos::prelude::*;
596///
597/// #[component]
598/// pub fn App() -> impl IntoView {
599///     // this would panic if it ran in the browser
600///     // but because this isn't an island, it only runs on the server
601///     let file =
602///         std::fs::read_to_string("./src/is_this_a_server_component.txt")
603///             .unwrap();
604///     let len = file.len();
605///
606///     view! {
607///         <p>"The starting value for the button is the file's length."</p>
608///         // `value` is serialized and given to the island as a prop
609///         <Island value=len>
610///             // `file` is only available on the server
611///             // island props are projected in, so we can nest
612///             // server-only content inside islands inside server content etc.
613///             <p>{file}</p>
614///         </Island>
615///     }
616/// }
617///
618/// #[island]
619/// pub fn Island(
620///     #[prop(into)] value: RwSignal<usize>,
621///     children: Children,
622/// ) -> impl IntoView {
623///     // because `RwSignal<T>` implements `From<T>`, we can pass in a plain
624///     // value and use it as the starting value of a signal here
625///     view! {
626///         <button on:click=move |_| value.update(|n| *n += 1)>
627///             {value}
628///         </button>
629///         {children()}
630///     }
631/// }
632/// ```
633#[proc_macro_error2::proc_macro_error]
634#[proc_macro_attribute]
635pub fn island(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
636    let is_transparent = if !args.is_empty() {
637        let transparent = parse_macro_input!(args as syn::Ident);
638
639        if transparent != "transparent" {
640            abort!(
641                transparent,
642                "only `transparent` is supported";
643                help = "try `#[island(transparent)]` or `#[island]`"
644            );
645        }
646
647        true
648    } else {
649        false
650    };
651
652    let island_src = s.to_string();
653    component_macro(s, is_transparent, Some(island_src))
654}
655
656fn component_macro(
657    s: TokenStream,
658    is_transparent: bool,
659    island: Option<String>,
660) -> TokenStream {
661    let mut dummy = syn::parse::<DummyModel>(s.clone());
662    let parse_result = syn::parse::<component::Model>(s);
663
664    if let (Ok(ref mut unexpanded), Ok(model)) = (&mut dummy, parse_result) {
665        let expanded = model.is_transparent(is_transparent).with_island(island).into_token_stream();
666        if !matches!(unexpanded.vis, Visibility::Public(_)) {
667            unexpanded.vis = Visibility::Public(Pub {
668                span: unexpanded.vis.span(),
669            })
670        }
671        unexpanded.sig.ident =
672            unmodified_fn_name_from_fn_name(&unexpanded.sig.ident);
673        quote! {
674            #expanded
675
676            #[doc(hidden)]
677            #[allow(non_snake_case, dead_code, clippy::too_many_arguments, clippy::needless_lifetimes)]
678            #unexpanded
679        }
680    } else {
681        match dummy {
682            Ok(mut dummy) => {
683                dummy.sig.ident = unmodified_fn_name_from_fn_name(&dummy.sig.ident);
684                quote! {
685                    #[doc(hidden)]
686                    #[allow(non_snake_case, dead_code, clippy::too_many_arguments, clippy::needless_lifetimes)]
687                    #dummy
688                }
689            }
690            Err(e) => {
691                proc_macro_error2::abort!(e.span(), e);
692            }
693        }
694    }.into()
695}
696
697/// Annotates a struct so that it can be used with your Component as a `slot`.
698///
699/// The `#[slot]` macro allows you to annotate plain Rust struct as component slots and use them
700/// within your Leptos [`component`](macro@crate::component) properties. The struct can contain any number
701/// of fields. When you use the component somewhere else, the names of the slot fields are the
702/// names of the properties you use in the [view](crate::view!) macro.
703///
704/// Here’s how you would define and use a simple Leptos component which can accept a custom slot:
705/// ```rust
706/// # use leptos::prelude::*;
707/// use std::time::Duration;
708///
709/// #[slot]
710/// struct HelloSlot {
711///     // Same prop syntax as components.
712///     #[prop(optional)]
713///     children: Option<Children>,
714/// }
715///
716/// #[component]
717/// fn HelloComponent(
718///     /// Component slot, should be passed through the <HelloSlot slot> syntax.
719///     hello_slot: HelloSlot,
720/// ) -> impl IntoView {
721///     hello_slot.children.map(|children| children())
722/// }
723///
724/// #[component]
725/// fn App() -> impl IntoView {
726///     view! {
727///         <HelloComponent>
728///             <HelloSlot slot>
729///                 "Hello, World!"
730///             </HelloSlot>
731///         </HelloComponent>
732///     }
733/// }
734/// ```
735///
736/// /// Here are some important details about how slots work within the framework:
737/// 1. Most of the same rules from [`macro@component`] macro should also be followed on slots.
738///
739/// 2. Specifying only `slot` without a name (such as in `<HelloSlot slot>`) will default the chosen slot to
740/// the a snake case version of the slot struct name (`hello_slot` for `<HelloSlot>`).
741///
742/// 3. Event handlers cannot be specified directly on the slot.
743///
744/// ```compile_error
745/// // ❌ This won't work
746/// # use leptos::prelude::*;
747///
748/// #[slot]
749/// struct SlotWithChildren {
750///     children: Children,
751/// }
752///
753/// #[component]
754/// fn ComponentWithSlot(slot: SlotWithChildren) -> impl IntoView {
755///     (slot.children)()
756/// }
757///
758/// #[component]
759/// fn App() -> impl IntoView {
760///     view! {
761///         <ComponentWithSlot>
762///           <SlotWithChildren slot:slot on:click=move |_| {}>
763///             <h1>"Hello, World!"</h1>
764///           </SlotWithChildren>
765///         </ComponentWithSlot>
766///     }
767/// }
768/// ```
769///
770/// ```
771/// // ✅ Do this instead
772/// # use leptos::prelude::*;
773///
774/// #[slot]
775/// struct SlotWithChildren {
776///     children: Children,
777/// }
778///
779/// #[component]
780/// fn ComponentWithSlot(slot: SlotWithChildren) -> impl IntoView {
781///     (slot.children)()
782/// }
783///
784/// #[component]
785/// fn App() -> impl IntoView {
786///     view! {
787///         <ComponentWithSlot>
788///           <SlotWithChildren slot:slot>
789///             <div on:click=move |_| {}>
790///               <h1>"Hello, World!"</h1>
791///             </div>
792///           </SlotWithChildren>
793///         </ComponentWithSlot>
794///     }
795/// }
796/// ```
797#[proc_macro_error2::proc_macro_error]
798#[proc_macro_attribute]
799pub fn slot(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
800    if !args.is_empty() {
801        abort!(
802            Span::call_site(),
803            "no arguments are supported";
804            help = "try just `#[slot]`"
805        );
806    }
807
808    parse_macro_input!(s as slot::Model)
809        .into_token_stream()
810        .into()
811}
812
813/// Declares that a function is a [server function](https://docs.rs/server_fn/latest/server_fn/index.html).
814/// This means that its body will only run on the server, i.e., when the `ssr` feature on this crate is enabled.
815///
816/// If you call a server function from the client (i.e., when the `csr` or `hydrate` features
817/// are enabled), it will instead make a network request to the server.
818///
819/// ## Named Arguments
820///
821/// You can provide any combination of the following named arguments:
822/// - `name`: sets the identifier for the server function’s type, which is a struct created
823///    to hold the arguments (defaults to the function identifier in PascalCase)
824/// - `prefix`: a prefix at which the server function handler will be mounted (defaults to `/api`)
825///    your prefix must begin with `/`. Otherwise your function won't be found.
826/// - `endpoint`: specifies the exact path at which the server function handler will be mounted,
827///   relative to the prefix (defaults to the function name followed by unique hash)
828/// - `input`: the encoding for the arguments (defaults to `PostUrl`)
829/// - `output`: the encoding for the response (defaults to `Json`)
830/// - `client`: a custom `Client` implementation that will be used for this server fn
831/// - `encoding`: (legacy, may be deprecated in future) specifies the encoding, which may be one
832///   of the following (not case sensitive)
833///     - `"Url"`: `POST` request with URL-encoded arguments and JSON response
834///     - `"GetUrl"`: `GET` request with URL-encoded arguments and JSON response
835///     - `"Cbor"`: `POST` request with CBOR-encoded arguments and response
836///     - `"GetCbor"`: `GET` request with URL-encoded arguments and CBOR response
837/// - `req` and `res` specify the HTTP request and response types to be used on the server (these
838///   should usually only be necessary if you are integrating with a server other than Actix/Axum)
839/// - `impl_from`: specifies whether to implement trait `From` for server function's type or not.
840///   By default, if a server function only has one argument, the macro automatically implements the `From` trait
841///   to convert from the argument type to the server function type, and vice versa, allowing you to convert
842///   between them easily. Setting `impl_from` to `false` disables this, which can be necessary for argument types
843///   for which this would create a conflicting implementation. (defaults to `true`)
844///
845/// ```rust,ignore
846/// #[server(
847///   name = SomeStructName,
848///   prefix = "/my_api",
849///   endpoint = "my_fn",
850///   input = Cbor,
851///   output = Json
852///   impl_from = true
853/// )]
854/// pub async fn my_wacky_server_fn(input: Vec<String>) -> Result<usize, ServerFnError> {
855///   todo!()
856/// }
857/// ```
858///
859/// ## Server Function Encodings
860///
861/// Server functions are designed to allow a flexible combination of `input` and `output` encodings, the set
862/// of which can be found in the [`server_fn::codec`](../server_fn/codec/index.html) module.
863///
864/// The serialization/deserialization process for server functions consists of a series of steps,
865/// each of which is represented by a different trait:
866/// 1. [`IntoReq`](../server_fn/codec/trait.IntoReq.html): The client serializes the [`ServerFn`](../server_fn/trait.ServerFn.html) argument type into an HTTP request.
867/// 2. The [`Client`](../server_fn/client/trait.Client.html) sends the request to the server.
868/// 3. [`FromReq`](../server_fn/codec/trait.FromReq.html): The server deserializes the HTTP request back into the [`ServerFn`](../server_fn/client/trait.Client.html) type.
869/// 4. The server calls calls [`ServerFn::run_body`](../server_fn/trait.ServerFn.html#tymethod.run_body) on the data.
870/// 5. [`IntoRes`](../server_fn/codec/trait.IntoRes.html): The server serializes the [`ServerFn::Output`](../server_fn/trait.ServerFn.html#associatedtype.Output) type into an HTTP response.
871/// 6. The server integration applies any middleware from [`ServerFn::middleware`](../server_fn/middleware/index.html) and responds to the request.
872/// 7. [`FromRes`](../server_fn/codec/trait.FromRes.html): The client deserializes the response back into the [`ServerFn::Output`](../server_fn/trait.ServerFn.html#associatedtype.Output) type.
873///
874/// Whatever encoding is provided to `input` should implement `IntoReq` and `FromReq`. Whatever encoding is provided
875/// to `output` should implement `IntoRes` and `FromRes`.
876///
877/// ## Default Values for Parameters
878///
879/// Individual function parameters can be annotated with `#[server(default)]`, which will pass
880/// through `#[serde(default)]`. This is useful for the empty values of arguments with some
881/// encodings. The URL encoding, for example, omits a field entirely if it is an empty `Vec<_>`,
882/// but this causes a deserialization error: the correct solution is to add `#[server(default)]`.
883/// ```rust,ignore
884/// pub async fn with_default_value(#[server(default)] values: Vec<u32>) /* etc. */
885/// ```
886///
887/// ## Important Notes
888/// - **Server functions must be `async`.** Even if the work being done inside the function body
889///   can run synchronously on the server, from the client’s perspective it involves an asynchronous
890///   function call.
891/// - **Server functions must return `Result<T, ServerFnError>`.** Even if the work being done
892///   inside the function body can’t fail, the processes of serialization/deserialization and the
893///   network call are fallible.
894///     - [`ServerFnError`](../server_fn/error/enum.ServerFnError.html) can be generic over some custom error type. If so, that type should implement
895///       [`FromStr`](std::str::FromStr) and [`Display`](std::fmt::Display), but does not need to implement [`Error`](std::error::Error). This is so the value
896///       can be easily serialized and deserialized along with the result.
897/// - **Server functions are part of the public API of your application.** A server function is an
898///   ad hoc HTTP API endpoint, not a magic formula. Any server function can be accessed by any HTTP
899///   client. You should take care to sanitize any data being returned from the function to ensure it
900///   does not leak data that should exist only on the server.
901/// - **Server functions can’t be generic.** Because each server function creates a separate API endpoint,
902///   it is difficult to monomorphize. As a result, server functions cannot be generic (for now?) If you need to use
903///   a generic function, you can define a generic inner function called by multiple concrete server functions.
904/// - **Arguments and return types must be serializable.** We support a variety of different encodings,
905///   but one way or another arguments need to be serialized to be sent to the server and deserialized
906///   on the server, and the return type must be serialized on the server and deserialized on the client.
907///   This means that the set of valid server function argument and return types is a subset of all
908///   possible Rust argument and return types. (i.e., server functions are strictly more limited than
909///   ordinary functions.)
910/// - **Context comes from the server.** Server functions are provided access to the HTTP request and other relevant
911///   server data via the server integrations, but they do *not* have access to reactive state that exists in the client.
912/// - Your server must be ready to handle the server functions at the API prefix you list. The easiest way to do this
913///   is to use the `handle_server_fns` function from [`leptos_actix`](https://docs.rs/leptos_actix/latest/leptos_actix/fn.handle_server_fns.html)
914///   or [`leptos_axum`](https://docs.rs/leptos_axum/latest/leptos_axum/fn.handle_server_fns.html).
915/// - **Server functions must have unique paths**. Unique paths are automatically generated for each
916///   server function. If you choose to specify a path in the fourth argument, you must ensure that these
917///   are unique. You cannot define two server functions with the same URL prefix and endpoint path,
918///   even if they have different URL encodings, e.g. a POST method at `/api/foo` and a GET method at `/api/foo`.
919#[proc_macro_attribute]
920#[proc_macro_error]
921pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
922    match server_fn_macro::server_macro_impl(
923        args.into(),
924        s.into(),
925        Some(syn::parse_quote!(::leptos::server_fn)),
926        "/api",
927        None,
928        None,
929    ) {
930        Err(e) => e.to_compile_error().into(),
931        Ok(s) => s.to_token_stream().into(),
932    }
933}
934
935/// Derives a trait that parses a map of string keys and values into a typed
936/// data structure, e.g., for route params.
937#[proc_macro_derive(Params)]
938pub fn params_derive(
939    input: proc_macro::TokenStream,
940) -> proc_macro::TokenStream {
941    match syn::parse(input) {
942        Ok(ast) => params::params_impl(&ast),
943        Err(err) => err.to_compile_error().into(),
944    }
945}
946
947/// Generates a `slice` into a struct with a default getter and setter.
948///
949/// Can be used to access deeply nested fields within a global state object.
950///
951/// ```rust
952/// # use leptos::prelude::*;
953/// # use leptos_macro::slice;
954///
955/// #[derive(Default)]
956/// pub struct Outer {
957///     count: i32,
958///     inner: Inner,
959/// }
960///
961/// #[derive(Default)]
962/// pub struct Inner {
963///     inner_count: i32,
964///     inner_name: String,
965/// }
966///
967/// let outer_signal = RwSignal::new(Outer::default());
968///
969/// let (count, set_count) = slice!(outer_signal.count);
970///
971/// let (inner_count, set_inner_count) = slice!(outer_signal.inner.inner_count);
972/// let (inner_name, set_inner_name) = slice!(outer_signal.inner.inner_name);
973/// ```
974#[proc_macro]
975pub fn slice(input: TokenStream) -> TokenStream {
976    slice::slice_impl(input)
977}
978
979/// Generates a `memo` into a struct with a default getter.
980///
981/// Can be used to access deeply nested fields within a global state object.
982///
983/// ```rust
984/// # use leptos::prelude::*;
985/// # use leptos_macro::memo;
986///
987/// #[derive(Default)]
988/// pub struct Outer {
989///     count: i32,
990///     inner: Inner,
991/// }
992///
993/// #[derive(Default)]
994/// pub struct Inner {
995///     inner_count: i32,
996///     inner_name: String,
997/// }
998///
999/// let outer_signal = RwSignal::new(Outer::default());
1000///
1001/// let count = memo!(outer_signal.count);
1002///
1003/// let inner_count = memo!(outer_signal.inner.inner_count);
1004/// let inner_name = memo!(outer_signal.inner.inner_name);
1005/// ```
1006#[proc_macro]
1007pub fn memo(input: TokenStream) -> TokenStream {
1008    memo::memo_impl(input)
1009}
1010
1011/// The `#[lazy]` macro marks an `async` function as a function that can be lazy-loaded from a
1012/// separate (WebAssembly) binary.
1013///
1014/// The first time the function is called, calling the function will first load that other binary,
1015/// then call the function. On subsequent call it will be called immediately, but still return
1016/// asynchronously to maintain the same API.
1017///
1018/// All parameters and output types should be concrete types, with no generics.
1019#[proc_macro_attribute]
1020#[proc_macro_error]
1021pub fn lazy(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
1022    lazy::lazy_impl(args, s)
1023}