,
}
@@ -35,7 +34,6 @@ where
render_fn,
memo,
props,
- // props: PropsAllocation::Borrowed(props),
_marker: PhantomData,
}
}
@@ -60,7 +58,7 @@ where
}
fn render(&'a self, cx: &'a ScopeState) -> RenderReturn<'a> {
- let scope = cx.bump().alloc(Scoped {
+ let scope: &mut Scoped = cx.bump().alloc(Scoped {
props: &self.props,
scope: cx,
});
diff --git a/packages/core/src/arena.rs b/packages/core/src/arena.rs
index a0da5b6ef..6add899e8 100644
--- a/packages/core/src/arena.rs
+++ b/packages/core/src/arena.rs
@@ -1,10 +1,14 @@
-use crate::{nodes::VNode, virtual_dom::VirtualDom, Mutations, ScopeId};
+use crate::{
+ factory::RenderReturn, nodes::VNode, virtual_dom::VirtualDom, AttributeValue, DynamicNode,
+ ScopeId, VFragment,
+};
+use bumpalo::boxed::Box as BumpBox;
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct ElementId(pub usize);
-pub struct ElementRef {
+pub(crate) struct ElementRef {
// the pathway of the real element inside the template
pub path: ElementPath,
@@ -19,7 +23,7 @@ pub enum ElementPath {
}
impl ElementRef {
- pub fn null() -> Self {
+ pub(crate) fn null() -> Self {
Self {
template: std::ptr::null_mut(),
path: ElementPath::Root(0),
@@ -28,74 +32,88 @@ impl ElementRef {
}
impl<'b> VirtualDom {
- pub fn next_element(&mut self, template: &VNode, path: &'static [u8]) -> ElementId {
+ pub(crate) fn next_element(&mut self, template: &VNode, path: &'static [u8]) -> ElementId {
let entry = self.elements.vacant_entry();
let id = entry.key();
-
entry.insert(ElementRef {
template: template as *const _ as *mut _,
path: ElementPath::Deep(path),
});
-
- println!("Claiming {}", id);
-
ElementId(id)
}
- pub fn next_root(&mut self, template: &VNode, path: usize) -> ElementId {
+ pub(crate) fn next_root(&mut self, template: &VNode, path: usize) -> ElementId {
let entry = self.elements.vacant_entry();
let id = entry.key();
-
entry.insert(ElementRef {
template: template as *const _ as *mut _,
path: ElementPath::Root(path),
});
-
- println!("Claiming {}", id);
-
ElementId(id)
}
- pub fn cleanup_element(&mut self, id: ElementId) {
- self.elements.remove(id.0);
+ pub(crate) fn reclaim(&mut self, el: ElementId) {
+ self.try_reclaim(el)
+ .unwrap_or_else(|| panic!("cannot reclaim {:?}", el));
}
- pub fn drop_scope(&mut self, id: ScopeId) {
- // let scope = self.scopes.get(id.0).unwrap();
-
- // let root = scope.root_node();
- // let root = unsafe { std::mem::transmute(root) };
-
- // self.drop_template(root, false);
- todo!()
- }
-
- pub fn reclaim(&mut self, el: ElementId) {
+ pub(crate) fn try_reclaim(&mut self, el: ElementId) -> Option {
assert_ne!(el, ElementId(0));
- self.elements.remove(el.0);
+ self.elements.try_remove(el.0)
}
- pub fn drop_template(
- &mut self,
- mutations: &mut Mutations,
- template: &'b VNode<'b>,
- gen_roots: bool,
- ) {
- // for node in template.dynamic_nodes.iter() {
- // match node {
- // DynamicNode::Text { id, .. } => {}
+ // Drop a scope and all its children
+ pub(crate) fn drop_scope(&mut self, id: ScopeId) {
+ let scope = self.scopes.get(id.0).unwrap();
- // DynamicNode::Component { .. } => {
- // todo!()
- // }
+ if let Some(root) = scope.as_ref().try_root_node() {
+ let root = unsafe { root.extend_lifetime_ref() };
+ match root {
+ RenderReturn::Sync(Ok(node)) => self.drop_scope_inner(node),
+ _ => {}
+ }
+ }
- // DynamicNode::Fragment { inner, nodes } => {}
- // DynamicNode::Placeholder(_) => todo!(),
- // _ => todo!(),
- // }
- // }
+ let scope = self.scopes.get(id.0).unwrap();
+
+ if let Some(root) = unsafe { scope.as_ref().previous_frame().try_load_node() } {
+ let root = unsafe { root.extend_lifetime_ref() };
+ match root {
+ RenderReturn::Sync(Ok(node)) => self.drop_scope_inner(node),
+ _ => {}
+ }
+ }
+
+ let scope = self.scopes.get(id.0).unwrap().as_ref();
+
+ // Drop all the hooks once the children are dropped
+ // this means we'll drop hooks bottom-up
+ for hook in scope.hook_list.borrow_mut().drain(..) {
+ drop(unsafe { BumpBox::from_raw(hook) });
+ }
+ }
+
+ fn drop_scope_inner(&mut self, node: &VNode) {
+ for attr in node.dynamic_attrs {
+ if let AttributeValue::Listener(l) = &attr.value {
+ l.borrow_mut().take();
+ }
+ }
+
+ for (idx, _) in node.template.roots.iter().enumerate() {
+ match node.dynamic_root(idx) {
+ Some(DynamicNode::Component(c)) => self.drop_scope(c.scope.get().unwrap()),
+ Some(DynamicNode::Fragment(VFragment::NonEmpty(nodes))) => {
+ for node in *nodes {
+ self.drop_scope_inner(node);
+ }
+ }
+ _ => {}
+ }
+ }
}
}
+
impl ElementPath {
pub(crate) fn is_ascendant(&self, big: &&[u8]) -> bool {
match *self {
diff --git a/packages/core/src/bump_frame.rs b/packages/core/src/bump_frame.rs
index ca7ff66f6..581452836 100644
--- a/packages/core/src/bump_frame.rs
+++ b/packages/core/src/bump_frame.rs
@@ -2,9 +2,9 @@ use crate::factory::RenderReturn;
use bumpalo::Bump;
use std::cell::Cell;
-pub struct BumpFrame {
+pub(crate) struct BumpFrame {
pub bump: Bump,
- pub node: Cell<*mut RenderReturn<'static>>,
+ pub node: Cell<*const RenderReturn<'static>>,
}
impl BumpFrame {
@@ -12,17 +12,18 @@ impl BumpFrame {
let bump = Bump::with_capacity(capacity);
Self {
bump,
- node: Cell::new(std::ptr::null_mut()),
+ node: Cell::new(std::ptr::null()),
}
}
- pub fn reset(&mut self) {
- self.bump.reset();
- self.node.set(std::ptr::null_mut());
- }
-
/// Creates a new lifetime out of thin air
- pub unsafe fn load_node<'b>(&self) -> &'b RenderReturn<'b> {
- unsafe { std::mem::transmute(&*self.node.get()) }
+ pub unsafe fn try_load_node<'b>(&self) -> Option<&'b RenderReturn<'b>> {
+ let node = self.node.get();
+
+ if node.is_null() {
+ return None;
+ }
+
+ unsafe { std::mem::transmute(&*node) }
}
}
diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs
index fc7ab37c7..1a308a577 100644
--- a/packages/core/src/create.rs
+++ b/packages/core/src/create.rs
@@ -5,9 +5,9 @@ use crate::mutations::Mutation::*;
use crate::nodes::VNode;
use crate::nodes::{DynamicNode, TemplateNode};
use crate::virtual_dom::VirtualDom;
-use crate::{AttributeValue, ElementId, ScopeId, SuspenseContext, TemplateAttribute};
+use crate::{AttributeValue, ScopeId, SuspenseContext, TemplateAttribute};
-impl<'b: 'static> VirtualDom {
+impl<'b> VirtualDom {
/// Create a new template [`VNode`] and write it to the [`Mutations`] buffer.
///
/// This method pushes the ScopeID to the internal scopestack and returns the number of nodes created.
@@ -44,7 +44,14 @@ impl<'b: 'static> VirtualDom {
DynamicNode::Text(VText { id: slot, value }) => {
let id = self.next_element(template, template.template.node_paths[*id]);
slot.set(id);
- self.mutations.push(CreateTextNode { value, id });
+
+ // Safety: we promise not to re-alias this text later on after committing it to the mutation
+ let unbounded_text = unsafe { std::mem::transmute(*value) };
+ self.mutations.push(CreateTextNode {
+ value: unbounded_text,
+ id,
+ });
+
1
}
@@ -96,16 +103,24 @@ impl<'b: 'static> VirtualDom {
let attribute = template.dynamic_attrs.get(attr_id).unwrap();
attribute.mounted_element.set(id);
+ // Safety: we promise not to re-alias this text later on after committing it to the mutation
+ let unbounded_name = unsafe { std::mem::transmute(attribute.name) };
+
match &attribute.value {
- AttributeValue::Text(value) => self.mutations.push(SetAttribute {
- name: attribute.name,
- value: *value,
- ns: attribute.namespace,
- id,
- }),
+ AttributeValue::Text(value) => {
+ // Safety: we promise not to re-alias this text later on after committing it to the mutation
+ let unbounded_value = unsafe { std::mem::transmute(*value) };
+
+ self.mutations.push(SetAttribute {
+ name: unbounded_name,
+ value: unbounded_value,
+ ns: attribute.namespace,
+ id,
+ })
+ }
AttributeValue::Bool(value) => {
self.mutations.push(SetBoolAttribute {
- name: attribute.name,
+ name: unbounded_name,
value: *value,
id,
})
@@ -113,7 +128,7 @@ impl<'b: 'static> VirtualDom {
AttributeValue::Listener(_) => {
self.mutations.push(NewEventListener {
// all listeners start with "on"
- event_name: &attribute.name[2..],
+ event_name: &unbounded_name[2..],
scope: cur_scope,
id,
})
@@ -315,11 +330,14 @@ impl<'b: 'static> VirtualDom {
// Make sure the text node is assigned to the correct element
text.id.set(new_id);
+ // Safety: we promise not to re-alias this text later on after committing it to the mutation
+ let value = unsafe { std::mem::transmute(text.value) };
+
// Add the mutation to the list
self.mutations.push(HydrateText {
id: new_id,
path: &template.template.node_paths[idx][1..],
- value: text.value,
+ value,
});
// Since we're hydrating an existing node, we don't create any new nodes
@@ -356,18 +374,20 @@ impl<'b: 'static> VirtualDom {
}
}
- fn create_component_node(
+ pub(super) fn create_component_node(
&mut self,
template: &'b VNode<'b>,
component: &'b VComponent<'b>,
idx: usize,
) -> usize {
- let props = component.props.replace(None).unwrap();
+ let props = component
+ .props
+ .replace(None)
+ .expect("Props to always exist when a component is being created");
- let prop_ptr = unsafe { std::mem::transmute(props.as_ref()) };
- let scope = self.new_scope(prop_ptr).id;
+ let unbounded_props = unsafe { std::mem::transmute(props) };
- component.props.replace(Some(props));
+ let scope = self.new_scope(unbounded_props).id;
component.scope.set(Some(scope));
let return_nodes = unsafe { self.run_scope(scope).extend_lifetime_ref() };
@@ -375,10 +395,7 @@ impl<'b: 'static> VirtualDom {
use RenderReturn::*;
match return_nodes {
- Sync(Ok(t)) => {
- self.mount_component(scope, template, t, idx)
- // self.create(t)
- }
+ Sync(Ok(t)) => self.mount_component(scope, template, t, idx),
Sync(Err(_e)) => todo!("Propogate error upwards"),
Async(_) => self.mount_component_placeholder(template, idx, scope),
}
diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs
index 75141e002..669288ca7 100644
--- a/packages/core/src/diff.rs
+++ b/packages/core/src/diff.rs
@@ -13,39 +13,29 @@ use fxhash::{FxHashMap, FxHashSet};
use std::cell::Cell;
use DynamicNode::*;
-impl<'b: 'static> VirtualDom {
+impl<'b> VirtualDom {
pub fn diff_scope(&mut self, scope: ScopeId) {
let scope_state = &mut self.scopes[scope.0];
- // Load the old and new bump arenas
- let cur_arena = scope_state.current_frame();
- let prev_arena = scope_state.previous_frame();
-
- // Make sure the nodes arent null (they've been set properly)
- // This is a rough check to make sure we're not entering any UB
- assert_ne!(
- cur_arena.node.get(),
- std::ptr::null_mut(),
- "Call rebuild before diffing"
- );
- assert_ne!(
- prev_arena.node.get(),
- std::ptr::null_mut(),
- "Call rebuild before diffing"
- );
-
self.scope_stack.push(scope);
unsafe {
- let cur_arena = cur_arena.load_node();
- let prev_arena = prev_arena.load_node();
- self.diff_maybe_node(prev_arena, cur_arena);
+ // Load the old and new bump arenas
+ let old = scope_state
+ .previous_frame()
+ .try_load_node()
+ .expect("Call rebuild before diffing");
+ let new = scope_state
+ .current_frame()
+ .try_load_node()
+ .expect("Call rebuild before diffing");
+ self.diff_maybe_node(old, new);
}
self.scope_stack.pop();
}
- fn diff_maybe_node(&mut self, left: &'b RenderReturn<'b>, right: &'b RenderReturn<'b>) {
+ fn diff_maybe_node(&mut self, old: &'b RenderReturn<'b>, new: &'b RenderReturn<'b>) {
use RenderReturn::{Async, Sync};
- match (left, right) {
+ match (old, new) {
(Sync(Ok(l)), Sync(Ok(r))) => self.diff_node(l, r),
// Err cases
@@ -62,10 +52,10 @@ impl<'b: 'static> VirtualDom {
}
}
- fn diff_ok_to_err(&mut self, l: &'b VNode<'b>, e: &anyhow::Error) {
+ fn diff_ok_to_err(&mut self, _l: &'b VNode<'b>, _e: &anyhow::Error) {
todo!("Not yet handling error rollover")
}
- fn diff_err_to_ok(&mut self, e: &anyhow::Error, l: &'b VNode<'b>) {
+ fn diff_err_to_ok(&mut self, _e: &anyhow::Error, _l: &'b VNode<'b>) {
todo!("Not yet handling error rollover")
}
@@ -88,11 +78,13 @@ impl<'b: 'static> VirtualDom {
// todo: add more types of attribute values
match right_attr.value {
AttributeValue::Text(text) => {
+ let name = unsafe { std::mem::transmute(left_attr.name) };
+ let value = unsafe { std::mem::transmute(text) };
self.mutations.push(Mutation::SetAttribute {
id: left_attr.mounted_element.get(),
- name: left_attr.name,
- value: text,
ns: right_attr.namespace,
+ name,
+ value,
});
}
// todo: more types of attribute values
@@ -101,15 +93,18 @@ impl<'b: 'static> VirtualDom {
}
}
- for (left_node, right_node) in left_template
+ for (idx, (left_node, right_node)) in left_template
.dynamic_nodes
.iter()
.zip(right_template.dynamic_nodes.iter())
+ .enumerate()
{
match (left_node, right_node) {
(Text(left), Text(right)) => self.diff_vtext(left, right),
(Fragment(left), Fragment(right)) => self.diff_vfragment(left, right),
- (Component(left), Component(right)) => self.diff_vcomponent(left, right),
+ (Component(left), Component(right)) => {
+ self.diff_vcomponent(left, right, right_template, idx)
+ }
_ => self.replace(left_template, right_template),
};
}
@@ -124,24 +119,44 @@ impl<'b: 'static> VirtualDom {
}
}
- fn diff_vcomponent(&mut self, left: &'b VComponent<'b>, right: &'b VComponent<'b>) {
- // Due to how templates work, we should never get two different components. The only way we could enter
- // this codepath is through "light_diff", but we check there that the pointers are the same
- assert_eq!(left.render_fn, right.render_fn);
+ fn diff_vcomponent(
+ &mut self,
+ left: &'b VComponent<'b>,
+ right: &'b VComponent<'b>,
+ right_template: &'b VNode<'b>,
+ idx: usize,
+ ) {
+ // Replace components that have different render fns
+ if left.render_fn != right.render_fn {
+ dbg!(&left, &right);
+ let created = self.create_component_node(right_template, right, idx);
+ let head = unsafe {
+ self.scopes[left.scope.get().unwrap().0]
+ .root_node()
+ .extend_lifetime_ref()
+ };
+ let id = match head {
+ RenderReturn::Sync(Ok(node)) => self.replace_inner(node),
+ _ => todo!(),
+ };
+ self.mutations
+ .push(Mutation::ReplaceWith { id, m: created });
+ return;
+ }
// Make sure the new vcomponent has the right scopeid associated to it
let scope_id = left.scope.get().unwrap();
right.scope.set(Some(scope_id));
// copy out the box for both
- let old = left.props.replace(None).unwrap();
+ let old = self.scopes[scope_id.0].props.as_ref();
let new = right.props.replace(None).unwrap();
// If the props are static, then we try to memoize by setting the new with the old
// The target scopestate still has the reference to the old props, so there's no need to update anything
// This also implicitly drops the new props since they're not used
if left.static_props && unsafe { old.memoize(new.as_ref()) } {
- return right.props.set(Some(old));
+ return;
}
// If the props are dynamic *or* the memoization failed, then we need to diff the props
@@ -198,7 +213,8 @@ impl<'b: 'static> VirtualDom {
None => self.replace(left, right),
Some(components) => components
.into_iter()
- .for_each(|(l, r)| self.diff_vcomponent(l, r)),
+ .enumerate()
+ .for_each(|(idx, (l, r))| self.diff_vcomponent(l, r, right, idx)),
}
}
@@ -207,12 +223,12 @@ impl<'b: 'static> VirtualDom {
/// This just moves the ID of the old node over to the new node, and then sets the text of the new node if it's
/// different.
fn diff_vtext(&mut self, left: &'b VText<'b>, right: &'b VText<'b>) {
- right.id.set(left.id.get());
+ let id = left.id.get();
+
+ right.id.set(id);
if left.value != right.value {
- self.mutations.push(Mutation::SetText {
- id: left.id.get(),
- value: right.value,
- });
+ let value = unsafe { std::mem::transmute(right.value) };
+ self.mutations.push(Mutation::SetText { id, value });
}
}
@@ -234,22 +250,25 @@ impl<'b: 'static> VirtualDom {
}
fn replace_nodes_with_placeholder(&mut self, l: &'b [VNode<'b>], r: &'b Cell) {
+ // Create the placeholder first, ensuring we get a dedicated ID for the placeholder
+ let placeholder = self.next_element(&l[0], &[]);
+ r.set(placeholder);
+ self.mutations
+ .push(Mutation::CreatePlaceholder { id: placeholder });
+
// Remove the old nodes, except for onea
let first = self.replace_inner(&l[0]);
self.remove_nodes(&l[1..]);
- let placeholder = self.next_element(&l[0], &[]);
- r.set(placeholder);
-
- self.mutations
- .push(Mutation::CreatePlaceholder { id: placeholder });
self.mutations
.push(Mutation::ReplaceWith { id: first, m: 1 });
- self.reclaim(first);
+ self.try_reclaim(first);
}
- // Remove all the top-level nodes, returning the firstmost root ElementId
+ /// Remove all the top-level nodes, returning the firstmost root ElementId
+ ///
+ /// All IDs will be garbage collected
fn replace_inner(&mut self, node: &'b VNode<'b>) -> ElementId {
let id = match node.dynamic_root(0) {
None => node.root_ids[0].get(),
@@ -284,8 +303,13 @@ impl<'b: 'static> VirtualDom {
///
/// Simply walks through the dynamic nodes
fn clean_up_node(&mut self, node: &'b VNode<'b>) {
- for node in node.dynamic_nodes {
- match node {
+ for (idx, dyn_node) in node.dynamic_nodes.iter().enumerate() {
+ // Roots are cleaned up automatically?
+ if node.template.node_paths[idx].len() == 1 {
+ continue;
+ }
+
+ match dyn_node {
Component(comp) => {
let scope = comp.scope.get().unwrap();
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
@@ -596,10 +620,9 @@ impl<'b: 'static> VirtualDom {
// If none of the old keys are reused by the new children, then we remove all the remaining old children and
// create the new children afresh.
if shared_keys.is_empty() {
- if let Some(first_old) = old.get(0) {
+ if let Some(_) = old.get(0) {
self.remove_nodes(&old[1..]);
- let nodes_created = self.create_children(new);
- self.replace_node_with_on_stack(first_old, nodes_created);
+ self.replace_many(&old[0], new);
} else {
// I think this is wrong - why are we appending?
// only valid of the if there are no trailing elements
@@ -718,10 +741,6 @@ impl<'b: 'static> VirtualDom {
}
}
- fn replace_node_with_on_stack(&mut self, old: &'b VNode<'b>, m: usize) {
- todo!()
- }
-
/// Remove these nodes from the dom
/// Wont generate mutations for the inner nodes
fn remove_nodes(&mut self, nodes: &'b [VNode<'b>]) {
@@ -766,7 +785,7 @@ impl<'b: 'static> VirtualDom {
}
/// Push all the real nodes on the stack
- fn push_all_real_nodes(&mut self, node: &VNode) -> usize {
+ fn push_all_real_nodes(&mut self, node: &'b VNode<'b>) -> usize {
let mut onstack = 0;
for (idx, _) in node.template.roots.iter().enumerate() {
@@ -785,7 +804,12 @@ impl<'b: 'static> VirtualDom {
}
}
Some(Component(comp)) => {
- todo!()
+ let scope = comp.scope.get().unwrap();
+ onstack +=
+ match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
+ RenderReturn::Sync(Ok(node)) => self.push_all_real_nodes(node),
+ _ => todo!(),
+ }
}
None => {
self.mutations.push(Mutation::PushRoot {
@@ -815,7 +839,7 @@ impl<'b: 'static> VirtualDom {
self.mutations.push(Mutation::InsertAfter { id, m })
}
- fn find_first_element(&self, node: &VNode) -> ElementId {
+ fn find_first_element(&self, node: &'b VNode<'b>) -> ElementId {
match node.dynamic_root(0) {
None => node.root_ids[0].get(),
Some(Text(t)) => t.id.get(),
@@ -823,7 +847,7 @@ impl<'b: 'static> VirtualDom {
Some(Fragment(VFragment::Empty(t))) => t.get(),
Some(Component(comp)) => {
let scope = comp.scope.get().unwrap();
- match self.scopes[scope.0].root_node() {
+ match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
RenderReturn::Sync(Ok(t)) => self.find_first_element(t),
_ => todo!("cannot handle nonstandard nodes"),
}
@@ -831,7 +855,7 @@ impl<'b: 'static> VirtualDom {
}
}
- fn find_last_element(&self, node: &VNode) -> ElementId {
+ fn find_last_element(&self, node: &'b VNode<'b>) -> ElementId {
match node.dynamic_root(node.template.roots.len() - 1) {
None => node.root_ids.last().unwrap().get(),
Some(Text(t)) => t.id.get(),
@@ -839,7 +863,7 @@ impl<'b: 'static> VirtualDom {
Some(Fragment(VFragment::Empty(t))) => t.get(),
Some(Component(comp)) => {
let scope = comp.scope.get().unwrap();
- match self.scopes[scope.0].root_node() {
+ match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
RenderReturn::Sync(Ok(t)) => self.find_last_element(t),
_ => todo!("cannot handle nonstandard nodes"),
}
@@ -855,7 +879,18 @@ impl<'b: 'static> VirtualDom {
id: first,
m: created,
});
- self.reclaim(id);
+ self.try_reclaim(id);
+ }
+
+ fn replace_many(&mut self, left: &'b VNode<'b>, right: &'b [VNode<'b>]) {
+ let first = self.find_first_element(left);
+ let id = self.replace_inner(left);
+ let created = self.create_children(right);
+ self.mutations.push(Mutation::ReplaceWith {
+ id: first,
+ m: created,
+ });
+ self.try_reclaim(id);
}
}
@@ -883,7 +918,7 @@ fn matching_components<'a>(
_ => return None,
};
- (l.render_fn == r.render_fn).then(|| (l, r))
+ Some((l, r))
})
.collect()
}
@@ -894,6 +929,7 @@ fn matching_components<'a>(
/// - for text - we can use SetTextContent
/// - for clearning children we can use RemoveChildren
/// - for appending children we can use AppendChildren
+#[allow(dead_code)]
fn is_dyn_node_only_child(node: &VNode, idx: usize) -> bool {
let path = node.template.node_paths[idx];
diff --git a/packages/core/src/error_boundary.rs b/packages/core/src/error_boundary.rs
index c2a1947da..6f40c2484 100644
--- a/packages/core/src/error_boundary.rs
+++ b/packages/core/src/error_boundary.rs
@@ -1,29 +1,19 @@
-use std::{cell::RefCell, rc::Rc};
+use std::cell::RefCell;
-use crate::{ScopeId, ScopeState};
+use crate::ScopeId;
-pub struct ErrorContext {
+/// A boundary that will capture any errors from child components
+#[allow(dead_code)]
+pub struct ErrorBoundary {
error: RefCell