mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-23 20:53:06 +00:00
parse and inject pre rendered content to work with trunk
This commit is contained in:
parent
f96425e425
commit
9877dd7ed8
5 changed files with 132 additions and 85 deletions
|
@ -70,23 +70,51 @@ where
|
|||
}
|
||||
|
||||
fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
||||
self,
|
||||
mut self,
|
||||
server_fn_route: &'static str,
|
||||
cfg: impl Into<ServeConfig<P>>,
|
||||
) -> Self {
|
||||
use tower_http::services::ServeDir;
|
||||
use tower_http::services::{ServeDir, ServeFile};
|
||||
|
||||
let cfg = cfg.into();
|
||||
|
||||
// Serve the dist folder and the index.html file
|
||||
let serve_dir = ServeDir::new(cfg.assets_path);
|
||||
|
||||
self.register_server_fns(server_fn_route)
|
||||
.nest_service("/assets", serve_dir)
|
||||
.route_service(
|
||||
"/",
|
||||
get(render_handler).with_state((cfg, SSRState::default())),
|
||||
// Serve all files in dist folder except index.html
|
||||
let dir = std::fs::read_dir(cfg.assets_path).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Couldn't read assets directory at {:?}: {}",
|
||||
&cfg.assets_path, e
|
||||
)
|
||||
});
|
||||
|
||||
for entry in dir.flatten() {
|
||||
let path = entry.path();
|
||||
if path.ends_with("index.html") {
|
||||
continue;
|
||||
}
|
||||
let route = path
|
||||
.strip_prefix(&cfg.assets_path)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|segment| {
|
||||
segment.to_str().unwrap_or_else(|| {
|
||||
panic!("Failed to convert path segment {:?} to string", segment)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("/");
|
||||
let route = format!("/{}", route);
|
||||
if path.is_dir() {
|
||||
self = self.nest_service(&route, ServeDir::new(path));
|
||||
} else {
|
||||
self = self.nest_service(&route, ServeFile::new(path));
|
||||
}
|
||||
}
|
||||
|
||||
// Add server functions and render index.html
|
||||
self.register_server_fns(server_fn_route).route(
|
||||
"/",
|
||||
get(render_handler).with_state((cfg, SSRState::default())),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,24 +50,50 @@ impl DioxusRouterExt for Router {
|
|||
}
|
||||
|
||||
fn register_server_fns(self, server_fn_route: &'static str) -> Self {
|
||||
self.register_server_fns_with_handler(|| ServerFnHandler {
|
||||
self.register_server_fns_with_handler(server_fn_route, |func| ServerFnHandler {
|
||||
server_context: DioxusServerContext::default(),
|
||||
function: func,
|
||||
})
|
||||
}
|
||||
|
||||
fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
||||
self,
|
||||
mut self,
|
||||
server_fn_route: &'static str,
|
||||
cfg: impl Into<ServeConfig<P>>,
|
||||
) -> Self {
|
||||
let cfg = cfg.into();
|
||||
// Serve the dist folder and the index.html file
|
||||
let serve_dir = StaticDir::new([cfg.assets_path]);
|
||||
|
||||
// Serve all files in dist folder except index.html
|
||||
let dir = std::fs::read_dir(cfg.assets_path).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Couldn't read assets directory at {:?}: {}",
|
||||
&cfg.assets_path, e
|
||||
)
|
||||
});
|
||||
|
||||
for entry in dir.flatten() {
|
||||
let path = entry.path();
|
||||
if path.ends_with("index.html") {
|
||||
continue;
|
||||
}
|
||||
let serve_dir = StaticDir::new([path.clone()]);
|
||||
let route = path
|
||||
.strip_prefix(&cfg.assets_path)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|segment| {
|
||||
segment.to_str().unwrap_or_else(|| {
|
||||
panic!("Failed to convert path segment {:?} to string", segment)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("/");
|
||||
let route = format!("/{}/<**path>", route);
|
||||
self = self.push(Router::with_path(route).get(serve_dir))
|
||||
}
|
||||
|
||||
self.register_server_fns(server_fn_route)
|
||||
.push(Router::with_path("/").get(SSRHandler { cfg }))
|
||||
.push(Router::with_path("assets/<**path>").get(serve_dir))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ pub fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
|||
.and(warp::get())
|
||||
.and(with_ssr_state())
|
||||
.map(move |renderer: SSRState| warp::reply::html(renderer.render(&cfg))))
|
||||
.or(warp::path("assets").and(serve_dir))
|
||||
.or(serve_dir)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use std::fmt::Write;
|
||||
use std::sync::Arc;
|
||||
|
||||
use dioxus_core::VirtualDom;
|
||||
|
@ -47,12 +46,7 @@ impl Default for SSRState {
|
|||
impl SSRState {
|
||||
pub fn render<P: 'static + Clone>(&self, cfg: &ServeConfig<P>) -> String {
|
||||
let ServeConfig {
|
||||
app,
|
||||
application_name,
|
||||
base_path,
|
||||
head,
|
||||
props,
|
||||
..
|
||||
app, props, index, ..
|
||||
} = cfg;
|
||||
|
||||
let mut vdom = VirtualDom::new_with_props(*app, props.clone());
|
||||
|
@ -63,38 +57,11 @@ impl SSRState {
|
|||
|
||||
let mut html = String::new();
|
||||
|
||||
let result = write!(
|
||||
&mut html,
|
||||
r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>{head}
|
||||
</head><body>
|
||||
<div id="main">"#
|
||||
);
|
||||
|
||||
if let Err(err) = result {
|
||||
eprintln!("Failed to write to html: {}", err);
|
||||
}
|
||||
html += &index.pre_main;
|
||||
|
||||
let _ = renderer.render_to(&mut html, &vdom);
|
||||
|
||||
if let Err(err) = write!(
|
||||
&mut html,
|
||||
r#"</div>
|
||||
<script type="module">
|
||||
import init from "/{base_path}/assets/dioxus/{application_name}.js";
|
||||
init("/{base_path}/assets/dioxus/{application_name}_bg.wasm").then(wasm => {{
|
||||
if (wasm.__wbindgen_start == undefined) {{
|
||||
wasm.main();
|
||||
}}
|
||||
}});
|
||||
</script>
|
||||
</body>
|
||||
</html>"#
|
||||
) {
|
||||
eprintln!("Failed to write to html: {}", err);
|
||||
}
|
||||
html += &index.post_main;
|
||||
|
||||
html
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use dioxus_core::Component;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ServeConfigBuilder<P: Clone> {
|
||||
pub(crate) app: Component<P>,
|
||||
pub(crate) props: P,
|
||||
pub(crate) application_name: Option<&'static str>,
|
||||
pub(crate) base_path: Option<&'static str>,
|
||||
pub(crate) head: Option<&'static str>,
|
||||
pub(crate) root_id: Option<&'static str>,
|
||||
pub(crate) index_path: Option<&'static str>,
|
||||
pub(crate) assets_path: Option<&'static str>,
|
||||
}
|
||||
|
||||
|
@ -16,32 +19,25 @@ impl<P: Clone> ServeConfigBuilder<P> {
|
|||
Self {
|
||||
app,
|
||||
props,
|
||||
application_name: None,
|
||||
base_path: None,
|
||||
head: None,
|
||||
root_id: None,
|
||||
index_path: None,
|
||||
assets_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the application name matching the name in the Dioxus.toml file used to build the application
|
||||
pub fn application_name(mut self, application_name: &'static str) -> Self {
|
||||
self.application_name = Some(application_name);
|
||||
/// Set the path of the index.html file to be served. (defaults to {assets_path}/index.html)
|
||||
pub fn index_path(mut self, index_path: &'static str) -> Self {
|
||||
self.index_path = Some(index_path);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the path the WASM application will be served under
|
||||
pub fn base_path(mut self, base_path: &'static str) -> Self {
|
||||
self.base_path = Some(base_path);
|
||||
/// Set the id of the root element in the index.html file to place the prerendered content into. (defaults to main)
|
||||
pub fn root_id(mut self, root_id: &'static str) -> Self {
|
||||
self.root_id = Some(root_id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the head content to be included in the HTML document served
|
||||
pub fn head(mut self, head: &'static str) -> Self {
|
||||
self.head = Some(head);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the path of the assets folder generated by the Dioxus CLI. (defaults to dist/assets)
|
||||
/// Set the path of the assets folder generated by the Dioxus CLI. (defaults to dist)
|
||||
pub fn assets_path(mut self, assets_path: &'static str) -> Self {
|
||||
self.assets_path = Some(assets_path);
|
||||
self
|
||||
|
@ -49,31 +45,61 @@ impl<P: Clone> ServeConfigBuilder<P> {
|
|||
|
||||
/// Build the ServeConfig
|
||||
pub fn build(self) -> ServeConfig<P> {
|
||||
let base_path = self.base_path.unwrap_or(".");
|
||||
let application_name = self.application_name.unwrap_or("dioxus");
|
||||
let assets_path = self.assets_path.unwrap_or("dist");
|
||||
|
||||
let index_path = self
|
||||
.index_path
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| format!("{assets_path}/index.html").into());
|
||||
|
||||
let root_id = self.root_id.unwrap_or("main");
|
||||
|
||||
let index = load_index_html(index_path, root_id);
|
||||
|
||||
ServeConfig {
|
||||
app: self.app,
|
||||
props: self.props,
|
||||
application_name,
|
||||
base_path,
|
||||
head: self.head.map(String::from).unwrap_or(format!(r#"<title>Dioxus Application</title>
|
||||
<link rel="preload" href="/{base_path}/assets/dioxus/{application_name}_bg.wasm" as="fetch" type="application/wasm" crossorigin="" />
|
||||
<link rel="modulepreload" href="/{base_path}/assets/dioxus/{application_name}.js" />
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="UTF-8" />"#)),
|
||||
assets_path: self.assets_path.unwrap_or("dist/assets"),
|
||||
index,
|
||||
assets_path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_index_html(path: PathBuf, root_id: &'static str) -> IndexHtml {
|
||||
let mut file = File::open(path).expect("Failed to find index.html. Make sure the index_path is set correctly and the WASM application has been built.");
|
||||
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)
|
||||
.expect("Failed to read index.html");
|
||||
|
||||
let (pre_main, post_main) = contents.split_once(&format!("id=\"{root_id}\"")).unwrap_or_else(|| panic!("Failed to find id=\"{root_id}\" in index.html. The id is used to inject the application into the page."));
|
||||
|
||||
let post_main = post_main.split_once('>').unwrap_or_else(|| {
|
||||
panic!("Failed to find closing > after id=\"{root_id}\" in index.html.")
|
||||
});
|
||||
|
||||
let (pre_main, post_main) = (
|
||||
pre_main.to_string() + &format!("id=\"{root_id}\"") + post_main.0 + ">",
|
||||
post_main.1.to_string(),
|
||||
);
|
||||
|
||||
IndexHtml {
|
||||
pre_main,
|
||||
post_main,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct IndexHtml {
|
||||
pub(crate) pre_main: String,
|
||||
pub(crate) post_main: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ServeConfig<P: Clone> {
|
||||
pub(crate) app: Component<P>,
|
||||
pub(crate) props: P,
|
||||
pub(crate) application_name: &'static str,
|
||||
pub(crate) base_path: &'static str,
|
||||
pub(crate) head: String,
|
||||
pub(crate) index: IndexHtml,
|
||||
pub(crate) assets_path: &'static str,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue