feat: allow component declaration without `use leptos::Scope` in scope (#748)
This commit is contained in:
parent
e6b1298915
commit
e9ff26abb4
|
@ -16,6 +16,7 @@ attribute-derive = { version = "0.5", features = ["syn-full"] }
|
|||
cfg-if = "1"
|
||||
html-escape = "0.2"
|
||||
itertools = "0.10"
|
||||
once_cell = "1"
|
||||
prettyplease = "0.1"
|
||||
proc-macro-error = "1"
|
||||
proc-macro2 = "1"
|
||||
|
|
|
@ -4,6 +4,7 @@ use convert_case::{
|
|||
Casing,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use once_cell::unsync::Lazy;
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, ToTokens, TokenStreamExt};
|
||||
use syn::{
|
||||
|
@ -43,7 +44,7 @@ impl Parse for Model {
|
|||
"this method requires a `Scope` parameter";
|
||||
help = "try `fn {}(cx: Scope, /* ... */)`", item.sig.ident
|
||||
);
|
||||
} else if props[0].ty != parse_quote!(Scope) {
|
||||
} else if !is_valid_scope_type(&props[0].ty) {
|
||||
abort!(
|
||||
item.sig.inputs,
|
||||
"this method requires a `Scope` parameter";
|
||||
|
@ -68,7 +69,7 @@ impl Parse for Model {
|
|||
});
|
||||
|
||||
// Make sure return type is correct
|
||||
if item.sig.output != parse_quote!(-> impl IntoView) {
|
||||
if !is_valid_into_view_return_type(&item.sig.output) {
|
||||
abort!(
|
||||
item.sig,
|
||||
"return type is incorrect";
|
||||
|
@ -206,7 +207,7 @@ impl ToTokens for Model {
|
|||
#tracing_instrument_attr
|
||||
#vis fn #name #generics (
|
||||
#[allow(unused_variables)]
|
||||
#scope_name: Scope,
|
||||
#scope_name: ::leptos::Scope,
|
||||
props: #props_name #generics
|
||||
) #ret #(+ #lifetimes)*
|
||||
#where_clause
|
||||
|
@ -436,7 +437,7 @@ impl ToTokens for TypedBuilderOpts {
|
|||
fn prop_builder_fields(vis: &Visibility, props: &[Prop]) -> TokenStream {
|
||||
props
|
||||
.iter()
|
||||
.filter(|Prop { ty, .. }| *ty != parse_quote!(Scope))
|
||||
.filter(|Prop { ty, .. }| !is_valid_scope_type(ty))
|
||||
.map(|prop| {
|
||||
let Prop {
|
||||
docs,
|
||||
|
@ -463,7 +464,7 @@ fn prop_builder_fields(vis: &Visibility, props: &[Prop]) -> TokenStream {
|
|||
fn prop_names(props: &[Prop]) -> TokenStream {
|
||||
props
|
||||
.iter()
|
||||
.filter(|Prop { ty, .. }| *ty != parse_quote!(Scope))
|
||||
.filter(|Prop { ty, .. }| !is_valid_scope_type(ty))
|
||||
.map(|Prop { name, .. }| quote! { #name, })
|
||||
.collect()
|
||||
}
|
||||
|
@ -642,3 +643,27 @@ fn prop_to_doc(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const VALID_SCOPE_TYPES: Lazy<Vec<Type>> = Lazy::new(|| {
|
||||
vec![
|
||||
parse_quote!(Scope),
|
||||
parse_quote!(leptos::Scope),
|
||||
parse_quote!(::leptos::Scope),
|
||||
]
|
||||
});
|
||||
|
||||
fn is_valid_scope_type(ty: &Type) -> bool {
|
||||
VALID_SCOPE_TYPES.iter().any(|test| ty == test)
|
||||
}
|
||||
|
||||
const VALID_INTO_VIEW_RETURN_TYPES: Lazy<Vec<ReturnType>> = Lazy::new(|| {
|
||||
vec![
|
||||
parse_quote!(-> impl IntoView),
|
||||
parse_quote!(-> impl leptos::IntoView),
|
||||
parse_quote!(-> impl ::leptos::IntoView),
|
||||
]
|
||||
});
|
||||
|
||||
fn is_valid_into_view_return_type(ty: &ReturnType) -> bool {
|
||||
VALID_INTO_VIEW_RETURN_TYPES.iter().any(|test| ty == test)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#[test]
|
||||
fn ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/*.rs");
|
||||
t.compile_fail("tests/ui/component.rs");
|
||||
t.compile_fail("tests/ui/component_absolute.rs");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
#[::leptos::component]
|
||||
fn missing_scope() {}
|
||||
|
||||
#[::leptos::component]
|
||||
fn missing_return_type(cx: ::leptos::Scope) {}
|
||||
|
||||
#[::leptos::component]
|
||||
fn unknown_prop_option(cx: ::leptos::Scope, #[prop(hello)] test: bool) -> impl ::leptos::IntoView {}
|
||||
|
||||
#[::leptos::component]
|
||||
fn optional_and_optional_no_strip(
|
||||
cx: Scope,
|
||||
#[prop(optional, optional_no_strip)] conflicting: bool,
|
||||
) -> impl IntoView {
|
||||
}
|
||||
|
||||
#[::leptos::component]
|
||||
fn optional_and_strip_option(
|
||||
cx: ::leptos::Scope,
|
||||
#[prop(optional, strip_option)] conflicting: bool,
|
||||
) -> impl ::leptos::IntoView {
|
||||
}
|
||||
|
||||
#[::leptos::component]
|
||||
fn optional_no_strip_and_strip_option(
|
||||
cx: ::leptos::Scope,
|
||||
#[prop(optional_no_strip, strip_option)] conflicting: bool,
|
||||
) -> impl ::leptos::IntoView {
|
||||
}
|
||||
|
||||
#[::leptos::component]
|
||||
fn default_without_value(
|
||||
cx: ::leptos::Scope,
|
||||
#[prop(default)] default: bool,
|
||||
) -> impl ::leptos::IntoView {
|
||||
}
|
||||
|
||||
#[::leptos::component]
|
||||
fn default_with_invalid_value(
|
||||
cx: ::leptos::Scope,
|
||||
#[prop(default= |)] default: bool,
|
||||
) -> impl ::leptos::IntoView {
|
||||
}
|
||||
|
||||
#[::leptos::component]
|
||||
pub fn using_the_view_macro(cx: ::leptos::Scope) -> impl ::leptos::IntoView {
|
||||
::leptos::view! { cx,
|
||||
"ok"
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,53 @@
|
|||
error: this method requires a `Scope` parameter
|
||||
--> tests/ui/component_absolute.rs:2:1
|
||||
|
|
||||
2 | fn missing_scope() {}
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: try `fn missing_scope(cx: Scope, /* ... */)`
|
||||
|
||||
error: return type is incorrect
|
||||
--> tests/ui/component_absolute.rs:5:1
|
||||
|
|
||||
5 | fn missing_return_type(cx: ::leptos::Scope) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: return signature must be `-> impl IntoView`
|
||||
|
||||
error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default` and `into`
|
||||
--> tests/ui/component_absolute.rs:8:52
|
||||
|
|
||||
8 | fn unknown_prop_option(cx: ::leptos::Scope, #[prop(hello)] test: bool) -> impl ::leptos::IntoView {}
|
||||
| ^^^^^
|
||||
|
||||
error: `optional` conflicts with mutually exclusive `optional_no_strip`
|
||||
--> tests/ui/component_absolute.rs:13:12
|
||||
|
|
||||
13 | #[prop(optional, optional_no_strip)] conflicting: bool,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `optional` conflicts with mutually exclusive `strip_option`
|
||||
--> tests/ui/component_absolute.rs:20:12
|
||||
|
|
||||
20 | #[prop(optional, strip_option)] conflicting: bool,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `optional_no_strip` conflicts with mutually exclusive `strip_option`
|
||||
--> tests/ui/component_absolute.rs:27:12
|
||||
|
|
||||
27 | #[prop(optional_no_strip, strip_option)] conflicting: bool,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unexpected end of input, expected assignment `=`
|
||||
--> tests/ui/component_absolute.rs:34:19
|
||||
|
|
||||
34 | #[prop(default)] default: bool,
|
||||
| ^
|
||||
|
||||
error: unexpected end of input, expected one of: `::`, `<`, `_`, literal, `const`, `ref`, `mut`, `&`, parentheses, square brackets, `..`, `const`
|
||||
|
||||
= help: try `#[prop(default=5 * 10)]`
|
||||
--> tests/ui/component_absolute.rs:41:22
|
||||
|
|
||||
41 | #[prop(default= |)] default: bool,
|
||||
| ^
|
Loading…
Reference in New Issue