Add a ton of comments to rsx/hotreload, add snapshot testing, refactor a bit to simplify the crate (#2130)

Merge dynamic context and dynamic mapping, clean up the rsx hotreload logic, and add location data to the Rsx objects
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2024-03-24 13:31:26 -07:00
1 parent b19a546c0a
commit eb79e61642
31 files changed
+1413 -811

No files matched your search

+126
View File
@@ -0,0 +1,126 @@
use crate::*;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
pub struct TemplateRenderer<'a> {
pub roots: &'a [BodyNode],
location: Option<String>,
}
impl<'a> TemplateRenderer<'a> {
/// Render the contents of the callbody out with a specific location
///
/// This will cascade location information down the tree if it already hasn't been set
pub fn as_tokens(roots: &'a [BodyNode], location: Option<String>) -> TokenStream2 {
TemplateRenderer::render(Self { roots, location })
}
fn render(mut self) -> TokenStream2 {
// Create a new dynamic context that tracks the state of all the dynamic nodes
// We have no old template, to seed it with, so it'll just be used for rendering
let mut context = DynamicContext::default();
// If we have an implicit key, then we need to write its tokens
let key_tokens = match self.implicit_key() {
Some(tok) => quote! { Some( #tok.to_string() ) },
None => quote! { None },
};
// Get the tokens we'll use as the ID of the template
// This follows the file:line:column:id format
let name = self.get_template_id_tokens();
// Render the static nodes, generating the mapping of dynamic
// This will modify the bodynodes in place - sorry about that
let roots = self.render_body_nodes(&mut context);
let dynamic_nodes = &context.dynamic_nodes;
let dyn_attr_printer = context
.dynamic_attributes
.iter()
.map(|attrs| AttributeType::merge_quote(attrs));
let node_paths = context.node_paths.iter().map(|it| quote!(&[#(#it),*]));
let attr_paths = context.attr_paths.iter().map(|it| quote!(&[#(#it),*]));
quote! {
static TEMPLATE: dioxus_core::Template = dioxus_core::Template {
name: #name,
roots: #roots,
node_paths: &[ #(#node_paths),* ],
attr_paths: &[ #(#attr_paths),* ],
};
{
// NOTE: Allocating a temporary is important to make reads within rsx drop before the value is returned
let __vnodes = dioxus_core::VNode::new(
#key_tokens,
TEMPLATE,
Box::new([ #( #dynamic_nodes),* ]),
Box::new([ #(#dyn_attr_printer),* ]),
);
__vnodes
}
}
}
fn get_template_id_tokens(&self) -> TokenStream2 {
match self.location {
Some(ref loc) => quote! { #loc },
None => {
// Get the root:column:id tag we'll use as the ID of the template
let root_col = self.get_root_col_id();
quote! {
concat!(
file!(),
":",
line!(),
":",
column!(),
":",
#root_col
)
}
}
}
}
fn get_root_col_id(&self) -> String {
let root_col = match self.roots.first() {
Some(first_root) => {
let first_root_span = format!("{:?}", first_root.span());
first_root_span
.rsplit_once("..")
.and_then(|(_, after)| after.split_once(')').map(|(before, _)| before))
.unwrap_or_default()
.to_string()
}
_ => "0".to_string(),
};
root_col
}
fn implicit_key(&self) -> Option<IfmtInput> {
let key = match self.roots.first() {
Some(BodyNode::Element(el)) if self.roots.len() == 1 => el.key.clone(),
Some(BodyNode::Component(comp)) if self.roots.len() == 1 => comp.key().cloned(),
_ => None,
};
key
}
/// Render a list of BodyNodes as a static array (&[...])
pub fn render_body_nodes(&mut self, context: &mut DynamicContext<'a>) -> TokenStream2 {
let root_printer = self
.roots
.iter()
.enumerate()
.map(|(idx, root)| context.render_children_nodes(idx, root));
// Render the static nodes, generating the mapping of dynamic
quote! {
&[ #( #root_printer ),* ]
}
}
}