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
|
//! - [ ] Keys
|
||||||
//! - [ ] Properties spreading with with `..` syntax
|
//! - [ ] Properties spreading with with `..` syntax
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
@@ -165,8 +167,9 @@ impl ToTokens for Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !self.children.is_empty() {
|
if !self.children.is_empty() {
|
||||||
let renderer = TemplateRenderer {
|
let renderer: TemplateRenderer = TemplateRenderer {
|
||||||
roots: &self.children,
|
roots: &self.children,
|
||||||
|
phantom: PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
toks.append_all(quote! {
|
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 errors;
|
||||||
mod component;
|
mod component;
|
||||||
mod element;
|
mod element;
|
||||||
|
mod hot_reloading_context;
|
||||||
mod ifmt;
|
mod ifmt;
|
||||||
mod node;
|
mod node;
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ mod node;
|
|||||||
pub use component::*;
|
pub use component::*;
|
||||||
use dioxus_core::{Template, TemplateAttribute, TemplateNode};
|
use dioxus_core::{Template, TemplateAttribute, TemplateNode};
|
||||||
pub use element::*;
|
pub use element::*;
|
||||||
|
use hot_reloading_context::{Empty, HotReloadingContext};
|
||||||
pub use ifmt::*;
|
pub use ifmt::*;
|
||||||
pub use node::*;
|
pub use node::*;
|
||||||
|
|
||||||
@@ -35,24 +37,29 @@ use syn::{
|
|||||||
|
|
||||||
/// Fundametnally, every CallBody is a template
|
/// Fundametnally, every CallBody is a template
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct CallBody {
|
pub struct CallBody<Ctx: HotReloadingContext = Empty> {
|
||||||
pub roots: Vec<BodyNode>,
|
pub roots: Vec<BodyNode>,
|
||||||
|
|
||||||
// set this after
|
// set this after
|
||||||
pub inline_cx: bool,
|
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.
|
/// 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.
|
/// 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.
|
/// 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 {
|
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)
|
renderer.leak_template(previous_location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for CallBody {
|
impl<Ctx: HotReloadingContext> Parse for CallBody<Ctx> {
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
let mut roots = Vec::new();
|
let mut roots = Vec::new();
|
||||||
|
|
||||||
@@ -69,14 +76,18 @@ impl Parse for CallBody {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
roots,
|
roots,
|
||||||
inline_cx: false,
|
inline_cx: false,
|
||||||
|
phantom: std::marker::PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize the same way, regardless of flavor
|
/// 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) {
|
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 {
|
if self.inline_cx {
|
||||||
out_tokens.append_all(quote! {
|
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],
|
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> {
|
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
|
let roots: Vec<_> = self
|
||||||
.roots
|
.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) {
|
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) {
|
let key = match self.roots.get(0) {
|
||||||
Some(BodyNode::Element(el)) if self.roots.len() == 1 => el.key.clone(),
|
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
|
// 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
|
// 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, Ctx: HotReloadingContext> {
|
||||||
pub struct DynamicContext<'a> {
|
|
||||||
dynamic_nodes: Vec<&'a BodyNode>,
|
dynamic_nodes: Vec<&'a BodyNode>,
|
||||||
dynamic_attributes: Vec<&'a ElementAttrNamed>,
|
dynamic_attributes: Vec<&'a ElementAttrNamed>,
|
||||||
current_path: Vec<u8>,
|
current_path: Vec<u8>,
|
||||||
|
|
||||||
node_paths: Vec<Vec<u8>>,
|
node_paths: Vec<Vec<u8>>,
|
||||||
attr_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> {
|
fn leak_node(&mut self, root: &'a BodyNode) -> TemplateNode<'static> {
|
||||||
match root {
|
match root {
|
||||||
BodyNode::Element(el) => {
|
BodyNode::Element(el) => {
|
||||||
@@ -222,15 +248,24 @@ impl<'a> DynamicContext<'a> {
|
|||||||
// [0, 2]
|
// [0, 2]
|
||||||
// [0, 2, 1]
|
// [0, 2, 1]
|
||||||
|
|
||||||
|
let element_name_rust = el.name.to_string();
|
||||||
|
|
||||||
let static_attrs: Vec<_> = el
|
let static_attrs: Vec<_> = el
|
||||||
.attributes
|
.attributes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|attr| match &attr.attr {
|
.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 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 {
|
TemplateAttribute::Static {
|
||||||
name: "todo",
|
name,
|
||||||
namespace: None,
|
namespace,
|
||||||
value: Box::leak(value.value().into_boxed_str()),
|
value: Box::leak(value.value().into_boxed_str()),
|
||||||
// name: dioxus_elements::#el_name::#name.0,
|
// name: dioxus_elements::#el_name::#name.0,
|
||||||
// namespace: dioxus_elements::#el_name::#name.1,
|
// namespace: dioxus_elements::#el_name::#name.1,
|
||||||
@@ -277,15 +312,11 @@ impl<'a> DynamicContext<'a> {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// TemplateNode::Element {
|
let (tag, namespace) = Ctx::map_element(&element_name_rust)
|
||||||
// tag: dioxus_elements::#el_name::TAG_NAME,
|
.unwrap_or((Box::leak(element_name_rust.into_boxed_str()), None));
|
||||||
// namespace: dioxus_elements::#el_name::NAME_SPACE,
|
|
||||||
// attrs: &[ #attrs ],
|
|
||||||
// children: &[ #children ],
|
|
||||||
// }
|
|
||||||
TemplateNode::Element {
|
TemplateNode::Element {
|
||||||
tag: "todo",
|
tag,
|
||||||
namespace: None,
|
namespace,
|
||||||
attrs: Box::leak(static_attrs.into_boxed_slice()),
|
attrs: Box::leak(static_attrs.into_boxed_slice()),
|
||||||
children: Box::leak(children.into_boxed_slice()),
|
children: Box::leak(children.into_boxed_slice()),
|
||||||
}
|
}
|
||||||
@@ -420,7 +451,7 @@ impl<'a> DynamicContext<'a> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn template() {
|
fn template() {
|
||||||
let input = quote! {
|
let input = quote! {
|
||||||
div {
|
svg {
|
||||||
width: 100,
|
width: 100,
|
||||||
height: "100px",
|
height: "100px",
|
||||||
"width2": 100,
|
"width2": 100,
|
||||||
@@ -431,7 +462,33 @@ fn template() {
|
|||||||
(0..10).map(|i| rsx!{"{i}"})
|
(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");
|
let template = call_body.leak_template("testing");
|
||||||
|
|
||||||
@@ -442,13 +499,13 @@ fn template() {
|
|||||||
Template {
|
Template {
|
||||||
name: "testing",
|
name: "testing",
|
||||||
roots: &[TemplateNode::Element {
|
roots: &[TemplateNode::Element {
|
||||||
tag: "div",
|
tag: "svg",
|
||||||
namespace: None,
|
namespace: Some("svg"),
|
||||||
attrs: &[
|
attrs: &[
|
||||||
TemplateAttribute::Dynamic { id: 0 },
|
TemplateAttribute::Dynamic { id: 0 },
|
||||||
TemplateAttribute::Static {
|
TemplateAttribute::Static {
|
||||||
name: "height",
|
name: "height",
|
||||||
namespace: None,
|
namespace: Some("style"),
|
||||||
value: "100px",
|
value: "100px",
|
||||||
},
|
},
|
||||||
TemplateAttribute::Dynamic { id: 1 },
|
TemplateAttribute::Dynamic { id: 1 },
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||||
@@ -141,7 +143,10 @@ impl ToTokens for BodyNode {
|
|||||||
pat, expr, body, ..
|
pat, expr, body, ..
|
||||||
} = exp;
|
} = exp;
|
||||||
|
|
||||||
let renderer = TemplateRenderer { roots: body };
|
let renderer: TemplateRenderer = TemplateRenderer {
|
||||||
|
roots: body,
|
||||||
|
phantom: PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
tokens.append_all(quote! {
|
tokens.append_all(quote! {
|
||||||
__cx.make_node(
|
__cx.make_node(
|
||||||
|
|||||||
Reference in new issue
Block a user