mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
Fix imports when in SSR mode so we don't run wasm-bindgen code
This commit is contained in:
parent
fcae17eab7
commit
37ab7b34f9
4 changed files with 152 additions and 20 deletions
|
@ -33,6 +33,7 @@ hydrate = [
|
|||
"leptos_server/hydrate",
|
||||
]
|
||||
ssr = [
|
||||
"leptos_dom/ssr",
|
||||
"leptos_core/ssr",
|
||||
"leptos_macro/ssr",
|
||||
"leptos_reactive/ssr",
|
||||
|
|
|
@ -67,7 +67,7 @@ features = [
|
|||
]
|
||||
|
||||
[features]
|
||||
default = ["web"]
|
||||
default = []
|
||||
web = ["leptos_reactive/csr", "leptos/csr"]
|
||||
ssr = ["leptos_reactive/ssr", "leptos/ssr"]
|
||||
stable = ["leptos_reactive/stable"]
|
||||
|
|
|
@ -24,12 +24,15 @@ pub use components::*;
|
|||
pub use events::typed as ev;
|
||||
pub use helpers::*;
|
||||
pub use html::*;
|
||||
pub use js_sys;
|
||||
use hydration::HydrationCtx;
|
||||
use leptos_reactive::Scope;
|
||||
pub use logging::*;
|
||||
pub use node_ref::*;
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
use smallvec::SmallVec;
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
pub use ssr::*;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
use std::cell::LazyCell;
|
||||
use std::{borrow::Cow, fmt};
|
||||
|
@ -507,23 +510,6 @@ where
|
|||
std::mem::forget(disposer);
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
/// Runs the given function and renders it's result to a string.
|
||||
pub fn render_to_string<F, N>(f: F) -> String
|
||||
where
|
||||
F: FnOnce(Scope) -> N + 'static,
|
||||
N: IntoView,
|
||||
{
|
||||
let runtime = leptos_reactive::create_runtime();
|
||||
|
||||
let view = leptos_reactive::run_scope(runtime, |cx| f(cx).into_view(cx));
|
||||
|
||||
HydrationCtx::reset_id();
|
||||
runtime.dispose();
|
||||
|
||||
view.render_to_string().into_owned()
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
pub(crate) static WINDOW: web_sys::Window = web_sys::window().unwrap_throw();
|
||||
|
||||
|
|
|
@ -1,13 +1,158 @@
|
|||
#![cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
|
||||
use crate::{CoreComponent, HydrationCtx, View};
|
||||
use crate::{CoreComponent, HydrationCtx, IntoView, View};
|
||||
use cfg_if::cfg_if;
|
||||
use futures::{Stream, StreamExt, stream::FuturesUnordered};
|
||||
use itertools::Itertools;
|
||||
use std::borrow::Cow;
|
||||
use leptos_reactive::*;
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
/// Renders the given function to a static HTML string.
|
||||
///
|
||||
/// ```
|
||||
/// # cfg_if::cfg_if! { if #[cfg(not(any(feature = "csr", feature = "hydrate")))] {
|
||||
/// # use leptos_reactive::*; use leptos_dom::*; use leptos_macro::view;
|
||||
/// let html = render_to_string(|cx| view! { cx,
|
||||
/// <p>"Hello, world!"</p>
|
||||
/// });
|
||||
/// assert_eq!(html, r#"<p>Hello, world!</p>"#);
|
||||
/// # }}
|
||||
/// ```
|
||||
pub fn render_to_string<F, N>(f: F) -> String
|
||||
where
|
||||
F: FnOnce(Scope) -> N + 'static,
|
||||
N: IntoView,
|
||||
{
|
||||
let runtime = leptos_reactive::create_runtime();
|
||||
|
||||
let view = leptos_reactive::run_scope(runtime, |cx| f(cx).into_view(cx));
|
||||
|
||||
HydrationCtx::reset_id();
|
||||
runtime.dispose();
|
||||
|
||||
view.render_to_string().into_owned()
|
||||
}
|
||||
|
||||
/// Renders a function to a stream of HTML strings.
|
||||
///
|
||||
/// This renders:
|
||||
/// 1) the application shell
|
||||
/// a) HTML for everything that is not under a `<Suspense/>`,
|
||||
/// b) the `fallback` for any `<Suspense/>` component that is not already resolved, and
|
||||
/// c) JavaScript necessary to receive streaming [Resource](leptos_reactive::Resource) data.
|
||||
/// 2) streaming [Resource](leptos_reactive::Resource) data. Resources begin loading on the
|
||||
/// server and are sent down to the browser to resolve. On the browser, if the app sees that
|
||||
/// it is waiting for a resource to resolve from the server, it doesn't run it initially.
|
||||
/// 3) HTML fragments to replace each `<Suspense/>` fallback with its actual data as the resources
|
||||
/// read under that `<Suspense/>` resolve.
|
||||
pub fn render_to_stream(view: impl FnOnce(Scope) -> View + 'static) -> impl Stream<Item = String> {
|
||||
render_to_stream_with_prefix(view, |_| "".into())
|
||||
}
|
||||
|
||||
/// Renders a function to a stream of HTML strings. After the `view` runs, the `prefix` will run with
|
||||
/// the same scope. This can be used to generate additional HTML that has access to the same `Scope`.
|
||||
///
|
||||
/// This renders:
|
||||
/// 1) the prefix
|
||||
/// 2) the application shell
|
||||
/// a) HTML for everything that is not under a `<Suspense/>`,
|
||||
/// b) the `fallback` for any `<Suspense/>` component that is not already resolved, and
|
||||
/// c) JavaScript necessary to receive streaming [Resource](leptos_reactive::Resource) data.
|
||||
/// 3) streaming [Resource](leptos_reactive::Resource) data. Resources begin loading on the
|
||||
/// server and are sent down to the browser to resolve. On the browser, if the app sees that
|
||||
/// it is waiting for a resource to resolve from the server, it doesn't run it initially.
|
||||
/// 4) HTML fragments to replace each `<Suspense/>` fallback with its actual data as the resources
|
||||
/// read under that `<Suspense/>` resolve.
|
||||
pub fn render_to_stream_with_prefix(
|
||||
view: impl FnOnce(Scope) -> View + 'static,
|
||||
prefix: impl FnOnce(Scope) -> Cow<'static, str> + 'static
|
||||
) -> impl Stream<Item = String> {
|
||||
// create the runtime
|
||||
let runtime = create_runtime();
|
||||
|
||||
let ((shell, prefix, pending_resources, pending_fragments, serializers), _, disposer) =
|
||||
run_scope_undisposed(runtime, {
|
||||
move |cx| {
|
||||
// the actual app body/template code
|
||||
// this does NOT contain any of the data being loaded asynchronously in resources
|
||||
let shell = view(cx).render_to_string();
|
||||
|
||||
let resources = cx.all_resources();
|
||||
let pending_resources = serde_json::to_string(&resources).unwrap();
|
||||
let prefix = prefix(cx);
|
||||
|
||||
(
|
||||
shell,
|
||||
prefix,
|
||||
pending_resources,
|
||||
cx.pending_fragments(),
|
||||
cx.serialization_resolvers(),
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
let fragments = FuturesUnordered::new();
|
||||
for (fragment_id, fut) in pending_fragments {
|
||||
fragments.push(async move { (fragment_id, fut.await) })
|
||||
}
|
||||
|
||||
// resources and fragments
|
||||
let resources_and_fragments = futures::stream::select(
|
||||
// stream data for each Resource as it resolves
|
||||
serializers.map(|(id, json)| {
|
||||
let id = serde_json::to_string(&id).unwrap();
|
||||
format!(
|
||||
r#"<script>
|
||||
if(__LEPTOS_RESOURCE_RESOLVERS.get({id})) {{
|
||||
__LEPTOS_RESOURCE_RESOLVERS.get({id})({json:?})
|
||||
}} else {{
|
||||
__LEPTOS_RESOLVED_RESOURCES.set({id}, {json:?});
|
||||
}}
|
||||
</script>"#,
|
||||
)
|
||||
}),
|
||||
// stream HTML for each <Suspense/> as it resolves
|
||||
fragments.map(|(fragment_id, html)| {
|
||||
format!(
|
||||
r#"
|
||||
<template id="{fragment_id}">{html}</template>
|
||||
<script>
|
||||
var frag = document.querySelector(`[data-fragment-id="{fragment_id}"]`);
|
||||
var tpl = document.getElementById("{fragment_id}");
|
||||
if(frag) frag.replaceWith(tpl.content.cloneNode(true));
|
||||
</script>
|
||||
"#
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
// HTML for the view function and script to store resources
|
||||
futures::stream::once(async move {
|
||||
format!(
|
||||
r#"
|
||||
{prefix}
|
||||
{shell}
|
||||
<script>
|
||||
__LEPTOS_PENDING_RESOURCES = {pending_resources};
|
||||
__LEPTOS_RESOLVED_RESOURCES = new Map();
|
||||
__LEPTOS_RESOURCE_RESOLVERS = new Map();
|
||||
</script>
|
||||
"#
|
||||
)
|
||||
})
|
||||
.chain(resources_and_fragments)
|
||||
// dispose of Scope and Runtime
|
||||
.chain(futures::stream::once(async move {
|
||||
disposer.dispose();
|
||||
//runtime.dispose();
|
||||
Default::default()
|
||||
}))
|
||||
}
|
||||
|
||||
impl View {
|
||||
/// Consumes the node and renders it into an HTML string.
|
||||
pub(crate) fn render_to_string(self) -> Cow<'static, str> {
|
||||
pub fn render_to_string(self) -> Cow<'static, str> {
|
||||
match self {
|
||||
View::Text(node) => node.content,
|
||||
View::Component(node) => {
|
||||
|
|
Loading…
Reference in a new issue