Improve compile times by splitting out rsx-hotreload from rsx (#2971)

* fix imports, migrate over rsx-hotreload
This commit is contained in:
Jonathan Kelley 2024-09-16 17:35:05 -07:00 committed by GitHub
parent b67122dd2a
commit 463e67cd12
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 188 additions and 190 deletions

21
Cargo.lock generated
View file

@ -2510,9 +2510,11 @@ dependencies = [
"dioxus-check",
"dioxus-cli-config",
"dioxus-core",
"dioxus-core-types",
"dioxus-hot-reload",
"dioxus-html",
"dioxus-rsx",
"dioxus-rsx-hotreload",
"dioxus-rsx-rosetta",
"dirs",
"env_logger 0.11.5",
@ -2792,6 +2794,7 @@ dependencies = [
"dioxus-core",
"dioxus-html",
"dioxus-rsx",
"dioxus-rsx-hotreload",
"dioxus-signals",
"execute",
"futures-util",
@ -2815,6 +2818,7 @@ dependencies = [
"dioxus",
"dioxus-core",
"dioxus-core-macro",
"dioxus-core-types",
"dioxus-hooks",
"dioxus-html-internal-macro",
"dioxus-rsx",
@ -3033,14 +3037,25 @@ dependencies = [
name = "dioxus-rsx"
version = "0.6.0-alpha.2"
dependencies = [
"dioxus-core",
"internment",
"prettier-please",
"prettyplease",
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"serde",
"syn 2.0.77",
]
[[package]]
name = "dioxus-rsx-hotreload"
version = "0.6.0-alpha.2"
dependencies = [
"dioxus-core",
"dioxus-core-types",
"dioxus-rsx",
"internment",
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn 2.0.77",
"tracing",
]

View file

@ -24,6 +24,7 @@ members = [
"packages/autofmt",
"packages/check",
"packages/rsx",
"packages/rsx-hotreload",
"packages/rsx-rosetta",
"packages/generational-box",
"packages/signals",
@ -87,6 +88,7 @@ dioxus-liveview = { path = "packages/liveview", version = "0.6.0-alpha.0" }
dioxus-autofmt = { path = "packages/autofmt", version = "0.6.0-alpha.0" }
dioxus-check = { path = "packages/check", version = "0.6.0-alpha.0" }
dioxus-rsx = { path = "packages/rsx", version = "0.6.0-alpha.0" }
dioxus-rsx-hotreload = { path = "packages/rsx-hotreload", version = "0.6.0-alpha.0" }
dioxus-rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.6.0-alpha.0" }
dioxus-signals = { path = "packages/signals", version = "0.6.0-alpha.0" }
dioxus-cli-config = { path = "packages/cli-config", version = "0.6.0-alpha.0", default-features = false}

View file

@ -121,7 +121,7 @@ impl Files {
fn enter_dir(&mut self, dir_id: usize) {
let path = &self.path_names[dir_id];
self.current_path = path.clone();
self.current_path.clone_from(path);
self.reload_path_list();
}

View file

@ -90,9 +90,11 @@ brotli = "6.0.0"
dioxus-autofmt = { workspace = true }
dioxus-check = { workspace = true }
dioxus-rsx-rosetta = { workspace = true }
dioxus-rsx = { workspace = true, features = ["serde"]}
dioxus-rsx = { workspace = true }
dioxus-rsx-hotreload = { workspace = true }
dioxus-html = { workspace = true, features = ["hot-reload-context"] }
dioxus-core = { workspace = true, features = ["serialize"] }
dioxus-core-types = { workspace = true }
dioxus-hot-reload = { workspace = true, features = ["serve"] }
ignore = "0.4.22"
env_logger = "0.11.3"

View file

@ -1,8 +1,7 @@
use dioxus_core::internal::{HotReloadTemplateWithLocation, HotReloadedTemplate};
use dioxus_rsx::{
hot_reload::{diff_rsx, ChangedRsx},
CallBody, HotReloadingContext,
};
use dioxus_core_types::HotReloadingContext;
use dioxus_rsx::CallBody;
use dioxus_rsx_hotreload::{diff_rsx, ChangedRsx};
use krates::cm::MetadataCommand;
use krates::Cmd;
pub use std::collections::HashMap;
@ -155,7 +154,7 @@ impl FileMap {
let template_location = template_location(old_start, file);
// Returns a list of templates that are hotreloadable
let hotreload_result = dioxus_rsx::hot_reload::HotReloadResult::new::<Ctx>(
let hotreload_result = dioxus_rsx_hotreload::HotReloadResult::new::<Ctx>(
&old_call_body.body,
&new_call_body.body,
template_location.clone(),

View file

@ -1,3 +1,5 @@
pub mod bubbles;
pub mod hr_context;
pub use bubbles::*;
pub use hr_context::*;

View file

@ -10,7 +10,8 @@ description = "Hot reloading utilities for Dioxus"
keywords = ["dom", "ui", "gui", "react", "hot-reloading"]
[dependencies]
dioxus-rsx = { workspace = true, features = ["serde"] }
dioxus-rsx = { workspace = true }
dioxus-rsx-hotreload = { workspace = true }
dioxus-core = { workspace = true, features = ["serialize"] }
dioxus-html = { workspace = true, optional = true }
dioxus-signals = { workspace = true, optional = true }

View file

@ -12,6 +12,7 @@ keywords = ["dom", "ui", "gui", "react"]
[dependencies]
dioxus-core = { workspace = true }
dioxus-core-macro = { workspace = true }
dioxus-core-types = { workspace = true }
dioxus-rsx = { workspace = true, optional = true }
dioxus-html-internal-macro = { workspace = true }
dioxus-hooks = { workspace = true }
@ -96,7 +97,7 @@ file-engine = [
]
wasm-bind = ["dep:web-sys", "dep:wasm-bindgen", "dep:wasm-bindgen-futures"]
native-bind = ["dep:tokio", "file-engine"]
hot-reload-context = ["dep:dioxus-rsx", "dioxus-rsx/hot_reload_traits"]
hot-reload-context = ["dep:dioxus-rsx"]
html-to-rsx = []
[package.metadata.docs.rs]

View file

@ -2,9 +2,9 @@
use dioxus_core::prelude::IntoAttributeValue;
use dioxus_core::HasAttributes;
use dioxus_html_internal_macro::impl_extension_attributes;
#[cfg(feature = "hot-reload-context")]
use dioxus_rsx::HotReloadingContext;
use dioxus_core_types::HotReloadingContext;
use dioxus_html_internal_macro::impl_extension_attributes;
#[cfg(feature = "hot-reload-context")]
use crate::{map_global_attributes, map_svg_attributes};

View file

@ -0,0 +1,15 @@
[package]
name = "dioxus-rsx-hotreload"
edition = "2021"
version.workspace = true
[dependencies]
dioxus-rsx = { workspace = true }
internment = { workspace = true }
proc-macro2 = { workspace = true, features = ["span-locations"] }
proc-macro2-diagnostics = { workspace = true }
quote = { workspace = true }
syn = { workspace = true, features = ["full", "extra-traits", "visit", "visit-mut"] }
tracing = { workspace = true }
dioxus-core = { workspace = true }
dioxus-core-types = { workspace = true }

View file

@ -63,14 +63,16 @@
//! The subproblem is optimal because the alternative is leaving less dynamic items for the remaining templates to hot reload which just makes it
//! more difficult to match future templates.
use crate::innerlude::*;
use crate::HotReloadingContext;
use dioxus_core::internal::{
FmtedSegments, HotReloadAttributeValue, HotReloadDynamicAttribute, HotReloadDynamicNode,
HotReloadLiteral, HotReloadedTemplate, NamedAttribute,
};
use dioxus_core_types::HotReloadingContext;
use dioxus_rsx::*;
use std::collections::HashMap;
use crate::extensions::{html_tag_and_namespace, intern, to_template_node};
use super::last_build_state::LastBuildState;
/// A result of hot reloading
@ -193,7 +195,7 @@ impl HotReloadResult {
let roots: Vec<_> = new
.roots
.iter()
.map(|node| node.to_template_node::<Ctx>())
.map(|node| to_template_node::<Ctx>(node))
.collect();
let roots: &[dioxus_core::TemplateNode] = intern(&*roots);
@ -589,7 +591,7 @@ impl HotReloadResult {
&mut self,
attribute: &Attribute,
) -> Option<()> {
let (tag, namespace) = attribute.html_tag_and_namespace::<Ctx>();
let (tag, namespace) = html_tag_and_namespace::<Ctx>(attribute);
// If the attribute is a spread, try to grab it from the last build
// If it wasn't in the last build with the same name, we can't hot reload it

View file

@ -0,0 +1,111 @@
use dioxus_core::TemplateNode;
use dioxus_core_types::HotReloadingContext;
use dioxus_rsx::*;
use internment::Intern;
use std::hash::Hash;
// interns a object into a static object, reusing the value if it already exists
pub(crate) fn intern<T: Eq + Hash + Send + Sync + ?Sized + 'static>(
s: impl Into<Intern<T>>,
) -> &'static T {
s.into().as_ref()
}
pub(crate) fn html_tag_and_namespace<Ctx: HotReloadingContext>(
attr: &Attribute,
) -> (&'static str, Option<&'static str>) {
let attribute_name_rust = attr.name.to_string();
let element_name = attr.el_name.as_ref().unwrap();
let rust_name = match element_name {
ElementName::Ident(i) => i.to_string(),
// If this is a web component, just use the name of the elements instead of mapping the attribute
// through the hot reloading context
ElementName::Custom(_) => return (intern(attribute_name_rust.as_str()), None),
};
Ctx::map_attribute(&rust_name, &attribute_name_rust)
.unwrap_or((intern(attribute_name_rust.as_str()), None))
}
pub fn to_template_attribute<Ctx: HotReloadingContext>(
attr: &Attribute,
) -> 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,
// or something sufficiently serializable
// (ie `checked`` being a bool and bools being interpretable)
//
// For now, just give up if that attribute doesn't exist in the mapping
if !attr.is_static_str_literal() {
let id = attr.dyn_idx.get();
return TemplateAttribute::Dynamic { id };
}
// Otherwise it's a static node and we can build it
let (_, value) = attr.as_static_str_literal().unwrap();
let (name, namespace) = html_tag_and_namespace::<Ctx>(attr);
TemplateAttribute::Static {
name,
namespace,
value: intern(value.to_static().unwrap().as_str()),
}
}
/// Convert this BodyNode into a TemplateNode.
///
/// dioxus-core uses this to understand templates at compiletime
pub fn to_template_node<Ctx: HotReloadingContext>(node: &BodyNode) -> dioxus_core::TemplateNode {
use dioxus_core::TemplateNode;
match node {
BodyNode::Element(el) => {
let rust_name = el.name.to_string();
let (tag, namespace) =
Ctx::map_element(&rust_name).unwrap_or((intern(rust_name.as_str()), None));
TemplateNode::Element {
tag,
namespace,
children: intern(
el.children
.iter()
.map(|c| to_template_node::<Ctx>(c))
.collect::<Vec<_>>(),
),
attrs: intern(
el.merged_attributes
.iter()
.map(|attr| to_template_attribute::<Ctx>(attr))
.collect::<Vec<_>>(),
),
}
}
BodyNode::Text(text) => text_to_template_node(text),
BodyNode::RawExpr(exp) => TemplateNode::Dynamic {
id: exp.dyn_idx.get(),
},
BodyNode::Component(comp) => TemplateNode::Dynamic {
id: comp.dyn_idx.get(),
},
BodyNode::ForLoop(floop) => TemplateNode::Dynamic {
id: floop.dyn_idx.get(),
},
BodyNode::IfChain(chain) => TemplateNode::Dynamic {
id: chain.dyn_idx.get(),
},
}
}
pub fn text_to_template_node(node: &TextNode) -> TemplateNode {
match node.input.to_static() {
Some(text) => TemplateNode::Text {
text: intern(text.as_str()),
},
None => TemplateNode::Dynamic {
id: node.dyn_idx.get(),
},
}
}

View file

@ -1,5 +1,5 @@
use crate::innerlude::*;
use dioxus_core::internal::{FmtSegment, FmtedSegments, HotReloadLiteral};
use dioxus_rsx::*;
use std::cell::Cell;
/// A pool of items we can grab from during hot reloading.

View file

@ -0,0 +1,9 @@
mod collect;
pub use collect::*;
mod diff;
pub use diff::*;
mod last_build_state;
mod extensions;

View file

@ -10,10 +10,9 @@ use dioxus_core::{
prelude::{Template, TemplateNode},
TemplateAttribute, VNode,
};
use dioxus_rsx::{
hot_reload::{self, diff_rsx, ChangedRsx, HotReloadResult},
CallBody, HotReloadingContext,
};
use dioxus_core_types::HotReloadingContext;
use dioxus_rsx::CallBody;
use dioxus_rsx_hotreload::{self, diff_rsx, ChangedRsx, HotReloadResult};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse::Parse, spanned::Spanned, token::Token, File};

View file

@ -1,4 +1,4 @@
use dioxus_rsx::hot_reload::diff_rsx;
use dioxus_rsx_hotreload::diff_rsx;
use syn::File;
macro_rules! assert_rsx_changed {

View file

@ -13,20 +13,13 @@ keywords = ["dom", "ui", "gui", "react"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
quote = { workspace = true }
proc-macro2 = { workspace = true, features = ["span-locations"] }
dioxus-core = { workspace = true, optional = true }
proc-macro2-diagnostics = { workspace = true }
quote = { workspace = true }
syn = { workspace = true, features = ["full", "extra-traits", "visit", "visit-mut"] }
serde = { workspace = true, features = ["derive"], optional = true }
internment = { version = "0.7.0", optional = true }
tracing = { workspace = true }
proc-macro2-diagnostics = { version = "0.10", default-features = false }
[features]
default = ["hot_reload"]
hot_reload_traits = []
hot_reload = ["dep:internment", "dep:dioxus-core", "hot_reload_traits", "serde"]
serde = ["dep:serde", "dioxus-core/serialize"]
default = []
[dev-dependencies]
prettyplease = { workspace = true }

View file

@ -166,52 +166,6 @@ 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(),
// If this is a web component, just use the name of the elements instead of mapping the attribute
// through the hot reloading context
ElementName::Custom(_) => return (intern(attribute_name_rust.as_str()), 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,
) -> 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,
// or something sufficiently serializable
// (ie `checked`` being a bool and bools being interpretable)
//
// For now, just give up if that attribute doesn't exist in the mapping
if !self.is_static_str_literal() {
let id = self.dyn_idx.get();
return TemplateAttribute::Dynamic { id };
}
// Otherwise it's a static node and we can build it
let (_, value) = self.as_static_str_literal().unwrap();
let (name, namespace) = self.html_tag_and_namespace::<Ctx>();
TemplateAttribute::Static {
name,
namespace,
value: intern(value.to_static().unwrap().as_str()),
}
}
pub fn rendered_as_dynamic_attr(&self) -> TokenStream2 {
// Shortcut out with spreads
if let AttributeName::Spread(_) = self.name {

View file

@ -253,7 +253,7 @@ impl Component {
}
// Iterate over the props of the component (without spreads, key, and custom attributes)
pub(crate) fn component_props(&self) -> impl Iterator<Item = &Attribute> {
pub fn component_props(&self) -> impl Iterator<Item = &Attribute> {
self.fields
.iter()
.filter(move |attr| !attr.name.is_likely_key())

View file

@ -1,17 +0,0 @@
#[cfg(feature = "hot_reload")]
mod collect;
#[cfg(feature = "hot_reload")]
pub use collect::*;
#[cfg(feature = "hot_reload_traits")]
mod context;
#[cfg(feature = "hot_reload_traits")]
pub use context::*;
#[cfg(feature = "hot_reload")]
mod diff;
#[cfg(feature = "hot_reload")]
pub use diff::*;
#[cfg(feature = "hot_reload")]
mod last_build_state;

View file

@ -78,11 +78,6 @@ pub use partial_closure::PartialClosure;
pub use rsx_call::*;
pub use template_body::TemplateBody;
pub mod hot_reload;
#[cfg(feature = "hot_reload_traits")]
pub use hot_reload::HotReloadingContext;
use quote::{quote, ToTokens, TokenStreamExt};
use syn::{
parse::{Parse, ParseStream},

View file

@ -126,52 +126,6 @@ impl ToTokens for BodyNode {
}
impl BodyNode {
/// Convert this BodyNode into a TemplateNode.
///
/// dioxus-core uses this to understand templates at compiletime
#[cfg(feature = "hot_reload")]
pub fn to_template_node<Ctx: crate::HotReloadingContext>(&self) -> dioxus_core::TemplateNode {
use dioxus_core::TemplateNode;
match self {
BodyNode::Element(el) => {
let rust_name = el.name.to_string();
let (tag, namespace) =
Ctx::map_element(&rust_name).unwrap_or((intern(rust_name.as_str()), None));
TemplateNode::Element {
tag,
namespace,
children: intern(
el.children
.iter()
.map(|c| c.to_template_node::<Ctx>())
.collect::<Vec<_>>(),
),
attrs: intern(
el.merged_attributes
.iter()
.map(|attr| attr.to_template_attribute::<Ctx>())
.collect::<Vec<_>>(),
),
}
}
BodyNode::Text(text) => text.to_template_node(),
BodyNode::RawExpr(exp) => TemplateNode::Dynamic {
id: exp.dyn_idx.get(),
},
BodyNode::Component(comp) => TemplateNode::Dynamic {
id: comp.dyn_idx.get(),
},
BodyNode::ForLoop(floop) => TemplateNode::Dynamic {
id: floop.dyn_idx.get(),
},
BodyNode::IfChain(chain) => TemplateNode::Dynamic {
id: chain.dyn_idx.get(),
},
}
}
pub fn get_dyn_idx(&self) -> usize {
match self {
BodyNode::Text(text) => text.dyn_idx.get(),

View file

@ -238,7 +238,7 @@ impl TemplateBody {
self.roots.is_empty()
}
pub(crate) fn implicit_key(&self) -> Option<&AttributeValue> {
pub fn implicit_key(&self) -> Option<&AttributeValue> {
match self.roots.first() {
Some(BodyNode::Element(el)) => el.key(),
Some(BodyNode::Component(comp)) => comp.get_key(),
@ -310,7 +310,7 @@ impl TemplateBody {
}
/// Iterate through the literal component properties of this rsx call in depth-first order
pub(crate) fn literal_component_properties(&self) -> impl Iterator<Item = &HotLiteral> + '_ {
pub fn literal_component_properties(&self) -> impl Iterator<Item = &HotLiteral> + '_ {
self.dynamic_nodes()
.filter_map(|node| {
if let BodyNode::Component(component) = node {

View file

@ -1,6 +1,3 @@
#[cfg(feature = "hot_reload")]
use dioxus_core::TemplateNode;
use crate::{literal::HotLiteral, location::DynIdx, HotReloadFormattedSegment, IfmtInput};
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::ToTokens;
@ -62,19 +59,6 @@ impl TextNode {
pub fn is_static(&self) -> bool {
self.input.is_static()
}
#[cfg(feature = "hot_reload")]
pub fn to_template_node(&self) -> TemplateNode {
use crate::intern;
match self.input.to_static() {
Some(text) => TemplateNode::Text {
text: intern(text.as_str()),
},
None => TemplateNode::Dynamic {
id: self.dyn_idx.get(),
},
}
}
}
#[cfg(test)]

View file

@ -1,8 +1,5 @@
#![allow(unused)]
#[cfg(feature = "hot_reload")]
use internment::Intern;
use proc_macro2::TokenStream as TokenStream2;
use std::{fmt::Debug, hash::Hash};
use syn::{
@ -11,14 +8,6 @@ use syn::{
Ident,
};
/// interns a object into a static object, reusing the value if it already exists
#[cfg(feature = "hot_reload")]
pub(crate) fn intern<T: Eq + Hash + Send + Sync + ?Sized + 'static>(
s: impl Into<Intern<T>>,
) -> &'static T {
s.into().as_ref()
}
/// Parse a raw ident and return a new ident with the r# prefix added
pub fn parse_raw_ident(parse_buffer: &ParseBuffer) -> syn::Result<Ident> {
// First try to parse as a normal ident

View file

@ -1 +0,0 @@
dioxus_core :: TemplateNode :: Element { tag : dioxus_elements :: elements :: circle :: TAG_NAME , namespace : dioxus_elements :: elements :: circle :: NAME_SPACE , attrs : & [dioxus_core :: TemplateAttribute :: Dynamic { id : 0usize } , dioxus_core :: TemplateAttribute :: Dynamic { id : 1usize } , dioxus_core :: TemplateAttribute :: Dynamic { id : 2usize } , dioxus_core :: TemplateAttribute :: Static { name : dioxus_elements :: elements :: circle :: stroke . 0 , namespace : dioxus_elements :: elements :: circle :: stroke . 1 , value : "green" , } , dioxus_core :: TemplateAttribute :: Static { name : dioxus_elements :: elements :: circle :: fill . 0 , namespace : dioxus_elements :: elements :: circle :: fill . 1 , value : "yellow" , } ,] , children : & [] , }

View file

@ -1,11 +0,0 @@
rsx! {
circle {
cx: 50,
cy: 50,
r: 40,
stroke: "green",
fill: "yellow"
}
}