Merge branch 'leptos_dom_v2' of https://github.com/jquesada2016/leptos into leptos_dom_v2
This commit is contained in:
commit
5f95776a08
|
@ -28,15 +28,16 @@ fn leptos_ssr_bench(b: &mut Bencher) {
|
|||
<Counter initial=2/>
|
||||
<Counter initial=3/>
|
||||
</main>
|
||||
}.into_view(cx).render_to_string();
|
||||
}.into_view(cx).render_to_string(cx);
|
||||
|
||||
assert!(
|
||||
!rendered.is_empty()
|
||||
assert_eq!(
|
||||
rendered,
|
||||
"<main><h1>Welcome to our benchmark page.</h1><p>Here's some introductory text.</p><div><button>-1</button><span>Value: <!>1<template id=\"_3\"></template>!</span><button>+1</button></div><template id=\"_1\"></template><div><button>-1</button><span>Value: <!>2<template id=\"_2\"></template>!</span><button>+1</button></div><template id=\"_0\"></template><div><button>-1</button><span>Value: <!>3<template id=\"_2\"></template>!</span><button>+1</button></div><template id=\"_0\"></template></main>"
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
#[bench]
|
||||
fn tera_ssr_bench(b: &mut Bencher) {
|
||||
use tera::*;
|
||||
|
@ -193,3 +194,4 @@ fn yew_ssr_bench(b: &mut Bencher) {
|
|||
});
|
||||
});
|
||||
}
|
||||
*/
|
|
@ -1,4 +1,4 @@
|
|||
use leptos::*;
|
||||
pub use leptos::*;
|
||||
use miniserde::*;
|
||||
use web_sys::HtmlInputElement;
|
||||
|
||||
|
@ -103,7 +103,7 @@ const ESCAPE_KEY: u32 = 27;
|
|||
const ENTER_KEY: u32 = 13;
|
||||
|
||||
#[component]
|
||||
pub fn TodoMVC(cx: Scope, todos: Todos) -> impl IntoView {
|
||||
pub fn TodoMVC(cx: Scope,todos: Todos) -> impl IntoView {
|
||||
let mut next_id = todos
|
||||
.0
|
||||
.iter()
|
||||
|
|
|
@ -8,14 +8,13 @@ mod yew;
|
|||
#[bench]
|
||||
fn leptos_todomvc_ssr(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
use self::leptos::*;
|
||||
use ::leptos::*;
|
||||
use crate::todomvc::leptos::*;
|
||||
|
||||
_ = create_scope(create_runtime(), |cx| {
|
||||
let rendered = view! {
|
||||
cx,
|
||||
<TodoMVC todos=Todos::new(cx)/>
|
||||
}.into_view(cx).render_to_string();
|
||||
}.into_view(cx).render_to_string(cx);
|
||||
|
||||
assert!(rendered.len() > 1);
|
||||
});
|
||||
|
@ -67,7 +66,7 @@ fn leptos_todomvc_ssr_with_1000(b: &mut Bencher) {
|
|||
let rendered = view! {
|
||||
cx,
|
||||
<TodoMVC todos=Todos::new_with_1000(cx)/>
|
||||
}.into_view(cx).render_to_string();
|
||||
}.into_view(cx).render_to_string(cx);
|
||||
|
||||
assert!(rendered.len() > 1);
|
||||
});
|
||||
|
|
|
@ -204,7 +204,7 @@ where IV: IntoView
|
|||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link rel="modulepreload" href="{pkg_path}.js">
|
||||
<link rel="preload" href="{pkg_path}.wasm" as="fetch" type="application/wasm" crossorigin="">
|
||||
<link rel="preload" href="{pkg_path}_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
|
||||
<script type="module">import init, {{ hydrate }} from '{pkg_path}.js'; init('{pkg_path}_bg.wasm').then(hydrate);</script>
|
||||
{leptos_autoreload}
|
||||
"#
|
||||
|
|
|
@ -133,3 +133,13 @@ pub fn window_event_listener(
|
|||
window().add_event_listener_with_callback(event_name, cb.unchecked_ref());
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// This exists only to enable type inference on event listeners when in SSR mode.
|
||||
pub fn ssr_event_listener<E: crate::ev::EventDescriptor + 'static>(
|
||||
event: E,
|
||||
event_handler: impl FnMut(E::EventType) + 'static,
|
||||
) {
|
||||
_ = event;
|
||||
_ = event_handler;
|
||||
}
|
|
@ -205,6 +205,8 @@ cfg_if! {
|
|||
#[educe(Debug(ignore))]
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) children: SmallVec<[View; 4]>,
|
||||
#[educe(Debug(ignore))]
|
||||
pub(crate) prerendered: Option<Cow<'static, str>>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -235,11 +237,24 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
attrs: smallvec![],
|
||||
children: smallvec![],
|
||||
element,
|
||||
prerendered: None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
pub fn from_html(cx: Scope, element: El, html: impl Into<Cow<'static, str>>) -> Self {
|
||||
Self {
|
||||
cx,
|
||||
attrs: smallvec![],
|
||||
children: smallvec![],
|
||||
element,
|
||||
prerendered: Some(html.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts this element into [`HtmlElement<AnyElement>`].
|
||||
pub fn into_any(self) -> HtmlElement<AnyElement> {
|
||||
cfg_if! {
|
||||
|
@ -263,12 +278,14 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
attrs,
|
||||
children,
|
||||
element,
|
||||
prerendered
|
||||
} = self;
|
||||
|
||||
HtmlElement {
|
||||
cx,
|
||||
attrs,
|
||||
children,
|
||||
prerendered,
|
||||
element: AnyElement {
|
||||
name: element.name(),
|
||||
is_void: element.is_void(),
|
||||
|
@ -546,6 +563,7 @@ impl<El: IntoElement> IntoView for HtmlElement<El> {
|
|||
element,
|
||||
mut attrs,
|
||||
children,
|
||||
prerendered,
|
||||
..
|
||||
} = self;
|
||||
|
||||
|
@ -562,6 +580,7 @@ impl<El: IntoElement> IntoView for HtmlElement<El> {
|
|||
|
||||
element.attrs = attrs;
|
||||
element.children.extend(children);
|
||||
element.prerendered = prerendered;
|
||||
|
||||
View::Element(element)
|
||||
}
|
||||
|
|
|
@ -46,11 +46,12 @@ impl HydrationCtx {
|
|||
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
pub(crate) fn set_id(cx: Scope) {
|
||||
let new_id = if let Some(id) = cx.get_hydration_key() {
|
||||
/* let new_id = if let Some(id) = cx.get_hydration_key() {
|
||||
id + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
}; */
|
||||
let new_id = 0;
|
||||
println!("setting ID to {new_id}");
|
||||
unsafe { ID = new_id };
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@ cfg_if! {
|
|||
is_void: bool,
|
||||
attrs: SmallVec<[(Cow<'static, str>, Cow<'static, str>); 4]>,
|
||||
children: Vec<View>,
|
||||
prerendered: Option<Cow<'static, str>>,
|
||||
id: usize,
|
||||
}
|
||||
|
||||
|
@ -203,6 +204,7 @@ impl Element {
|
|||
attrs,
|
||||
children,
|
||||
id,
|
||||
prerendered
|
||||
} = self;
|
||||
|
||||
let element = AnyElement { name, is_void, id };
|
||||
|
@ -212,6 +214,7 @@ impl Element {
|
|||
element,
|
||||
attrs,
|
||||
children: children.into_iter().collect(),
|
||||
prerendered
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,10 +245,24 @@ impl Element {
|
|||
attrs: Default::default(),
|
||||
children: Default::default(),
|
||||
id: el.hydration_id(),
|
||||
prerendered: None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
#[track_caller]
|
||||
fn from_html<El: IntoElement>(el: El, html: impl Into<Cow<'static, str>>) -> Self {
|
||||
Self {
|
||||
name: el.name(),
|
||||
is_void: el.is_void(),
|
||||
attrs: Default::default(),
|
||||
children: Default::default(),
|
||||
id: el.hydration_id(),
|
||||
prerendered: Some(html.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
|
@ -154,12 +154,12 @@ pub fn render_to_stream_with_prefix(
|
|||
impl View {
|
||||
/// Consumes the node and renders it into an HTML string.
|
||||
pub fn render_to_string(self, cx: Scope) -> Cow<'static, str> {
|
||||
cx.set_hydration_key(HydrationCtx::current_id());
|
||||
//cx.set_hydration_key(HydrationCtx::current_id());
|
||||
HydrationCtx::set_id(cx);
|
||||
|
||||
let s = self.render_to_string_helper();
|
||||
|
||||
cx.set_hydration_key(HydrationCtx::current_id());
|
||||
//cx.set_hydration_key(HydrationCtx::current_id());
|
||||
|
||||
s
|
||||
}
|
||||
|
@ -279,34 +279,38 @@ impl View {
|
|||
}
|
||||
}
|
||||
View::Element(el) => {
|
||||
let tag_name = el.name;
|
||||
|
||||
let attrs = el
|
||||
.attrs
|
||||
.into_iter()
|
||||
.map(|(name, value)| -> Cow<'static, str> {
|
||||
if value.is_empty() {
|
||||
format!(" {name}").into()
|
||||
} else {
|
||||
format!(
|
||||
" {name}=\"{}\"",
|
||||
html_escape::encode_double_quoted_attribute(&value)
|
||||
)
|
||||
.into()
|
||||
}
|
||||
})
|
||||
.join("");
|
||||
|
||||
if el.is_void {
|
||||
format!("<{tag_name}{attrs}/>").into()
|
||||
if let Some(prerendered) = el.prerendered {
|
||||
prerendered
|
||||
} else {
|
||||
let children = el
|
||||
.children
|
||||
.into_iter()
|
||||
.map(|node| node.render_to_string_helper())
|
||||
.join("");
|
||||
let tag_name = el.name;
|
||||
|
||||
format!("<{tag_name}{attrs}>{children}</{tag_name}>").into()
|
||||
let attrs = el
|
||||
.attrs
|
||||
.into_iter()
|
||||
.map(|(name, value)| -> Cow<'static, str> {
|
||||
if value.is_empty() {
|
||||
format!(" {name}").into()
|
||||
} else {
|
||||
format!(
|
||||
" {name}=\"{}\"",
|
||||
html_escape::encode_double_quoted_attribute(&value)
|
||||
)
|
||||
.into()
|
||||
}
|
||||
})
|
||||
.join("");
|
||||
|
||||
if el.is_void {
|
||||
format!("<{tag_name}{attrs}/>").into()
|
||||
} else {
|
||||
let children = el
|
||||
.children
|
||||
.into_iter()
|
||||
.map(|node| node.render_to_string_helper())
|
||||
.join("");
|
||||
|
||||
format!("<{tag_name}{attrs}>{children}</{tag_name}>").into()
|
||||
}
|
||||
}
|
||||
}
|
||||
View::Transparent(_) => Default::default(),
|
||||
|
|
|
@ -233,7 +233,9 @@ pub fn view(tokens: TokenStream) -> TokenStream {
|
|||
Ok(nodes) => render_view(
|
||||
&proc_macro2::Ident::new(&cx.to_string(), cx.span().into()),
|
||||
&nodes,
|
||||
Mode::default(),
|
||||
// swap to Mode::default() to use faster SSR templating
|
||||
Mode::Client
|
||||
//Mode::default(),
|
||||
),
|
||||
Err(error) => error.to_compile_error(),
|
||||
}
|
||||
|
|
|
@ -139,26 +139,58 @@ pub(crate) fn render_view(
|
|||
nodes: &[Node],
|
||||
mode: Mode,
|
||||
) -> TokenStream {
|
||||
if nodes.is_empty() {
|
||||
let span = Span::call_site();
|
||||
quote_spanned! {
|
||||
span => leptos::Unit
|
||||
if mode == Mode::Ssr {
|
||||
if nodes.is_empty() {
|
||||
let span = Span::call_site();
|
||||
quote_spanned! {
|
||||
span => leptos::Unit
|
||||
}
|
||||
} else if nodes.len() == 1 {
|
||||
root_node_to_tokens_ssr(cx, &nodes[0])
|
||||
} else {
|
||||
fragment_to_tokens_ssr(cx, Span::call_site(), nodes)
|
||||
}
|
||||
} else if nodes.len() == 1 {
|
||||
node_to_tokens(cx, &nodes[0], mode)
|
||||
} else {
|
||||
fragment_to_tokens(cx, Span::call_site(), nodes, mode)
|
||||
if nodes.is_empty() {
|
||||
let span = Span::call_site();
|
||||
quote_spanned! {
|
||||
span => leptos::Unit
|
||||
}
|
||||
} else if nodes.len() == 1 {
|
||||
node_to_tokens(cx, &nodes[0])
|
||||
} else {
|
||||
fragment_to_tokens(cx, Span::call_site(), nodes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fragment_to_tokens(
|
||||
cx: &Ident,
|
||||
span: Span,
|
||||
nodes: &[Node],
|
||||
mode: Mode,
|
||||
) -> TokenStream {
|
||||
fn root_node_to_tokens_ssr(cx: &Ident, node: &Node) -> TokenStream {
|
||||
match node {
|
||||
Node::Fragment(fragment) => {
|
||||
fragment_to_tokens_ssr(cx, Span::call_site(), &fragment.children)
|
||||
}
|
||||
Node::Comment(_) | Node::Doctype(_) | Node::Attribute(_) => quote! {},
|
||||
Node::Text(node) => {
|
||||
let span = node.value.span();
|
||||
let value = node.value.as_ref();
|
||||
quote_spanned! {
|
||||
span => leptos::text(#value)
|
||||
}
|
||||
}
|
||||
Node::Block(node) => {
|
||||
let span = node.value.span();
|
||||
let value = node.value.as_ref();
|
||||
quote_spanned! {
|
||||
span => #value
|
||||
}
|
||||
}
|
||||
Node::Element(node) => root_element_to_tokens_ssr(cx, node),
|
||||
}
|
||||
}
|
||||
|
||||
fn fragment_to_tokens_ssr(cx: &Ident, span: Span, nodes: &[Node]) -> TokenStream {
|
||||
let nodes = nodes.iter().map(|node| {
|
||||
let node = node_to_tokens(cx, node, mode);
|
||||
let node = root_node_to_tokens_ssr(cx, node);
|
||||
let span = node.span();
|
||||
quote_spanned! {
|
||||
span => #node.into_view(#cx),
|
||||
|
@ -173,17 +205,195 @@ fn fragment_to_tokens(
|
|||
}
|
||||
}
|
||||
|
||||
fn node_to_tokens(cx: &Ident, node: &Node, mode: Mode) -> TokenStream {
|
||||
fn root_element_to_tokens_ssr(cx: &Ident, node: &NodeElement) -> TokenStream {
|
||||
let mut template = String::new();
|
||||
let mut holes = Vec::<TokenStream>::new();
|
||||
let mut exprs_for_compiler = Vec::<TokenStream>::new();
|
||||
|
||||
let span = node.name.span();
|
||||
|
||||
element_to_tokens_ssr(cx, node, &mut template, &mut holes, &mut exprs_for_compiler);
|
||||
|
||||
let template = if holes.is_empty() {
|
||||
quote! {
|
||||
#template
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
format!(
|
||||
#template,
|
||||
#(#holes)*
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// TODO get proper element types for return-type purposes
|
||||
quote_spanned! {
|
||||
span => {
|
||||
#(#exprs_for_compiler)*
|
||||
::leptos::HtmlElement::from_html(cx, leptos::Div::default(), #template)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn element_to_tokens_ssr(cx: &Ident, node: &NodeElement, template: &mut String, holes: &mut Vec<TokenStream>, exprs_for_compiler: &mut Vec<TokenStream>) {
|
||||
let span = node.name.span();
|
||||
|
||||
if is_component_node(node) {
|
||||
template.push_str("{}");
|
||||
let component = component_to_tokens(cx, node);
|
||||
holes.push(quote_spanned! {
|
||||
span => {#component}.into_view(cx).render_to_string(cx),
|
||||
})
|
||||
} else {
|
||||
template.push('<');
|
||||
template.push_str(&node.name.to_string());
|
||||
|
||||
for attr in &node.attributes {
|
||||
if let Node::Attribute(attr) = attr {
|
||||
attribute_to_tokens_ssr(cx, attr, template, holes, exprs_for_compiler);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle classes
|
||||
|
||||
if is_self_closing(node) {
|
||||
template.push_str("/>");
|
||||
} else {
|
||||
template.push('>');
|
||||
for child in &node.children {
|
||||
match child {
|
||||
Node::Element(child) => element_to_tokens_ssr(cx, child, template, holes, exprs_for_compiler),
|
||||
Node::Text(text) => {
|
||||
if let Some(value) = value_to_string(&text.value) {
|
||||
template.push_str(&value);
|
||||
} else {
|
||||
template.push_str("{}");
|
||||
let value = text.value.as_ref();
|
||||
let span = text.value.span();
|
||||
holes.push(quote_spanned! {
|
||||
span => #value.into_view(#cx).render_to_string(#cx),
|
||||
})
|
||||
}
|
||||
},
|
||||
Node::Block(block) => {
|
||||
if let Some(value) = value_to_string(&block.value) {
|
||||
template.push_str(&value);
|
||||
} else {
|
||||
template.push_str("{}");
|
||||
let value = block.value.as_ref();
|
||||
let span = block.value.span();
|
||||
holes.push(quote_spanned! {
|
||||
span => #value.into_view(#cx).render_to_string(#cx),
|
||||
})
|
||||
}
|
||||
},
|
||||
Node::Fragment(_) => todo!(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
template.push_str("</");
|
||||
template.push_str(&node.name.to_string());
|
||||
template.push('>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn value_to_string(value: &syn_rsx::NodeValueExpr) -> Option<String> {
|
||||
match &value.as_ref() {
|
||||
syn::Expr::Lit(lit) => match &lit.lit {
|
||||
syn::Lit::Str(s) => Some(s.value()),
|
||||
syn::Lit::Char(c) => Some(c.value().to_string()),
|
||||
syn::Lit::Int(i) => Some(i.base10_digits().to_string()),
|
||||
syn::Lit::Float(f) => Some(f.base10_digits().to_string()),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn attribute_to_tokens_ssr(cx: &Ident, node: &NodeAttribute, template: &mut String, holes: &mut Vec<TokenStream>, exprs_for_compiler: &mut Vec<TokenStream>) {
|
||||
let span = node.key.span();
|
||||
let name = node.key.to_string();
|
||||
if name == "ref" || name == "_ref" {
|
||||
// ignore refs on SSR
|
||||
} else if let Some(name) = name.strip_prefix("on:") {
|
||||
let span = name.span();
|
||||
let handler = node
|
||||
.value
|
||||
.as_ref()
|
||||
.expect("event listener attributes need a value")
|
||||
.as_ref();
|
||||
let event_type = TYPED_EVENTS
|
||||
.iter()
|
||||
.find(|e| **e == name)
|
||||
.copied()
|
||||
.unwrap_or("Custom");
|
||||
let event_type = event_type
|
||||
.parse::<TokenStream>()
|
||||
.expect("couldn't parse event name");
|
||||
|
||||
exprs_for_compiler.push(quote_spanned! {
|
||||
span => leptos::ssr_event_listener(leptos::ev::#event_type, #handler);
|
||||
})
|
||||
} else if let Some(name) = name.strip_prefix("prop:") {
|
||||
// ignore props for SSR
|
||||
} else if let Some(name) = name.strip_prefix("class:") {
|
||||
// ignore classes: we'll handle these separately
|
||||
} else {
|
||||
let name = name.replacen("attr:", "", 1);
|
||||
template.push(' ');
|
||||
template.push_str(&name);
|
||||
|
||||
if let Some(value) = node.value.as_ref() {
|
||||
if let Some(value) = value_to_string(value) {
|
||||
template.push_str(&value);
|
||||
} else {
|
||||
template.push_str("{}");
|
||||
let span = value.span();
|
||||
let value = value.as_ref();
|
||||
holes.push(quote_spanned! {
|
||||
span => leptos::escape_attr(&{#value}.into_attribute(#cx).as_value_string(#name)),
|
||||
})
|
||||
}
|
||||
template.push('"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fragment_to_tokens(
|
||||
cx: &Ident,
|
||||
span: Span,
|
||||
nodes: &[Node],
|
||||
) -> TokenStream {
|
||||
let nodes = nodes.iter().map(|node| {
|
||||
let node = node_to_tokens(cx, node);
|
||||
let span = node.span();
|
||||
quote_spanned! {
|
||||
span => #node.into_view(#cx),
|
||||
}
|
||||
});
|
||||
quote_spanned! {
|
||||
span => {
|
||||
leptos::Fragment::new(vec![
|
||||
#(#nodes)*
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn node_to_tokens(cx: &Ident, node: &Node) -> TokenStream {
|
||||
match node {
|
||||
Node::Fragment(fragment) => {
|
||||
fragment_to_tokens(cx, Span::call_site(), &fragment.children, mode)
|
||||
fragment_to_tokens(cx, Span::call_site(), &fragment.children)
|
||||
}
|
||||
Node::Comment(_) | Node::Doctype(_) => quote! {},
|
||||
Node::Text(node) => {
|
||||
let span = node.value.span();
|
||||
let value = node.value.as_ref();
|
||||
quote_spanned! {
|
||||
span => text(#value)
|
||||
span => leptos::text(#value)
|
||||
}
|
||||
}
|
||||
Node::Block(node) => {
|
||||
|
@ -193,19 +403,18 @@ fn node_to_tokens(cx: &Ident, node: &Node, mode: Mode) -> TokenStream {
|
|||
span => #value
|
||||
}
|
||||
}
|
||||
Node::Attribute(node) => attribute_to_tokens(cx, node, mode),
|
||||
Node::Element(node) => element_to_tokens(cx, node, mode),
|
||||
Node::Attribute(node) => attribute_to_tokens(cx, node),
|
||||
Node::Element(node) => element_to_tokens(cx, node),
|
||||
}
|
||||
}
|
||||
|
||||
fn element_to_tokens(
|
||||
cx: &Ident,
|
||||
node: &NodeElement,
|
||||
mode: Mode,
|
||||
) -> TokenStream {
|
||||
let span = node.name.span();
|
||||
if is_component_node(node) {
|
||||
component_to_tokens(cx, node, mode)
|
||||
component_to_tokens(cx, node)
|
||||
} else {
|
||||
let name = if is_custom_element(&node.name) {
|
||||
let name = node.name.to_string();
|
||||
|
@ -216,7 +425,7 @@ fn element_to_tokens(
|
|||
};
|
||||
let attrs = node.attributes.iter().filter_map(|node| {
|
||||
if let Node::Attribute(node) = node {
|
||||
Some(attribute_to_tokens(cx, node, mode))
|
||||
Some(attribute_to_tokens(cx, node))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -224,7 +433,7 @@ fn element_to_tokens(
|
|||
let children = node.children.iter().map(|node| {
|
||||
let child = match node {
|
||||
Node::Fragment(fragment) => {
|
||||
fragment_to_tokens(cx, Span::call_site(), &fragment.children, mode)
|
||||
fragment_to_tokens(cx, Span::call_site(), &fragment.children)
|
||||
}
|
||||
Node::Text(node) => {
|
||||
let span = node.value.span();
|
||||
|
@ -240,7 +449,7 @@ fn element_to_tokens(
|
|||
span => #[allow(unused_braces)] #value
|
||||
}
|
||||
}
|
||||
Node::Element(node) => element_to_tokens(cx, node, mode),
|
||||
Node::Element(node) => element_to_tokens(cx, node),
|
||||
Node::Comment(_) | Node::Doctype(_) | Node::Attribute(_) => quote! {},
|
||||
};
|
||||
quote! {
|
||||
|
@ -258,12 +467,10 @@ fn element_to_tokens(
|
|||
fn attribute_to_tokens(
|
||||
cx: &Ident,
|
||||
node: &NodeAttribute,
|
||||
_mode: Mode,
|
||||
) -> TokenStream {
|
||||
let span = node.key.span();
|
||||
let name = node.key.to_string();
|
||||
if name == "ref" || name == "_ref" {
|
||||
//if mode != Mode::Ssr {
|
||||
let value = node
|
||||
.value
|
||||
.as_ref()
|
||||
|
@ -272,11 +479,7 @@ fn attribute_to_tokens(
|
|||
quote_spanned! {
|
||||
span => .node_ref(#value)
|
||||
}
|
||||
/* } else {
|
||||
todo!()
|
||||
} */
|
||||
} else if let Some(name) = name.strip_prefix("on:") {
|
||||
//if mode != Mode::Ssr {
|
||||
let span = name.span();
|
||||
let handler = node
|
||||
.value
|
||||
|
@ -295,35 +498,24 @@ fn attribute_to_tokens(
|
|||
quote_spanned! {
|
||||
span => .on(leptos::ev::#event_type, #handler)
|
||||
}
|
||||
/* } else {
|
||||
todo!()
|
||||
} */
|
||||
} else if let Some(name) = name.strip_prefix("prop:") {
|
||||
let value = node
|
||||
.value
|
||||
.as_ref()
|
||||
.expect("prop: attributes need a value")
|
||||
.as_ref();
|
||||
//if mode != Mode::Ssr {
|
||||
quote_spanned! {
|
||||
span => .prop(#name, (#cx, #[allow(unused_braces)] #value))
|
||||
}
|
||||
/* } else {
|
||||
todo!()
|
||||
} */
|
||||
} else if let Some(name) = name.strip_prefix("class:") {
|
||||
let value = node
|
||||
.value
|
||||
.as_ref()
|
||||
.expect("class: attributes need a value")
|
||||
.as_ref();
|
||||
//if mode != Mode::Ssr {
|
||||
quote_spanned! {
|
||||
span => .class(#name, (#cx, #[allow(unused_braces)] #value))
|
||||
}
|
||||
/* } else {
|
||||
todo!()
|
||||
} */
|
||||
} else {
|
||||
let name = name.replacen("attr:", "", 1);
|
||||
let value = match node.value.as_ref() {
|
||||
|
@ -334,20 +526,15 @@ fn attribute_to_tokens(
|
|||
}
|
||||
None => quote_spanned! { span => "" },
|
||||
};
|
||||
//if mode != Mode::Ssr {
|
||||
quote_spanned! {
|
||||
span => .attr(#name, (#cx, #value))
|
||||
}
|
||||
/* } else {
|
||||
quote! { }
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
fn component_to_tokens(
|
||||
cx: &Ident,
|
||||
node: &NodeElement,
|
||||
mode: Mode,
|
||||
) -> TokenStream {
|
||||
let name = &node.name;
|
||||
let component_name = ident_from_tag_name(&node.name);
|
||||
|
@ -358,7 +545,7 @@ fn component_to_tokens(
|
|||
let children = if node.children.is_empty() {
|
||||
quote! { }
|
||||
} else {
|
||||
let children = fragment_to_tokens(cx, span, &node.children, mode);
|
||||
let children = fragment_to_tokens(cx, span, &node.children);
|
||||
quote! { .children(Box::new(move |#cx| #children)) }
|
||||
};
|
||||
|
||||
|
@ -434,3 +621,25 @@ fn expr_to_ident(expr: &syn::Expr) -> Option<&ExprPath> {
|
|||
fn is_custom_element(name: &NodeName) -> bool {
|
||||
name.to_string().contains('-')
|
||||
}
|
||||
|
||||
fn is_self_closing(node: &NodeElement) -> bool {
|
||||
// self-closing tags
|
||||
// https://developer.mozilla.org/en-US/docs/Glossary/Empty_element
|
||||
matches!(
|
||||
node.name.to_string().as_str(),
|
||||
"area"
|
||||
| "base"
|
||||
| "br"
|
||||
| "col"
|
||||
| "embed"
|
||||
| "hr"
|
||||
| "img"
|
||||
| "input"
|
||||
| "link"
|
||||
| "meta"
|
||||
| "param"
|
||||
| "source"
|
||||
| "track"
|
||||
| "wbr"
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue