diff --git a/tachys/src/html/element/custom.rs b/tachys/src/html/element/custom.rs
index 9ed1901c73..42d618b7a6 100644
--- a/tachys/src/html/element/custom.rs
+++ b/tachys/src/html/element/custom.rs
@@ -9,8 +9,9 @@ where
E: AsRef,
{
HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at: std::panic::Location::caller(),
tag: Custom(tag),
-
attributes: (),
children: (),
}
diff --git a/tachys/src/html/element/elements.rs b/tachys/src/html/element/elements.rs
index fc804d67d7..67b5926c6f 100644
--- a/tachys/src/html/element/elements.rs
+++ b/tachys/src/html/element/elements.rs
@@ -25,10 +25,11 @@ macro_rules! html_element_inner {
{
HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at: std::panic::Location::caller(),
tag: $struct_name,
attributes: (),
children: (),
-
}
}
@@ -55,10 +56,17 @@ macro_rules! html_element_inner {
At: NextTuple,
::Output], V>>: Attribute,
{
- let HtmlElement { tag, children, attributes } = self;
+ let HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at,
+ tag,
+ children,
+ attributes
+ } = self;
HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at,
tag,
-
children,
attributes: attributes.next_tuple($crate::html::attribute::$attr(value)),
}
@@ -118,14 +126,16 @@ macro_rules! html_self_closing_elements {
paste::paste! {
$(
#[$meta]
+ #[track_caller]
pub fn $tag() -> HtmlElement<[<$tag:camel>], (), ()>
where
{
HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at: std::panic::Location::caller(),
attributes: (),
children: (),
-
tag: [<$tag:camel>],
}
}
@@ -138,7 +148,6 @@ macro_rules! html_self_closing_elements {
impl HtmlElement<[<$tag:camel>], At, ()>
where
At: Attribute,
-
{
$(
#[doc = concat!("The [`", stringify!($attr), "`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/", stringify!($tag), "#", stringify!($attr) ,") attribute on `<", stringify!($tag), ">`.")]
@@ -151,13 +160,18 @@ macro_rules! html_self_closing_elements {
V: AttributeValue,
At: NextTuple,
::Output], V>>: Attribute,
-
{
- let HtmlElement { tag, children, attributes,
+ let HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at,
+ tag,
+ children,
+ attributes,
} = self;
HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at,
tag,
-
children,
attributes: attributes.next_tuple($crate::html::attribute::$attr(value)),
}
diff --git a/tachys/src/html/element/mod.rs b/tachys/src/html/element/mod.rs
index 388eb3805b..e1581ca7c8 100644
--- a/tachys/src/html/element/mod.rs
+++ b/tachys/src/html/element/mod.rs
@@ -1,6 +1,8 @@
+#[cfg(debug_assertions)]
+use crate::hydration::set_currently_hydrating;
use crate::{
html::attribute::Attribute,
- hydration::Cursor,
+ hydration::{failed_to_cast_element, Cursor},
renderer::{CastFrom, Rndr},
ssr::StreamBuilder,
view::{
@@ -24,10 +26,14 @@ pub use custom::*;
pub use element_ext::*;
pub use elements::*;
pub use inner_html::*;
+#[cfg(debug_assertions)]
+use std::panic::Location;
/// The typed representation of an HTML element.
#[derive(Debug, PartialEq, Eq)]
pub struct HtmlElement {
+ #[cfg(debug_assertions)]
+ pub(crate) defined_at: &'static Location<'static>,
pub(crate) tag: E,
pub(crate) attributes: At,
pub(crate) children: Ch,
@@ -36,8 +42,9 @@ pub struct HtmlElement {
impl Clone for HtmlElement {
fn clone(&self) -> Self {
HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at: self.defined_at,
tag: self.tag.clone(),
-
attributes: self.attributes.clone(),
children: self.children.clone(),
}
@@ -75,14 +82,16 @@ where
fn child(self, child: NewChild) -> Self::Output {
let HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at,
tag,
-
attributes,
children,
} = self;
HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at,
tag,
-
attributes,
children: children.next_tuple(child.into_render()),
}
@@ -103,11 +112,15 @@ where
attr: NewAttr,
) -> Self::Output {
let HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at,
tag,
attributes,
children,
} = self;
HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at,
tag,
attributes: attributes.add_any_attr(attr),
children,
@@ -229,8 +242,9 @@ where
let (attributes, children) =
join(self.attributes.resolve(), self.children.resolve()).await;
HtmlElement {
+ #[cfg(debug_assertions)]
+ defined_at: self.defined_at,
tag: self.tag,
-
attributes,
children,
}
@@ -336,6 +350,11 @@ where
cursor: &Cursor,
position: &PositionState,
) -> Self::State {
+ #[cfg(debug_assertions)]
+ {
+ set_currently_hydrating(Some(self.defined_at));
+ }
+
// non-Static custom elements need special support in templates
// because they haven't been inserted type-wise
if E::TAG.is_empty() && !FROM_SERVER {
@@ -349,7 +368,9 @@ where
cursor.sibling();
}
let el = crate::renderer::types::Element::cast_from(cursor.current())
- .unwrap();
+ .unwrap_or_else(|| {
+ failed_to_cast_element(E::TAG, cursor.current())
+ });
let attrs = self.attributes.hydrate::(&el);
diff --git a/tachys/src/hydration.rs b/tachys/src/hydration.rs
index 5e631841ca..7f7cdd6245 100644
--- a/tachys/src/hydration.rs
+++ b/tachys/src/hydration.rs
@@ -2,7 +2,10 @@ use crate::{
renderer::{CastFrom, Rndr},
view::{Position, PositionState},
};
-use std::{cell::RefCell, rc::Rc};
+#[cfg(debug_assertions)]
+use std::cell::Cell;
+use std::{cell::RefCell, panic::Location, rc::Rc};
+use web_sys::{Comment, Element, Node, Text};
/// Hydration works by walking over the DOM, adding interactivity as needed.
///
@@ -95,13 +98,121 @@ where
}
let marker = self.current();
position.set(Position::NextChild);
- crate::renderer::types::Placeholder::cast_from(marker)
- .expect("could not convert current node into marker node")
- /*let marker2 = marker.clone();
- Rndr::Placeholder::cast_from(marker).unwrap_or_else(|| {
- crate::dom::log("expecting to find a marker. instead, found");
- Rndr::log_node(&marker2);
- panic!("oops.");
- })*/
+ crate::renderer::types::Placeholder::cast_from(marker.clone())
+ .unwrap_or_else(|| failed_to_cast_marker_node(marker))
+ }
+}
+
+#[cfg(debug_assertions)]
+thread_local! {
+ static CURRENTLY_HYDRATING: Cell