dioxus/packages/ssr/src/cache.rs

193 lines
6.4 KiB
Rust
Raw Normal View History

2022-11-15 16:05:22 -08:00
use dioxus_core::prelude::*;
use std::fmt::Write;
2023-08-08 10:56:41 -07:00
use crate::renderer::{str_truthy, BOOL_ATTRS};
2022-12-07 15:29:32 -08:00
#[derive(Debug)]
2022-11-15 16:05:22 -08:00
pub struct StringCache {
pub segments: Vec<Segment>,
pub template: Template<'static>,
2022-11-15 16:05:22 -08:00
}
#[derive(Default)]
pub struct StringChain {
pub segments: Vec<Segment>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Segment {
Attr(usize),
Node(usize),
PreRendered(String),
2023-03-22 10:10:18 -05:00
/// A marker for where to insert a dynamic styles
StyleMarker {
// If the marker is inside a style tag or not
// This will be true if there are static styles
inside_style_tag: bool,
},
2023-04-07 09:35:32 -05:00
/// A marker for where to insert a dynamic inner html
InnerHtmlMarker,
/// A marker for where to insert a node id for an attribute
2023-12-27 10:17:18 -06:00
AttributeNodeMarker,
/// A marker for where to insert a node id for a root node
2023-12-27 10:17:18 -06:00
RootNodeMarker,
2022-11-15 16:05:22 -08:00
}
impl std::fmt::Write for StringChain {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
match self.segments.last_mut() {
Some(Segment::PreRendered(s2)) => s2.push_str(s),
_ => self.segments.push(Segment::PreRendered(s.to_string())),
}
Ok(())
}
}
impl StringCache {
pub fn from_template(template: &VNode, prerender: bool) -> Result<Self, std::fmt::Error> {
2022-11-15 16:05:22 -08:00
let mut chain = StringChain::default();
let mut cur_path = vec![];
2022-12-19 19:28:44 -06:00
for (root_idx, root) in template.template.get().roots.iter().enumerate() {
Self::recurse(root, &mut cur_path, root_idx, true, prerender, &mut chain)?;
2022-11-15 16:05:22 -08:00
}
Ok(Self {
segments: chain.segments,
2022-12-19 19:28:44 -06:00
template: template.template.get(),
2022-11-15 16:05:22 -08:00
})
}
fn recurse(
root: &TemplateNode,
cur_path: &mut Vec<usize>,
root_idx: usize,
is_root: bool,
prerender: bool,
2022-11-15 16:05:22 -08:00
chain: &mut StringChain,
) -> Result<(), std::fmt::Error> {
match root {
TemplateNode::Element {
tag,
attrs,
children,
..
} => {
cur_path.push(root_idx);
2023-01-27 20:35:46 -06:00
write!(chain, "<{tag}")?;
2023-03-22 10:10:18 -05:00
// we need to collect the styles and write them at the end
let mut styles = Vec::new();
2023-04-07 09:35:32 -05:00
// we need to collect the inner html and write it at the end
let mut inner_html = None;
// we need to keep track of if we have dynamic attrs to know if we need to insert a style and inner_html marker
2023-12-27 10:17:18 -06:00
let mut has_dyn_attrs = false;
2022-11-15 16:05:22 -08:00
for attr in *attrs {
match attr {
2023-03-22 10:10:18 -05:00
TemplateAttribute::Static {
name,
value,
namespace,
} => {
2023-04-07 09:35:32 -05:00
if *name == "dangerous_inner_html" {
inner_html = Some(value);
} else if let Some("style") = namespace {
2023-03-22 10:10:18 -05:00
styles.push((name, value));
2023-08-08 11:39:36 -07:00
} else if BOOL_ATTRS.contains(name) {
2023-08-08 10:56:41 -07:00
if str_truthy(value) {
write!(chain, " {name}=\"{value}\"",)?;
}
2023-03-22 10:10:18 -05:00
} else {
write!(chain, " {name}=\"{value}\"")?;
}
2022-11-15 16:05:22 -08:00
}
TemplateAttribute::Dynamic { id: index } => {
let index = *index;
chain.segments.push(Segment::Attr(index));
2023-12-27 10:17:18 -06:00
has_dyn_attrs = true
2022-11-15 16:05:22 -08:00
}
}
}
2023-03-22 10:10:18 -05:00
// write the styles
if !styles.is_empty() {
write!(chain, " style=\"")?;
for (name, value) in styles {
write!(chain, "{name}:{value};")?;
}
chain.segments.push(Segment::StyleMarker {
inside_style_tag: true,
});
write!(chain, "\"")?;
2023-12-27 10:17:18 -06:00
} else if has_dyn_attrs {
2023-03-22 10:10:18 -05:00
chain.segments.push(Segment::StyleMarker {
inside_style_tag: false,
});
}
// write the id if we are prerendering and this is either a root node or a node with a dynamic attribute
2023-12-27 10:17:18 -06:00
if prerender && (has_dyn_attrs || is_root) {
write!(chain, " data-node-hydration=\"")?;
2023-12-27 10:17:18 -06:00
if has_dyn_attrs {
chain.segments.push(Segment::AttributeNodeMarker);
} else if is_root {
2023-12-27 10:17:18 -06:00
chain.segments.push(Segment::RootNodeMarker);
}
write!(chain, "\"")?;
}
2022-12-06 17:44:29 -08:00
if children.is_empty() && tag_is_self_closing(tag) {
2022-11-15 16:05:22 -08:00
write!(chain, "/>")?;
} else {
write!(chain, ">")?;
2023-04-07 09:35:32 -05:00
// Write the static inner html, or insert a marker if dynamic inner html is possible
if let Some(inner_html) = inner_html {
chain.write_str(inner_html)?;
2023-12-27 10:17:18 -06:00
} else if has_dyn_attrs {
2023-04-07 09:35:32 -05:00
chain.segments.push(Segment::InnerHtmlMarker);
}
2022-11-15 16:05:22 -08:00
for child in *children {
Self::recurse(child, cur_path, root_idx, false, prerender, chain)?;
2022-11-15 16:05:22 -08:00
}
2023-01-27 20:35:46 -06:00
write!(chain, "</{tag}>")?;
2022-11-15 16:05:22 -08:00
}
cur_path.pop();
}
2023-02-14 08:44:01 -06:00
TemplateNode::Text { text } => {
write!(
chain,
"{}",
askama_escape::escape(text, askama_escape::Html)
)?;
}
TemplateNode::Dynamic { id: idx } | TemplateNode::DynamicText { id: idx } => {
2022-11-15 16:05:22 -08:00
chain.segments.push(Segment::Node(*idx))
}
}
Ok(())
}
}
fn tag_is_self_closing(tag: &str) -> bool {
2022-12-06 17:44:29 -08:00
matches!(
tag,
"area"
| "base"
| "br"
| "col"
| "embed"
| "hr"
| "img"
| "input"
| "link"
| "meta"
| "param"
| "source"
| "track"
| "wbr"
)
2022-11-15 16:05:22 -08:00
}