Switch to a pool of dynamic values for hot reloading (#2705)

* create the dynamic value pool

* assign ids to dynamic formatted segments

* separate the rendering and literal pools

* rsx output compiles again

* more examples compiling with new rsx expansion

* update template body explanation

* all workspace examples compile

* fix formatted segments in keys

* start hot reload diffing

* fix component literal hot reloading

* start integrate new hot reloading with the CLI

* simple hot reloads working

* Fix hot reloading blocks with components

* implement hot reloading for if chains

* Fix hot reloading after a template requires a full rebuild

* Fix hot reloading any attribute values

* remove unsafe from hot reload utils

* Fix hot reloading empty rsx

* add more hot reloading tests

* reorganize hot reload module

* fix web hydration

* fix empty rsx nodes in autoformatting

* fix tests

* remove path sorting logic from core

* make template names more consistent in debug mode

* fix quote_as_hot_reload_literal for explicitly typed literals

* fix can_be_shorthand for string literals

* fix formatted single dynamic expression

* Fix usize component properties and playwright tests

* remove default implementation for TemplateBody

* add a bunch more comments for diffing, scoring and why this scoring system is optimal
This commit is contained in:
Evan Almloff authored and GitHub committed 2024-07-30 17:16:27 +00:00
1 parent f3ca1484a1
commit 34bdcd15cf
40 files changed
+2288 -1897

No files matched your search

+50 -21
View File
@@ -28,9 +28,6 @@ use syn::{
Block, Expr, ExprClosure, ExprIf, Ident, Lit, LitBool, LitFloat, LitInt, LitStr, Token,
};
#[cfg(feature = "hot_reload")]
use dioxus_core::prelude::TemplateAttribute;
/// A property value in the from of a `name: value` pair with an optional comma.
/// Note that the colon and value are optional in the case of shorthand attributes. We keep them around
/// to support "lossless" parsing in case that ever might be useful.
@@ -120,6 +117,16 @@ impl Attribute {
}
}
/// Set the dynamic index of this attribute
pub fn set_dyn_idx(&self, idx: usize) {
self.dyn_idx.set(idx);
}
/// Get the dynamic index of this attribute
pub fn get_dyn_idx(&self) -> usize {
self.dyn_idx.get()
}
pub fn span(&self) -> proc_macro2::Span {
self.name.span()
}
@@ -140,18 +147,15 @@ impl Attribute {
pub fn ifmt(&self) -> Option<&IfmtInput> {
match &self.value {
AttributeValue::AttrLiteral(lit) => match &lit.value {
HotLiteralType::Fmted(input) => Some(input),
_ => None,
},
AttributeValue::AttrLiteral(HotLiteral::Fmted(input)) => Some(input),
_ => None,
}
}
pub fn as_static_str_literal(&self) -> Option<(&AttributeName, &IfmtInput)> {
match &self.value {
AttributeValue::AttrLiteral(lit) => match &lit.value {
HotLiteralType::Fmted(input) if input.is_static() => Some((&self.name, input)),
AttributeValue::AttrLiteral(lit) => match &lit {
HotLiteral::Fmted(input) if input.is_static() => Some((&self.name, input)),
_ => None,
},
_ => None,
@@ -162,11 +166,27 @@ impl Attribute {
self.as_static_str_literal().is_some()
}
#[cfg(feature = "hot_reload")]
pub(crate) fn html_tag_and_namespace<Ctx: crate::HotReloadingContext>(
&self,
) -> (&'static str, Option<&'static str>) {
let attribute_name_rust = self.name.to_string();
let element_name = self.el_name.as_ref().unwrap();
let rust_name = match element_name {
ElementName::Ident(i) => i.to_string(),
ElementName::Custom(s) => return (intern(s.value()), None),
};
Ctx::map_attribute(&rust_name, &attribute_name_rust)
.unwrap_or((intern(attribute_name_rust.as_str()), None))
}
#[cfg(feature = "hot_reload")]
pub fn to_template_attribute<Ctx: crate::HotReloadingContext>(
&self,
rust_name: &str,
) -> TemplateAttribute {
) -> dioxus_core::TemplateAttribute {
use dioxus_core::TemplateAttribute;
// If it's a dynamic node, just return it
// For dynamic attributes, we need to check the mapping to see if that mapping exists
// todo: one day we could generate new dynamic attributes on the fly if they're a literal,
@@ -180,11 +200,8 @@ impl Attribute {
}
// Otherwise it's a static node and we can build it
let (_name, value) = self.as_static_str_literal().unwrap();
let attribute_name_rust = self.name.to_string();
let (name, namespace) = Ctx::map_attribute(rust_name, &attribute_name_rust)
.unwrap_or((intern(attribute_name_rust.as_str()), None));
let (_, value) = self.as_static_str_literal().unwrap();
let (name, namespace) = self.html_tag_and_namespace::<Ctx>();
TemplateAttribute::Static {
name,
@@ -303,8 +320,15 @@ impl Attribute {
return true;
}
if self.name.to_token_stream().to_string() == self.value.to_token_stream().to_string() {
return true;
// Or if it is a builtin attribute with a single ident value
if let (AttributeName::BuiltIn(name), AttributeValue::AttrExpr(expr)) =
(&self.name, &self.value)
{
if let Ok(Expr::Path(path)) = expr.as_expr() {
if path.path.get_ident() == Some(name) {
return true;
}
}
}
false
@@ -537,7 +561,11 @@ impl IfAttributeValue {
return non_string_diagnostic(current_if_value.span());
};
let HotLiteralType::Fmted(new) = &lit.value else {
let HotLiteral::Fmted(HotReloadFormattedSegment {
formatted_input: new,
..
}) = &lit
else {
return non_string_diagnostic(current_if_value.span());
};
@@ -554,8 +582,9 @@ impl IfAttributeValue {
}
// If the else value is a literal, then we need to append it to the expression and break
Some(AttributeValue::AttrLiteral(lit)) => {
if let HotLiteralType::Fmted(new) = &lit.value {
expression.extend(quote! { { #new.to_string() } });
if let HotLiteral::Fmted(new) = &lit {
let fmted = &new.formatted_input;
expression.extend(quote! { { #fmted.to_string() } });
break;
} else {
return non_string_diagnostic(current_if_value.span());