Merge branch 'main' into typed-event-handlers

This commit is contained in:
Greg Johnston 2022-11-23 19:03:52 -05:00 committed by GitHub
commit 19d7b8434b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 10 deletions

View File

@ -70,11 +70,12 @@ features = [
] ]
[dev-dependencies] [dev-dependencies]
leptos = { path = "../leptos", default-features = false, version = "0.0" }
leptos_macro = { path = "../leptos_macro", default-features = false, version = "0.0" } leptos_macro = { path = "../leptos_macro", default-features = false, version = "0.0" }
[features] [features]
csr = ["leptos_reactive/csr", "leptos_macro/csr"] csr = ["leptos_reactive/csr", "leptos_macro/csr", "leptos/csr"]
hydrate = ["leptos_reactive/hydrate", "leptos_macro/hydrate"] hydrate = ["leptos_reactive/hydrate", "leptos_macro/hydrate", "leptos/hydrate"]
ssr = ["leptos_reactive/ssr", "leptos_macro/ssr"] ssr = ["leptos_reactive/ssr", "leptos_macro/ssr", "leptos/ssr"]
stable = ["leptos_reactive/stable", "leptos_macro/stable"] stable = ["leptos_reactive/stable", "leptos_macro/stable", "leptos/stable"]

View File

@ -140,3 +140,15 @@ macro_rules! is_dev {
cfg!(debug_assertions) cfg!(debug_assertions)
}; };
} }
#[doc(hidden)]
pub fn __leptos_renderer_error(expected: &'static str, location: &'static str) -> web_sys::Node {
cfg_if! {
if #[cfg(debug_assertions)] {
panic!("Yikes! Something went wrong while Leptos was trying to traverse the DOM to set up the reactive system.\n\nThe renderer expected {expected:?} as {location} and couldn't get it.\n\nThis is almost certainly a bug in the framework, not your application. Please open an issue on GitHub and provide example code if possible.\n\nIn the meantime, these bugs are often related to <Component/>s or {{block}}s when they are siblings of each other. Try wrapping those in a <span> or <div> for now. Sorry for the pain!")
} else {
_ = expected;
panic!("Renderer error. You can find a more detailed error message if you compile in debug mode.")
}
}
}

View File

@ -244,6 +244,60 @@ pub fn view(tokens: TokenStream) -> TokenStream {
} }
} }
/// Annotates a function so that it can be used with your template as a <Component/>
///
/// Here are some things you should know.
/// 1. The component name should be `CamelCase` instead of `snake_case`. This is how the renderer
/// recognizes that a particular tag is a component, not an HTML element.
///
/// ```
/// # use leptos::*;
/// // ❌ not snake_case
/// #[component]
/// fn my_component(cx: Scope) -> Element { todo!() }
///
/// // ✅ CamelCase
/// #[component]
/// fn MyComponent(cx: Scope) -> Element { todo!() }
/// ```
///
/// 2. The macro generates a type `ComponentProps` for every `Component` (so, `HomePage` generates `HomePageProps`,
/// `Button` generates `ButtonProps`, etc.) When youre importing the component, you also need to **explicitly import
/// the prop type.**
///
/// ```
/// # use leptos::*;
///
/// use component::{MyComponent, MyComponentProps};
///
/// mod component {
/// use leptos::*;
///
/// #[component]
/// pub fn MyComponent(cx: Scope) -> Element { todo!() }
/// }
/// ```
///
/// 3. You can pass generic arguments, but they should be defined in a `where` clause and not inline.
///
/// ```compile_error
/// # use leptos::*;
/// #[component]
/// fn MyComponent<T: Fn() -> Element>(cx: Scope, render_prop: T) -> Element {
/// todo!()
/// }
/// ```
///
/// ```
/// # use leptos::*;
/// #[component]
/// fn MyComponent<T>(cx: Scope, render_prop: T) -> Element
/// where
/// T: Fn() -> Element,
/// {
/// todo!()
/// }
/// ```
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn component(_args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { pub fn component(_args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
match syn::parse::<component::InlinePropsBody>(s) { match syn::parse::<component::InlinePropsBody>(s) {

View File

@ -383,14 +383,14 @@ fn element_to_tokens(
quote_spanned! { quote_spanned! {
span => let #this_el_ident = #debug_name; span => let #this_el_ident = #debug_name;
//log::debug!("next_sibling ({})", #debug_name); //log::debug!("next_sibling ({})", #debug_name);
let #this_el_ident = #prev_sib.next_sibling().unwrap_throw(); let #this_el_ident = #prev_sib.next_sibling().unwrap_or_else(|| ::leptos::__leptos_renderer_error(#debug_name, "nextSibling"));
//log::debug!("=> got {}", #this_el_ident.node_name()); //log::debug!("=> got {}", #this_el_ident.node_name());
} }
} else { } else {
quote_spanned! { quote_spanned! {
span => let #this_el_ident = #debug_name; span => let #this_el_ident = #debug_name;
//log::debug!("first_child ({})", #debug_name); //log::debug!("first_child ({})", #debug_name);
let #this_el_ident = #parent.first_child().unwrap_throw(); let #this_el_ident = #parent.first_child().unwrap_or_else(|| ::leptos::__leptos_renderer_error(#debug_name, "firstChild"));
//log::debug!("=> got {}", #this_el_ident.node_name()); //log::debug!("=> got {}", #this_el_ident.node_name());
} }
}; };
@ -816,13 +816,13 @@ fn block_to_tokens(
let location = if let Some(sibling) = &prev_sib { let location = if let Some(sibling) = &prev_sib {
quote_spanned! { quote_spanned! {
span => //log::debug!("-> next sibling"); span => //log::debug!("-> next sibling");
let #name = #sibling.next_sibling().unwrap_throw(); let #name = #sibling.next_sibling().unwrap_or_else(|| ::leptos::__leptos_renderer_error("{block}", "nextSibling"));
//log::debug!("\tnext sibling = {}", #name.node_name()); //log::debug!("\tnext sibling = {}", #name.node_name());
} }
} else { } else {
quote_spanned! { quote_spanned! {
span => //log::debug!("\\|/ first child on {}", #parent.node_name()); span => //log::debug!("\\|/ first child on {}", #parent.node_name());
let #name = #parent.first_child().unwrap_throw(); let #name = #parent.first_child().unwrap_or_else(|| ::leptos::__leptos_renderer_error("{block}", "firstChild"));
//log::debug!("\tfirst child = {}", #name.node_name()); //log::debug!("\tfirst child = {}", #name.node_name());
} }
}; };
@ -938,6 +938,8 @@ fn component_to_tokens(
mode: Mode, mode: Mode,
is_first_child: bool, is_first_child: bool,
) -> PrevSibChange { ) -> PrevSibChange {
let component_name = ident_from_tag_name(&node.name);
let component_name = format!("<{component_name}/>");
let create_component = create_component(cx, node, mode); let create_component = create_component(cx, node, mode);
let span = node.name.span(); let span = node.name.span();
@ -974,13 +976,13 @@ fn component_to_tokens(
let starts_at = if let Some(prev_sib) = prev_sib { let starts_at = if let Some(prev_sib) = prev_sib {
quote::quote! {{ quote::quote! {{
//log::debug!("starts_at = next_sibling"); //log::debug!("starts_at = next_sibling");
#prev_sib.next_sibling().unwrap_throw() #prev_sib.next_sibling().unwrap_or_else(|| ::leptos::__leptos_renderer_error(#component_name, "nextSibling"))
//log::debug!("ok starts_at"); //log::debug!("ok starts_at");
}} }}
} else { } else {
quote::quote! {{ quote::quote! {{
//log::debug!("starts_at first_child"); //log::debug!("starts_at first_child");
#parent.first_child().unwrap_throw() #parent.first_child().unwrap_or_else(|| ::leptos::__leptos_renderer_error(#component_name, "firstChild"))
//log::debug!("starts_at ok"); //log::debug!("starts_at ok");
}} }}
}; };