mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 23:04:24 +00:00
Merge pull request #354 from jclmnop/feat/allow-snake-case-components
Allow snake case components
This commit is contained in:
commit
fd6e63796e
5 changed files with 70 additions and 7 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,3 +6,4 @@ blob.rs
|
|||
Cargo.lock
|
||||
**/*.rs.bk
|
||||
.DS_Store
|
||||
.idea
|
||||
|
|
|
@ -55,6 +55,40 @@ fn ssr_test_with_components() {
|
|||
});
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "csr", feature = "hydrate")))]
|
||||
#[test]
|
||||
fn ssr_test_with_snake_case_components() {
|
||||
use leptos::*;
|
||||
|
||||
#[component]
|
||||
fn snake_case_counter(cx: Scope, initial_value: i32) -> impl IntoView {
|
||||
let (value, set_value) = create_signal(cx, initial_value);
|
||||
view! {
|
||||
cx,
|
||||
<div>
|
||||
<button on:click=move |_| set_value.update(|value| *value -= 1)>"-1"</button>
|
||||
<span>"Value: " {move || value.get().to_string()} "!"</span>
|
||||
<button on:click=move |_| set_value.update(|value| *value += 1)>"+1"</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
_ = create_scope(create_runtime(), |cx| {
|
||||
let rendered = view! {
|
||||
cx,
|
||||
<div class="counters">
|
||||
<SnakeCaseCounter initial_value=1/>
|
||||
<SnakeCaseCounter initial_value=2/>
|
||||
</div>
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
rendered.into_view(cx).render_to_string(cx),
|
||||
"<div id=\"_0-1\" class=\"counters\"><!--hk=_0-1-0o|leptos-snake-case-counter-start--><div id=\"_0-1-1\"><button id=\"_0-1-2\">-1</button><span id=\"_0-1-3\">Value: <!--hk=_0-1-4o|leptos-dyn-child-start-->1<!--hk=_0-1-4c|leptos-dyn-child-end-->!</span><button id=\"_0-1-5\">+1</button></div><!--hk=_0-1-0c|leptos-snake-case-counter-end--><!--hk=_0-1-5-0o|leptos-snake-case-counter-start--><div id=\"_0-1-5-1\"><button id=\"_0-1-5-2\">-1</button><span id=\"_0-1-5-3\">Value: <!--hk=_0-1-5-4o|leptos-dyn-child-start-->2<!--hk=_0-1-5-4c|leptos-dyn-child-end-->!</span><button id=\"_0-1-5-5\">+1</button></div><!--hk=_0-1-5-0c|leptos-snake-case-counter-end--></div>"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "csr", feature = "hydrate")))]
|
||||
#[test]
|
||||
fn test_classes() {
|
||||
|
|
|
@ -27,6 +27,7 @@ leptos_dom = { workspace = true }
|
|||
leptos_reactive = { workspace = true }
|
||||
leptos_server = { workspace = true }
|
||||
lazy_static = "1.4"
|
||||
convert_case = "0.6.0"
|
||||
|
||||
[dev-dependencies]
|
||||
log = "0.4"
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
use convert_case::{
|
||||
Case::{Pascal, Snake},
|
||||
Casing,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, ToTokens, TokenStreamExt};
|
||||
|
@ -75,7 +79,7 @@ impl Parse for Model {
|
|||
is_transparent: false,
|
||||
docs,
|
||||
vis: item.vis.clone(),
|
||||
name: item.sig.ident.clone(),
|
||||
name: convert_from_snake_case(&item.sig.ident),
|
||||
scope_name,
|
||||
props,
|
||||
ret: item.sig.output.clone(),
|
||||
|
@ -97,6 +101,15 @@ fn drain_filter<T>(vec: &mut Vec<T>, mut some_predicate: impl FnMut(&mut T) -> b
|
|||
}
|
||||
}
|
||||
|
||||
fn convert_from_snake_case(name: &Ident) -> Ident {
|
||||
let name_str = name.to_string();
|
||||
if !name_str.is_case(Snake) {
|
||||
name.clone()
|
||||
} else {
|
||||
Ident::new(&*name_str.to_case(Pascal), name.span().clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Model {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
|
|
|
@ -373,16 +373,18 @@ pub fn view(tokens: TokenStream) -> TokenStream {
|
|||
/// to do relatively expensive work within the component function, as it will only happen once,
|
||||
/// not on every state change.
|
||||
///
|
||||
/// 2. 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.
|
||||
/// 2. If a `snake_case` name is used, then the generated component's name will still be in
|
||||
/// `CamelCase`. This is how the renderer recognizes that a particular tag is a component, not
|
||||
/// an HTML element. It's important to be aware of this when using or importing the component.
|
||||
///
|
||||
/// ```
|
||||
/// # use leptos::*;
|
||||
/// // ❌ not snake_case
|
||||
/// #[component]
|
||||
/// fn my_component(cx: Scope) -> impl IntoView { todo!() }
|
||||
///
|
||||
/// // ✅ CamelCase
|
||||
/// // snake_case: Generated component will be called MySnakeCaseComponent
|
||||
/// #[component]
|
||||
/// fn my_snake_case_component(cx: Scope) -> impl IntoView { todo!() }
|
||||
///
|
||||
/// // CamelCase: Generated component will be called MyComponent
|
||||
/// #[component]
|
||||
/// fn MyComponent(cx: Scope) -> impl IntoView { todo!() }
|
||||
/// ```
|
||||
|
@ -403,6 +405,18 @@ pub fn view(tokens: TokenStream) -> TokenStream {
|
|||
/// pub fn MyComponent(cx: Scope) -> impl IntoView { todo!() }
|
||||
/// }
|
||||
/// ```
|
||||
/// ```
|
||||
/// # use leptos::*;
|
||||
///
|
||||
/// use snake_case_component::{MySnakeCaseComponent, MySnakeCaseComponentProps};
|
||||
///
|
||||
/// mod snake_case_component {
|
||||
/// use leptos::*;
|
||||
///
|
||||
/// #[component]
|
||||
/// pub fn my_snake_case_component(cx: Scope) -> impl IntoView { todo!() }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// 4. You can pass generic arguments, but they should be defined in a `where` clause and not inline.
|
||||
///
|
||||
|
|
Loading…
Reference in a new issue