mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 14:54:16 +00:00
149 lines
4.7 KiB
Rust
149 lines
4.7 KiB
Rust
use cfg_if::cfg_if;
|
|
use std::{cell::RefCell, fmt::Display};
|
|
|
|
cfg_if! {
|
|
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
|
use once_cell::unsync::Lazy as LazyCell;
|
|
use std::collections::HashMap;
|
|
use wasm_bindgen::JsCast;
|
|
|
|
// We can tell if we start in hydration mode by checking to see if the
|
|
// id "_0-1" is present in the DOM. If it is, we know we are hydrating from
|
|
// the server, if not, we are starting off in CSR
|
|
thread_local! {
|
|
static HYDRATION_COMMENTS: LazyCell<HashMap<String, web_sys::Comment>> = LazyCell::new(|| {
|
|
let document = crate::document();
|
|
let body = document.body().unwrap();
|
|
let walker = document
|
|
.create_tree_walker_with_what_to_show(&body, 128)
|
|
.unwrap();
|
|
let mut map = HashMap::new();
|
|
while let Ok(Some(node)) = walker.next_node() {
|
|
if let Some(content) = node.text_content() {
|
|
if let Some(hk) = content.strip_prefix("hk=") {
|
|
if let Some(hk) = hk.split('|').next() {
|
|
map.insert(hk.into(), node.unchecked_into());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
map
|
|
});
|
|
|
|
#[cfg(debug_assertions)]
|
|
pub(crate) static VIEW_MARKERS: LazyCell<HashMap<String, web_sys::Comment>> = LazyCell::new(|| {
|
|
let document = crate::document();
|
|
let body = document.body().unwrap();
|
|
let walker = document
|
|
.create_tree_walker_with_what_to_show(&body, 128)
|
|
.unwrap();
|
|
let mut map = HashMap::new();
|
|
while let Ok(Some(node)) = walker.next_node() {
|
|
if let Some(content) = node.text_content() {
|
|
if let Some(id) = content.strip_prefix("leptos-view|") {
|
|
map.insert(id.into(), node.unchecked_into());
|
|
}
|
|
}
|
|
}
|
|
map
|
|
});
|
|
|
|
static IS_HYDRATING: RefCell<LazyCell<bool>> = RefCell::new(LazyCell::new(|| {
|
|
#[cfg(debug_assertions)]
|
|
return crate::document().get_element_by_id("_0-1").is_some()
|
|
|| crate::document().get_element_by_id("_0-1o").is_some()
|
|
|| HYDRATION_COMMENTS.with(|comments| comments.get("_0-1o").is_some());
|
|
|
|
#[cfg(not(debug_assertions))]
|
|
return crate::document().get_element_by_id("_0-1").is_some()
|
|
|| HYDRATION_COMMENTS.with(|comments| comments.get("_0-1").is_some());
|
|
}));
|
|
}
|
|
|
|
pub(crate) fn get_marker(id: &str) -> Option<web_sys::Comment> {
|
|
HYDRATION_COMMENTS.with(|comments| comments.get(id).cloned())
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A stable identifier within the server-rendering or hydration process.
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
|
|
pub struct HydrationKey {
|
|
/// ID of the current key.
|
|
pub id: usize,
|
|
/// ID of the current fragment.
|
|
pub fragment: usize,
|
|
}
|
|
|
|
impl Display for HydrationKey {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}-{}", self.fragment, self.id)
|
|
}
|
|
}
|
|
|
|
thread_local!(static ID: RefCell<HydrationKey> = Default::default());
|
|
|
|
/// Control and utility methods for hydration.
|
|
pub struct HydrationCtx;
|
|
|
|
impl HydrationCtx {
|
|
/// Get the next `id` without incrementing it.
|
|
pub fn peek() -> HydrationKey {
|
|
ID.with(|id| *id.borrow())
|
|
}
|
|
|
|
/// Increments the current hydration `id` and returns it
|
|
pub fn id() -> HydrationKey {
|
|
ID.with(|id| {
|
|
let mut id = id.borrow_mut();
|
|
id.id = id.id.wrapping_add(1);
|
|
*id
|
|
})
|
|
}
|
|
|
|
/// Resets the hydration `id` for the next component, and returns it
|
|
pub fn next_component() -> HydrationKey {
|
|
ID.with(|id| {
|
|
let mut id = id.borrow_mut();
|
|
id.fragment = id.fragment.wrapping_add(1);
|
|
id.id = 0;
|
|
*id
|
|
})
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
|
pub fn reset_id() {
|
|
ID.with(|id| *id.borrow_mut() = Default::default());
|
|
}
|
|
|
|
/// Resumes hydration from the provided `id`. Useful for
|
|
/// `Suspense` and other fancy things.
|
|
pub fn continue_from(id: HydrationKey) {
|
|
ID.with(|i| *i.borrow_mut() = id);
|
|
}
|
|
|
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
|
pub(crate) fn stop_hydrating() {
|
|
IS_HYDRATING.with(|is_hydrating| {
|
|
std::mem::take(&mut *is_hydrating.borrow_mut());
|
|
})
|
|
}
|
|
|
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
|
pub(crate) fn is_hydrating() -> bool {
|
|
IS_HYDRATING.with(|is_hydrating| **is_hydrating.borrow())
|
|
}
|
|
|
|
pub(crate) fn to_string(id: &HydrationKey, closing: bool) -> String {
|
|
#[cfg(debug_assertions)]
|
|
return format!("_{id}{}", if closing { 'c' } else { 'o' });
|
|
|
|
#[cfg(not(debug_assertions))]
|
|
{
|
|
let _ = closing;
|
|
|
|
format!("_{id}")
|
|
}
|
|
}
|
|
}
|