add hot reloading context trait
This commit is contained in:
1 parent
f16e3be2c2
commit
2131e5658b
4 files changed
+116
-32
No files matched your search
@@ -11,6 +11,8 @@
|
||||
//! - [ ] Keys
|
||||
//! - [ ] Properties spreading with with `..` syntax
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::*;
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
@@ -165,8 +167,9 @@ impl ToTokens for Component {
|
||||
}
|
||||
|
||||
if !self.children.is_empty() {
|
||||
let renderer = TemplateRenderer {
|
||||
let renderer: TemplateRenderer = TemplateRenderer {
|
||||
roots: &self.children,
|
||||
phantom: PhantomData,
|
||||
};
|
||||
|
||||
toks.append_all(quote! {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
pub trait HotReloadingContext {
|
||||
fn map_attribute(
|
||||
element_name_rust: &str,
|
||||
attribute_name_rust: &str,
|
||||
) -> Option<(&'static str, Option<&'static str>)>;
|
||||
fn map_element(element_name_rust: &str) -> Option<(&'static str, Option<&'static str>)>;
|
||||
}
|
||||
|
||||
pub struct Empty;
|
||||
|
||||
impl HotReloadingContext for Empty {
|
||||
fn map_attribute(_: &str, _: &str) -> Option<(&'static str, Option<&'static str>)> {
|
||||
None
|
||||
}
|
||||
|
||||
fn map_element(_: &str) -> Option<(&'static str, Option<&'static str>)> {
|
||||
None
|
||||
}
|
||||
}
|
||||
+87
-30
@@ -15,6 +15,7 @@
|
||||
mod errors;
|
||||
mod component;
|
||||
mod element;
|
||||
mod hot_reloading_context;
|
||||
mod ifmt;
|
||||
mod node;
|
||||
|
||||
@@ -22,6 +23,7 @@ mod node;
|
||||
pub use component::*;
|
||||
use dioxus_core::{Template, TemplateAttribute, TemplateNode};
|
||||
pub use element::*;
|
||||
use hot_reloading_context::{Empty, HotReloadingContext};
|
||||
pub use ifmt::*;
|
||||
pub use node::*;
|
||||
|
||||
@@ -35,24 +37,29 @@ use syn::{
|
||||
|
||||
/// Fundametnally, every CallBody is a template
|
||||
#[derive(Default)]
|
||||
pub struct CallBody {
|
||||
pub struct CallBody<Ctx: HotReloadingContext = Empty> {
|
||||
pub roots: Vec<BodyNode>,
|
||||
|
||||
// set this after
|
||||
pub inline_cx: bool,
|
||||
|
||||
phantom: std::marker::PhantomData<Ctx>,
|
||||
}
|
||||
|
||||
impl CallBody {
|
||||
impl<Ctx: HotReloadingContext> CallBody<Ctx> {
|
||||
/// This function intentionally leaks memory to create a static template.
|
||||
/// Keeping the template static allows us to simplify the core of dioxus and leaking memory in dev mode is less of an issue.
|
||||
/// the previous_location is the location of the previous template at the time the template was originally compiled.
|
||||
pub fn leak_template(&self, previous_location: &'static str) -> Template {
|
||||
let mut renderer = TemplateRenderer { roots: &self.roots };
|
||||
let mut renderer: TemplateRenderer<Ctx> = TemplateRenderer {
|
||||
roots: &self.roots,
|
||||
phantom: std::marker::PhantomData,
|
||||
};
|
||||
renderer.leak_template(previous_location)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for CallBody {
|
||||
impl<Ctx: HotReloadingContext> Parse for CallBody<Ctx> {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut roots = Vec::new();
|
||||
|
||||
@@ -69,14 +76,18 @@ impl Parse for CallBody {
|
||||
Ok(Self {
|
||||
roots,
|
||||
inline_cx: false,
|
||||
phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize the same way, regardless of flavor
|
||||
impl ToTokens for CallBody {
|
||||
impl<Ctx: HotReloadingContext> ToTokens for CallBody<Ctx> {
|
||||
fn to_tokens(&self, out_tokens: &mut TokenStream2) {
|
||||
let body = TemplateRenderer { roots: &self.roots };
|
||||
let body: TemplateRenderer<Ctx> = TemplateRenderer {
|
||||
roots: &self.roots,
|
||||
phantom: std::marker::PhantomData,
|
||||
};
|
||||
|
||||
if self.inline_cx {
|
||||
out_tokens.append_all(quote! {
|
||||
@@ -95,13 +106,14 @@ impl ToTokens for CallBody {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TemplateRenderer<'a> {
|
||||
pub struct TemplateRenderer<'a, Ctx: HotReloadingContext = Empty> {
|
||||
pub roots: &'a [BodyNode],
|
||||
phantom: std::marker::PhantomData<Ctx>,
|
||||
}
|
||||
|
||||
impl<'a> TemplateRenderer<'a> {
|
||||
impl<'a, Ctx: HotReloadingContext> TemplateRenderer<'a, Ctx> {
|
||||
fn leak_template(&mut self, previous_location: &'static str) -> Template<'static> {
|
||||
let mut context = DynamicContext::default();
|
||||
let mut context: DynamicContext<Ctx> = DynamicContext::default();
|
||||
|
||||
let roots: Vec<_> = self
|
||||
.roots
|
||||
@@ -138,9 +150,9 @@ impl<'a> TemplateRenderer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToTokens for TemplateRenderer<'a> {
|
||||
impl<'a, Ctx: HotReloadingContext> ToTokens for TemplateRenderer<'a, Ctx> {
|
||||
fn to_tokens(&self, out_tokens: &mut TokenStream2) {
|
||||
let mut context = DynamicContext::default();
|
||||
let mut context: DynamicContext<Ctx> = DynamicContext::default();
|
||||
|
||||
let key = match self.roots.get(0) {
|
||||
Some(BodyNode::Element(el)) if self.roots.len() == 1 => el.key.clone(),
|
||||
@@ -199,17 +211,31 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
|
||||
}
|
||||
// As we print out the dynamic nodes, we want to keep track of them in a linear fashion
|
||||
// We'll use the size of the vecs to determine the index of the dynamic node in the final
|
||||
#[derive(Default)]
|
||||
pub struct DynamicContext<'a> {
|
||||
pub struct DynamicContext<'a, Ctx: HotReloadingContext> {
|
||||
dynamic_nodes: Vec<&'a BodyNode>,
|
||||
dynamic_attributes: Vec<&'a ElementAttrNamed>,
|
||||
current_path: Vec<u8>,
|
||||
|
||||
node_paths: Vec<Vec<u8>>,
|
||||
attr_paths: Vec<Vec<u8>>,
|
||||
|
||||
phantom: std::marker::PhantomData<Ctx>,
|
||||
}
|
||||
|
||||
impl<'a> DynamicContext<'a> {
|
||||
impl<'a, Ctx: HotReloadingContext> Default for DynamicContext<'a, Ctx> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
dynamic_nodes: Vec::new(),
|
||||
dynamic_attributes: Vec::new(),
|
||||
current_path: Vec::new(),
|
||||
node_paths: Vec::new(),
|
||||
attr_paths: Vec::new(),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Ctx: HotReloadingContext> DynamicContext<'a, Ctx> {
|
||||
fn leak_node(&mut self, root: &'a BodyNode) -> TemplateNode<'static> {
|
||||
match root {
|
||||
BodyNode::Element(el) => {
|
||||
@@ -222,15 +248,24 @@ impl<'a> DynamicContext<'a> {
|
||||
// [0, 2]
|
||||
// [0, 2, 1]
|
||||
|
||||
let element_name_rust = el.name.to_string();
|
||||
|
||||
let static_attrs: Vec<_> = el
|
||||
.attributes
|
||||
.iter()
|
||||
.map(|attr| match &attr.attr {
|
||||
ElementAttr::AttrText { name: _, value } if value.is_static() => {
|
||||
ElementAttr::AttrText { name, value } if value.is_static() => {
|
||||
let value = value.source.as_ref().unwrap();
|
||||
let attribute_name_rust = name.to_string();
|
||||
let (name, namespace) =
|
||||
Ctx::map_attribute(&element_name_rust, &attribute_name_rust)
|
||||
.unwrap_or((
|
||||
Box::leak(attribute_name_rust.into_boxed_str()),
|
||||
None,
|
||||
));
|
||||
TemplateAttribute::Static {
|
||||
name: "todo",
|
||||
namespace: None,
|
||||
name,
|
||||
namespace,
|
||||
value: Box::leak(value.value().into_boxed_str()),
|
||||
// name: dioxus_elements::#el_name::#name.0,
|
||||
// namespace: dioxus_elements::#el_name::#name.1,
|
||||
@@ -277,15 +312,11 @@ impl<'a> DynamicContext<'a> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
// TemplateNode::Element {
|
||||
// tag: dioxus_elements::#el_name::TAG_NAME,
|
||||
// namespace: dioxus_elements::#el_name::NAME_SPACE,
|
||||
// attrs: &[ #attrs ],
|
||||
// children: &[ #children ],
|
||||
// }
|
||||
let (tag, namespace) = Ctx::map_element(&element_name_rust)
|
||||
.unwrap_or((Box::leak(element_name_rust.into_boxed_str()), None));
|
||||
TemplateNode::Element {
|
||||
tag: "todo",
|
||||
namespace: None,
|
||||
tag,
|
||||
namespace,
|
||||
attrs: Box::leak(static_attrs.into_boxed_slice()),
|
||||
children: Box::leak(children.into_boxed_slice()),
|
||||
}
|
||||
@@ -420,7 +451,7 @@ impl<'a> DynamicContext<'a> {
|
||||
#[test]
|
||||
fn template() {
|
||||
let input = quote! {
|
||||
div {
|
||||
svg {
|
||||
width: 100,
|
||||
height: "100px",
|
||||
"width2": 100,
|
||||
@@ -431,7 +462,33 @@ fn template() {
|
||||
(0..10).map(|i| rsx!{"{i}"})
|
||||
}
|
||||
};
|
||||
let call_body: CallBody = syn::parse2(input).unwrap();
|
||||
|
||||
struct Mock;
|
||||
|
||||
impl HotReloadingContext for Mock {
|
||||
fn map_attribute(
|
||||
element_name_rust: &str,
|
||||
attribute_name_rust: &str,
|
||||
) -> Option<(&'static str, Option<&'static str>)> {
|
||||
match element_name_rust {
|
||||
"svg" => match attribute_name_rust {
|
||||
"width" => Some(("width", Some("style"))),
|
||||
"height" => Some(("height", Some("style"))),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_element(element_name_rust: &str) -> Option<(&'static str, Option<&'static str>)> {
|
||||
match element_name_rust {
|
||||
"svg" => Some(("svg", Some("svg"))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let call_body: CallBody<Mock> = syn::parse2(input).unwrap();
|
||||
|
||||
let template = call_body.leak_template("testing");
|
||||
|
||||
@@ -442,13 +499,13 @@ fn template() {
|
||||
Template {
|
||||
name: "testing",
|
||||
roots: &[TemplateNode::Element {
|
||||
tag: "div",
|
||||
namespace: None,
|
||||
tag: "svg",
|
||||
namespace: Some("svg"),
|
||||
attrs: &[
|
||||
TemplateAttribute::Dynamic { id: 0 },
|
||||
TemplateAttribute::Static {
|
||||
name: "height",
|
||||
namespace: None,
|
||||
namespace: Some("style"),
|
||||
value: "100px",
|
||||
},
|
||||
TemplateAttribute::Dynamic { id: 1 },
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::*;
|
||||
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
@@ -141,7 +143,10 @@ impl ToTokens for BodyNode {
|
||||
pat, expr, body, ..
|
||||
} = exp;
|
||||
|
||||
let renderer = TemplateRenderer { roots: body };
|
||||
let renderer: TemplateRenderer = TemplateRenderer {
|
||||
roots: body,
|
||||
phantom: PhantomData,
|
||||
};
|
||||
|
||||
tokens.append_all(quote! {
|
||||
__cx.make_node(
|
||||
|
||||
Reference in new issue
Block a user