Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(backend): integrate stylist #114

Merged
merged 7 commits into from
Jul 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ jobs:
stellation-backend-tower
stellation-backend-cli
stellation-frontend
stellation-stylist
stctl
stellation
)
Expand Down
112 changes: 111 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 76 additions & 0 deletions crates/stellation-backend/src/hooks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//! Useful hooks for stellation backend.

use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;

use bounce::{use_atom_value, Atom};
use futures::future::LocalBoxFuture;
use futures::{Future, FutureExt};
use yew::prelude::*;

type RenderAppendHead = Box<dyn FnOnce() -> LocalBoxFuture<'static, String>>;

#[derive(Atom, Clone)]
pub(crate) struct HeadContents {
inner: Rc<RefCell<Vec<RenderAppendHead>>>,
}

impl Default for HeadContents {
fn default() -> Self {
panic!("Attempting to use use_append_head_content on client side rendering!");
}
}

impl PartialEq for HeadContents {
// We never set this atom, so it will always be equal.
fn eq(&self, _other: &Self) -> bool {
true
}
}

impl Eq for HeadContents {}

impl HeadContents {
pub(crate) fn new() -> Self {
Self {
inner: Rc::default(),
}
}

pub(crate) async fn render_into(&self, w: &mut dyn fmt::Write) {
for i in self.inner.take() {
let _ = write!(w, "{}", i().await);
}
}
}

/// A server-side hook that appends content to head element.
/// This async function is resolved after the page is completed rendered and the returned string is
/// appended at the location of the ` <!--%STELLATION_HEAD%-->` comment in `index.html`, after other
/// contents.
///
/// # Warning
///
/// The content is not managed at the client side.
/// This hook is used to facility specific crates such as a CSS-in-Rust solution.
///
/// If you wish to render content into the `<head>` element, you should use
/// [`bounce::helmet::Helmet`].
///
///
/// # Panics
///
/// This hook should be used by a server-side only component. Panics if used in client-side
/// rendering.
#[hook]
pub fn use_append_head_content<F, Fut>(f: F)
where
F: 'static + FnOnce() -> Fut,
Fut: 'static + Future<Output = String>,
{
let boxed_f: RenderAppendHead = Box::new(move || f().boxed_local());
let head_contents = use_atom_value::<HeadContents>();

head_contents.inner.borrow_mut().push(boxed_f);
}
1 change: 1 addition & 0 deletions crates/stellation-backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ mod request;
pub use request::{RenderRequest, Request};
mod renderer;
pub use renderer::ServerRenderer;
pub mod hooks;
mod html;
6 changes: 6 additions & 0 deletions crates/stellation-backend/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use stellation_bridge::links::{Link, PhantomLink};
use stellation_bridge::Bridge;
use yew::BaseComponent;

use crate::hooks::HeadContents;
use crate::request::RenderRequest;
use crate::root::{StellationRoot, StellationRootProps};
use crate::{html, ServerAppProps};
Expand Down Expand Up @@ -92,6 +93,8 @@ where
let request: Rc<_> = request.into();

if !request.is_client_only() {
let head_contents = HeadContents::new();

let (reader, writer) = render_static();

let props = ServerAppProps::from_request(request.clone());
Expand All @@ -101,6 +104,7 @@ where
server_app_props: props,
helmet_writer: writer,
bridge,
head_contents: head_contents.clone(),
},
)
.render()
Expand All @@ -111,6 +115,8 @@ where
&mut head_s,
r#"<meta name="stellation-mode" content="hydrate">"#
);

head_contents.render_into(&mut head_s).await;
}

html::format_html(request.template(), helmet_tags, head_s, body_s).await
Expand Down
5 changes: 5 additions & 0 deletions crates/stellation-backend/src/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use yew::prelude::*;
use yew_router::history::{AnyHistory, History, MemoryHistory};
use yew_router::Router;

use crate::hooks::HeadContents;
use crate::props::ServerAppProps;
use crate::Request;

Expand All @@ -21,6 +22,7 @@ where
pub helmet_writer: StaticWriter,
pub server_app_props: ServerAppProps<CTX, REQ>,
pub bridge: Option<Bridge<L>>,
pub head_contents: HeadContents,
}

impl<CTX, REQ, L> PartialEq for StellationRootProps<CTX, REQ, L>
Expand All @@ -43,6 +45,7 @@ where
helmet_writer: self.helmet_writer.clone(),
server_app_props: self.server_app_props.clone(),
bridge: self.bridge.clone(),
head_contents: self.head_contents.clone(),
}
}
}
Expand All @@ -59,12 +62,14 @@ where
helmet_writer,
server_app_props,
bridge,
head_contents,
..
} = props.clone();

let get_init_states = use_callback(
move |_, bridge| {
let mut states = AnyMap::new();
states.insert(head_contents.clone());
if let Some(m) = bridge.clone().map(BridgeState::from_bridge) {
states.insert(m);
}
Expand Down
28 changes: 28 additions & 0 deletions crates/stellation-stylist/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "stellation-stylist"
version = "0.2.0"
edition = "2021"
rust-version = "1.66"
repository = "https://github.com/futursolo/stellation"
authors = [
"Kaede Hoshiakwa <futursolo@icloud.com>",
]
description = "The framework experience for Yew."
keywords = ["web", "wasm", "yew", "framework", "ssr"]
categories = ["wasm", "web-programming"]
readme = "../../README.md"
homepage = "https://github.com/futursolo/stellation"
license = "MIT OR Apache-2.0"

[dependencies]
stylist = { version = "0.12.1", features = ["yew_integration"] }
yew = "0.20"
stellation-backend = { version = "0.2.0", path = "../stellation-backend", optional = true }

[features]
frontend = ["stylist/hydration"]
backend = ["stylist/ssr", "dep:stellation-backend"]

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "documenting"]
Loading