sycamore_web/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
//! # `sycamore-web`
//!
//! Web rendering backend for [`sycamore`](https://docs.rs/sycamore). This is already re-exported
//! in the main `sycamore` crate, so you should rarely need to use this crate directly.
//!
//! ## Feature flags
//!
//! - `hydrate` - Enables hydration support in DOM node. By default, hydration is disabled to reduce
//! binary size.
//!
//! - `suspense` - Enables suspense and resources support.
//!
//! - `wasm-bindgen-interning` (_default_) - Enables interning for `wasm-bindgen` strings. This
//! improves performance at a slight cost in binary size. If you want to minimize the size of the
//! resulting `.wasm` binary, you might want to disable this.
//!
//! ## Server Side Rendering
//!
//! This crate uses target detection to determine whether to use DOM or SSR as the rendering
//! backend. If the target arch is `wasm32`, DOM rendering will be used. Otherwise, SSR will be
//! used. Sometimes, this isn't desirable (e.g., if using server side wasm). To override this
//! behavior, you can set `--cfg sycamore_force_ssr` in your `RUSTFLAGS` environment variable when
//! compiling to force SSR mode even on `wasm32`.
// NOTE: Determining whether we are in SSR mode or not uses the cfg_ssr! and cfg_not_ssr! macros.
// For dependencies, we have to put in the conditions manually.
use std::borrow::Cow;
use std::cell::Cell;
use std::rc::Rc;
use sycamore_macro::*;
use sycamore_reactive::*;
use wasm_bindgen::prelude::*;
pub mod bind;
pub mod events;
#[doc(hidden)]
pub mod utils;
mod attributes;
mod components;
mod elements;
mod iter;
mod macros;
mod node;
mod noderef;
mod portal;
#[cfg(feature = "suspense")]
mod resource;
mod stable_counter;
#[cfg(feature = "suspense")]
mod suspense;
pub(crate) mod view;
pub use self::attributes::*;
pub use self::components::*;
pub use self::elements::*;
pub use self::iter::*;
pub use self::node::*;
pub use self::noderef::*;
pub use self::portal::*;
#[cfg(feature = "suspense")]
pub use self::resource::*;
pub use self::stable_counter::*;
#[cfg(feature = "suspense")]
pub use self::suspense::*;
pub use self::view::*;
/// We add this to make the macros from `sycamore-macro` work properly.
extern crate self as sycamore;
#[doc(hidden)]
pub mod rt {
pub use sycamore_core::*;
#[cfg(feature = "suspense")]
pub use sycamore_futures::*;
pub use sycamore_macro::*;
pub use sycamore_reactive::*;
#[allow(unused_imports)] // Needed for macro support.
pub use web_sys;
#[cfg(feature = "suspense")]
pub use crate::WrapAsync;
pub use crate::{bind, custom_element, tags, View};
}
/// Re-export of `js-sys` and `wasm-bindgen` for convenience.
//#[doc(no_inline)]
pub use {js_sys, wasm_bindgen};
/// A macro that expands to whether we are in SSR mode or not.
///
/// Can also be used with a block to only include the code inside the block if in SSR mode.
///
/// # Example
/// ```
/// # use sycamore_web::*;
/// # fn access_database() {}
/// if is_ssr!() {
/// println!("We are running on the server!");
/// }
///
/// is_ssr! {
/// // Access server only APIs in here.
/// let _ = access_database();
/// }
/// ```
#[macro_export]
macro_rules! is_ssr {
() => {
cfg!(any(not(target_arch = "wasm32"), sycamore_force_ssr))
};
($($tt:tt)*) => {
#[cfg(any(not(target_arch = "wasm32"), sycamore_force_ssr))]
{ $($tt)* }
};
}
/// A macro that expands to whether we are in DOM mode or not.
///
/// Can also be used with a block to only include the code inside the block if in DOM mode.
///
/// # Example
/// ```
/// # use sycamore_web::*;
/// if is_not_ssr!() {
/// console_log!("We are running in the browser!");
/// }
///
/// is_not_ssr! {
/// // Access browser only APIs in here.
/// let document = document();
/// }
/// ```
#[macro_export]
macro_rules! is_not_ssr {
() => {
!$crate::is_ssr!()
};
($($tt:tt)*) => {
#[cfg(all(target_arch = "wasm32", not(sycamore_force_ssr)))]
{ $($tt)* }
};
}
/// `macro_rules!` equivalent of [`cfg_ssr`]. This is to get around the limitation of not being
/// able to put proc-macros on `mod` items.
#[macro_export]
macro_rules! cfg_ssr_item {
($item:item) => {
#[cfg(any(not(target_arch = "wasm32"), sycamore_force_ssr))]
$item
};
}
/// `macro_rules!` equivalent of [`cfg_not_ssr`]. This is to get around the limitation of not being
/// able to put proc-macros on `mod` items.
#[macro_export]
macro_rules! cfg_not_ssr_item {
($item:item) => {
#[cfg(all(target_arch = "wasm32", not(sycamore_force_ssr)))]
$item
};
}
/// A type alias for the rendering backend.
#[cfg_ssr]
pub type HtmlNode = SsrNode;
/// A type alias for the rendering backend.
#[cfg_not_ssr]
#[cfg(not(feature = "hydrate"))]
pub type HtmlNode = DomNode;
/// A type alias for the rendering backend.
#[cfg_not_ssr]
#[cfg(feature = "hydrate")]
pub type HtmlNode = HydrateNode;
/// A type alias for [`Children`](sycamore_core::Children) automatically selecting the correct node
/// type.
pub type Children = sycamore_core::Children<View>;
/// Create a new effect, but only if we are not in SSR mode.
pub fn create_client_effect(f: impl FnMut() + 'static) {
if is_not_ssr!() {
create_effect(f);
}
}
/// Queue up a callback to be executed when the component is mounted.
///
/// If not on `wasm32` target, does nothing.
///
/// # Potential Pitfalls
///
/// If called inside an async-component, the callback will be called after the next suspension
/// point (when there is an `.await`).
pub fn on_mount(f: impl FnOnce() + 'static) {
if cfg!(target_arch = "wasm32") {
let is_alive = Rc::new(Cell::new(true));
on_cleanup({
let is_alive = Rc::clone(&is_alive);
move || is_alive.set(false)
});
let scope = use_current_scope();
let cb = move || {
if is_alive.get() {
scope.run_in(f);
}
};
queue_microtask(cb);
}
}
/// Alias for `queueMicrotask`.
pub fn queue_microtask(f: impl FnOnce() + 'static) {
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_name = "queueMicrotask")]
fn queue_microtask_js(f: &wasm_bindgen::JsValue);
}
queue_microtask_js(&Closure::once_into_js(f));
}
/// Utility function for accessing the global [`web_sys::Window`] object.
pub fn window() -> web_sys::Window {
web_sys::window().expect("no global `window` exists")
}
/// Utility function for accessing the global [`web_sys::Document`] object.
pub fn document() -> web_sys::Document {
thread_local! {
/// Cache for small performance improvement by preventing repeated calls to `window().document()`.
static DOCUMENT: web_sys::Document = window().document().expect("no `document` exists");
}
DOCUMENT.with(Clone::clone)
}