Merge branch 'upstream' into fix-non-str-attributes

This commit is contained in:
Evan Almloff committed 2022-12-28 20:36:57 -06:00
commit 044462876d
52 files changed
+3002 -2852

No files matched your search

+31 -5
View File
@@ -142,7 +142,8 @@ use std::{any::Any, borrow::BorrowMut, cell::Cell, collections::BTreeSet, future
/// }
/// ```
pub struct VirtualDom {
pub(crate) templates: FxHashMap<TemplateId, Template<'static>>,
// Maps a template path to a map of byteindexes to templates
pub(crate) templates: FxHashMap<TemplateId, FxHashMap<usize, Template<'static>>>,
pub(crate) scopes: Slab<Box<ScopeState>>,
pub(crate) dirty_scopes: BTreeSet<DirtyScope>,
pub(crate) scheduler: Rc<Scheduler>,
@@ -354,10 +355,11 @@ impl VirtualDom {
while let Some(el_ref) = parent_path {
// safety: we maintain references of all vnodes in the element slab
let template = unsafe { &*el_ref.template };
let node_template = template.template.get();
let target_path = el_ref.path;
for (idx, attr) in template.dynamic_attrs.iter().enumerate() {
let this_path = template.template.attr_paths[idx];
let this_path = node_template.attr_paths[idx];
// listeners are required to be prefixed with "on", but they come back to the virtualdom with that missing
// we should fix this so that we look for "onclick" instead of "click"
@@ -454,6 +456,30 @@ impl VirtualDom {
}
}
/// Replace a template at runtime. This will re-render all components that use this template.
/// This is the primitive that enables hot-reloading.
///
/// The caller must ensure that the template refrences the same dynamic attributes and nodes as the original template.
///
/// This will only replace the the parent template, not any nested templates.
pub fn replace_template(&mut self, template: Template<'static>) {
self.register_template_first_byte_index(template);
// iterating a slab is very inefficient, but this is a rare operation that will only happen during development so it's fine
for (_, scope) in &self.scopes {
if let Some(RenderReturn::Ready(sync)) = scope.try_root_node() {
if sync.template.get().name.rsplit_once(':').unwrap().0
== template.name.rsplit_once(':').unwrap().0
{
let height = scope.height;
self.dirty_scopes.insert(DirtyScope {
height,
id: scope.id,
});
}
}
}
}
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch.
///
/// The mutations item expects the RealDom's stack to be the root of the application.
@@ -477,7 +503,7 @@ impl VirtualDom {
pub fn rebuild(&mut self) -> Mutations {
match unsafe { self.run_scope(ScopeId(0)).extend_lifetime_ref() } {
// Rebuilding implies we append the created elements to the root
RenderReturn::Sync(Some(node)) => {
RenderReturn::Ready(node) => {
let m = self.create_scope(ScopeId(0), node);
self.mutations.edits.push(Mutation::AppendChildren {
id: ElementId(0),
@@ -485,8 +511,8 @@ impl VirtualDom {
});
}
// If an error occurs, we should try to render the default error component and context where the error occured
RenderReturn::Sync(None) => panic!("Cannot catch errors during rebuild"),
RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"),
RenderReturn::Aborted(_placeholder) => panic!("Cannot catch errors during rebuild"),
RenderReturn::Pending(_) => unreachable!("Root scope cannot be an async component"),
}
self.finalize()