follow up to serveconfig (#4820)
* remove latent dbg! * cleanup serveconfig
This commit is contained in:
1 parent
7408a33be7
commit
306639ee9f
5 files changed
+120
-119
No files matched your search
@@ -11,11 +11,9 @@ use crate::{IncrementalRendererConfig, IndexHtml};
|
|||||||
pub(crate) type ContextProviders = Arc<Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync + 'static>>>;
|
pub(crate) type ContextProviders = Arc<Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync + 'static>>>;
|
||||||
|
|
||||||
/// A ServeConfig is used to configure how to serve a Dioxus application. It contains information about how to serve static assets, and what content to render with [`dioxus_ssr`].
|
/// A ServeConfig is used to configure how to serve a Dioxus application. It contains information about how to serve static assets, and what content to render with [`dioxus_ssr`].
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone)]
|
||||||
pub struct ServeConfig {
|
pub struct ServeConfig {
|
||||||
pub(crate) root_id: Option<&'static str>,
|
pub(crate) index: IndexHtml,
|
||||||
pub(crate) index: Option<IndexHtml>,
|
|
||||||
pub(crate) index_html_override: Option<String>,
|
|
||||||
pub(crate) incremental: Option<IncrementalRendererConfig>,
|
pub(crate) incremental: Option<IncrementalRendererConfig>,
|
||||||
pub(crate) context_providers: Vec<Arc<dyn Fn() -> Box<dyn Any> + Send + Sync + 'static>>,
|
pub(crate) context_providers: Vec<Arc<dyn Fn() -> Box<dyn Any> + Send + Sync + 'static>>,
|
||||||
pub(crate) streaming_mode: StreamingMode,
|
pub(crate) streaming_mode: StreamingMode,
|
||||||
@@ -35,6 +33,12 @@ pub enum StreamingMode {
|
|||||||
|
|
||||||
impl LaunchConfig for ServeConfig {}
|
impl LaunchConfig for ServeConfig {}
|
||||||
|
|
||||||
|
impl Default for ServeConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ServeConfig {
|
impl ServeConfig {
|
||||||
/// Create a new ServeConfig with incremental static generation disabled and the default index.html settings.
|
/// Create a new ServeConfig with incremental static generation disabled and the default index.html settings.
|
||||||
pub fn builder() -> Self {
|
pub fn builder() -> Self {
|
||||||
@@ -42,17 +46,54 @@ impl ServeConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new ServeConfig with incremental static generation disabled and the default index.html settings
|
/// Create a new ServeConfig with incremental static generation disabled and the default index.html settings
|
||||||
|
///
|
||||||
|
/// This will automatically use the `index.html` file in the `/public` directory if it exists.
|
||||||
|
/// The `/public` folder is meant located next to the current executable. If no `index.html` file is found,
|
||||||
|
/// a default index.html will be used, which will not include any JavaScript or WASM initialization code.
|
||||||
|
///
|
||||||
|
/// To provide an alternate `index.html`, you can use `with_index_html` method instead.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
let index = if let Some(public_path) = crate::public_path() {
|
||||||
|
let index_html_path = public_path.join("index.html");
|
||||||
|
|
||||||
|
if index_html_path.exists() {
|
||||||
|
let index_html = std::fs::read_to_string(index_html_path)
|
||||||
|
.expect("Failed to read index.html from public directory");
|
||||||
|
|
||||||
|
IndexHtml::new(&index_html, "main")
|
||||||
|
.expect("Failed to parse index.html from public directory")
|
||||||
|
} else {
|
||||||
|
tracing::warn!("No index.html found in public directory, using default index.html");
|
||||||
|
IndexHtml::ssr_only()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tracing::warn!(
|
||||||
|
"Cannot identify public directory, using default index.html. If you need client-side scripts (like JS + WASM), please provide an explicit public directory."
|
||||||
|
);
|
||||||
|
IndexHtml::ssr_only()
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
root_id: None,
|
index,
|
||||||
incremental: None,
|
incremental: None,
|
||||||
index: None,
|
|
||||||
index_html_override: None,
|
|
||||||
context_providers: Default::default(),
|
context_providers: Default::default(),
|
||||||
streaming_mode: StreamingMode::default(),
|
streaming_mode: StreamingMode::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new ServeConfig with the given parsed `IndexHtml` structure.
|
||||||
|
///
|
||||||
|
/// You can create the `IndexHtml` structure by using `IndexHtml::new` method, or manually from
|
||||||
|
/// a string or file.
|
||||||
|
pub fn with_index_html(index: IndexHtml) -> Self {
|
||||||
|
Self {
|
||||||
|
index,
|
||||||
|
incremental: Default::default(),
|
||||||
|
context_providers: Default::default(),
|
||||||
|
streaming_mode: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Enable incremental static generation. Incremental static generation caches the
|
/// Enable incremental static generation. Incremental static generation caches the
|
||||||
/// rendered html in memory and/or the file system. It can be used to improve performance of heavy routes.
|
/// rendered html in memory and/or the file system. It can be used to improve performance of heavy routes.
|
||||||
///
|
///
|
||||||
@@ -71,56 +112,6 @@ impl ServeConfig {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the contents of the index.html file to be served.
|
|
||||||
pub fn index_html(mut self, index_html: String) -> Self {
|
|
||||||
self.index_html_override = Some(index_html);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the id of the root element in the index.html file to place the prerendered content into. (defaults to main)
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// If your index.html file looks like this:
|
|
||||||
/// ```html
|
|
||||||
/// <!DOCTYPE html>
|
|
||||||
/// <html>
|
|
||||||
/// <head>
|
|
||||||
/// <title>My App</title>
|
|
||||||
/// </head>
|
|
||||||
/// <body>
|
|
||||||
/// <div id="my-custom-root"></div>
|
|
||||||
/// </body>
|
|
||||||
/// </html>
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// You can set the root id to `"my-custom-root"` to render the app into that element:
|
|
||||||
///
|
|
||||||
/// ```rust, no_run
|
|
||||||
/// # fn app() -> Element { unimplemented!() }
|
|
||||||
/// use dioxus::prelude::*;
|
|
||||||
///
|
|
||||||
/// // Finally, launch the app with the config
|
|
||||||
/// LaunchBuilder::new()
|
|
||||||
/// // Only set the server config if the server feature is enabled
|
|
||||||
/// .with_cfg(server_only! {
|
|
||||||
/// dioxus_server::ServeConfig::default().root_id("app")
|
|
||||||
/// })
|
|
||||||
/// // You also need to set the root id in your web config
|
|
||||||
/// .with_cfg(web! {
|
|
||||||
/// dioxus::web::Config::default().rootname("app")
|
|
||||||
/// })
|
|
||||||
/// // And desktop config
|
|
||||||
/// .with_cfg(desktop! {
|
|
||||||
/// dioxus::desktop::Config::default().with_root_name("app")
|
|
||||||
/// })
|
|
||||||
/// .launch(app);
|
|
||||||
/// ```
|
|
||||||
pub fn root_id(mut self, root_id: &'static str) -> Self {
|
|
||||||
self.root_id = Some(root_id);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provide context to the root and server functions. You can use this context while rendering with [`consume_context`](dioxus_core::consume_context).
|
/// Provide context to the root and server functions. You can use this context while rendering with [`consume_context`](dioxus_core::consume_context).
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
@@ -313,32 +304,4 @@ impl ServeConfig {
|
|||||||
self.streaming_mode = StreamingMode::OutOfOrder;
|
self.streaming_mode = StreamingMode::OutOfOrder;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_index(&mut self) -> &IndexHtml {
|
|
||||||
// Create the IndexHtml object if the user has specified an override
|
|
||||||
if let Some(raw_index_html) = self.index_html_override.as_ref() {
|
|
||||||
self.index = Some(
|
|
||||||
IndexHtml::new(raw_index_html, self.root_id.unwrap_or("main"))
|
|
||||||
.expect("Failed to parse custom index.html"),
|
|
||||||
);
|
|
||||||
} else if !cfg!(target_arch = "wasm32") {
|
|
||||||
let public_path = crate::public_path();
|
|
||||||
let index_html_path = public_path.join("index.html");
|
|
||||||
|
|
||||||
if index_html_path.exists() {
|
|
||||||
let index_html = std::fs::read_to_string(index_html_path)
|
|
||||||
.expect("Failed to read index.html from public directory");
|
|
||||||
self.index = Some(
|
|
||||||
IndexHtml::new(&index_html, self.root_id.unwrap_or("main"))
|
|
||||||
.expect("Failed to parse index.html from public directory"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if self.index.is_none() {
|
|
||||||
self.index = Some(IndexHtml::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.index
|
|
||||||
.as_ref()
|
|
||||||
.expect("IndexHtml is always set after it is resolved")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,34 @@
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
/// An `IndexHtml` represents the contents of an `index.html` file used to serve a web application.
|
/// An `IndexHtml` represents the contents of an `index.html` file used to serve a web application.
|
||||||
///
|
///
|
||||||
/// This defines the static portion of your web application, typically generated by a tool like `dx`
|
/// This defines the static portion of your web application, typically generated by a tool like `dx`
|
||||||
/// in conjunction with wasm-bindgen.
|
/// in conjunction with wasm-bindgen.
|
||||||
|
///
|
||||||
|
/// This structure expects a well-formed HTML document split into several sections:
|
||||||
|
/// - `head_before_title`: The portion of the `<head>` section before the `<title
|
||||||
|
/// - `title`: The `<title>` tag and its contents.
|
||||||
|
/// - `head_after_title`: The portion of the `<head>` section after the `<title>` tag.
|
||||||
|
/// - `close_head`: The closing `</head>` tag and any content following it.
|
||||||
|
/// - `post_main`: The content following the main application container (e.g., `<div id="main">`).
|
||||||
|
/// - `after_closing_body_tag`: The content following the closing `</body>` tag.
|
||||||
|
///
|
||||||
|
/// These fields are not explicitly exposed as part of the API, but are critical for dioxus-fullstack
|
||||||
|
/// to properly inject server-rendered content and client-side bootstrapping scripts into the HTML.
|
||||||
|
///
|
||||||
|
/// The simplest HTML document that satisfies this structure is:
|
||||||
|
///
|
||||||
|
/// ```html
|
||||||
|
/// <html>
|
||||||
|
/// <head> </head>
|
||||||
|
/// <body>
|
||||||
|
/// <div id="main"></div>
|
||||||
|
/// </body>
|
||||||
|
/// </html>
|
||||||
|
/// ```
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct IndexHtml {
|
pub struct IndexHtml {
|
||||||
pub(crate) head_before_title: String,
|
pub(crate) head_before_title: String,
|
||||||
pub(crate) head_after_title: String,
|
pub(crate) head_after_title: String,
|
||||||
pub(crate) title: String,
|
pub(crate) title: String,
|
||||||
@@ -20,7 +43,7 @@ impl IndexHtml {
|
|||||||
/// This function will parse the `index.html` and split it into sections for easier manipulation.
|
/// This function will parse the `index.html` and split it into sections for easier manipulation.
|
||||||
/// The `root_id` parameter specifies the id of the main application container (e.g., "main")
|
/// The `root_id` parameter specifies the id of the main application container (e.g., "main")
|
||||||
/// which your app will render into.
|
/// which your app will render into.
|
||||||
pub(crate) fn new(contents: &str, root_id: &'static str) -> Result<IndexHtml, anyhow::Error> {
|
pub fn new(contents: &str, root_id: &str) -> Result<IndexHtml, anyhow::Error> {
|
||||||
let (pre_main, post_main) = contents.split_once(&format!("id=\"{root_id}\""))
|
let (pre_main, post_main) = contents.split_once(&format!("id=\"{root_id}\""))
|
||||||
.with_context(|| format!("Failed to find id=\"{root_id}\" in index.html. The id is used to inject the application into the page."))?;
|
.with_context(|| format!("Failed to find id=\"{root_id}\" in index.html. The id is used to inject the application into the page."))?;
|
||||||
|
|
||||||
@@ -57,19 +80,29 @@ impl IndexHtml {
|
|||||||
head_after_title = new_head_after_title.to_string();
|
head_after_title = new_head_after_title.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(dbg!(IndexHtml {
|
Ok(IndexHtml {
|
||||||
head_before_title,
|
head_before_title,
|
||||||
head_after_title,
|
head_after_title,
|
||||||
title,
|
title,
|
||||||
close_head,
|
close_head,
|
||||||
post_main: post_main.to_string(),
|
post_main: post_main.to_string(),
|
||||||
after_closing_body_tag: "</body>".to_string() + after_closing_body_tag,
|
after_closing_body_tag: "</body>".to_string() + after_closing_body_tag,
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for IndexHtml {
|
/// Load an `IndexHtml` from a file at the given path.
|
||||||
fn default() -> Self {
|
///
|
||||||
|
/// This function reads the contents of the file and parses it into an `IndexHtml`.
|
||||||
|
/// The `id` parameter specifies the id of the main application container (e.g., "main").
|
||||||
|
pub fn from_file(path: &Path, id: &str) -> Result<IndexHtml, anyhow::Error> {
|
||||||
|
let contents = std::fs::read_to_string(path)
|
||||||
|
.with_context(|| format!("Failed to read index.html from {}", path.display()))?;
|
||||||
|
IndexHtml::new(&contents, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a default `IndexHtml` suitable for server-side rendering (SSR) only applications.
|
||||||
|
/// This will not include any customizations to the HTML nor any JavaScript to bootstrap a client-side app.
|
||||||
|
pub fn ssr_only() -> Self {
|
||||||
const DEFAULT: &str = r#"<!DOCTYPE html>
|
const DEFAULT: &str = r#"<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head> </head>
|
<head> </head>
|
||||||
|
|||||||
@@ -120,11 +120,9 @@ where
|
|||||||
S: Send + Sync + Clone + 'static,
|
S: Send + Sync + Clone + 'static,
|
||||||
{
|
{
|
||||||
fn serve_static_assets(self) -> Self {
|
fn serve_static_assets(self) -> Self {
|
||||||
let public_path = public_path();
|
let Some(public_path) = public_path() else {
|
||||||
|
|
||||||
if !public_path.exists() {
|
|
||||||
return self;
|
return self;
|
||||||
}
|
};
|
||||||
|
|
||||||
// Serve all files in public folder except index.html
|
// Serve all files in public folder except index.html
|
||||||
serve_dir_cached(self, &public_path, &public_path)
|
serve_dir_cached(self, &public_path, &public_path)
|
||||||
@@ -284,11 +282,9 @@ impl RenderHandleState {
|
|||||||
/// axum::serve(listener, router).await.unwrap();
|
/// axum::serve(listener, router).await.unwrap();
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn render_handler(State(mut state): State<Self>, request: Request<Body>) -> Response {
|
pub async fn render_handler(State(state): State<Self>, request: Request<Body>) -> Response {
|
||||||
let (parts, _) = request.into_parts();
|
let (parts, _) = request.into_parts();
|
||||||
|
|
||||||
state.config.resolve_index();
|
|
||||||
|
|
||||||
let response = state
|
let response = state
|
||||||
.renderers
|
.renderers
|
||||||
.clone()
|
.clone()
|
||||||
@@ -350,17 +346,19 @@ impl RenderHandleState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the path to the public assets directory to serve static files from
|
/// Get the path to the public assets directory to serve static files from
|
||||||
pub(crate) fn public_path() -> PathBuf {
|
pub(crate) fn public_path() -> Option<PathBuf> {
|
||||||
if let Ok(path) = std::env::var("DIOXUS_PUBLIC_PATH") {
|
if let Ok(path) = std::env::var("DIOXUS_PUBLIC_PATH") {
|
||||||
return PathBuf::from(path);
|
return Some(PathBuf::from(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The CLI always bundles static assets into the exe/public directory
|
// The CLI always bundles static assets into the exe/public directory
|
||||||
std::env::current_exe()
|
Some(
|
||||||
.expect("Failed to get current executable path")
|
std::env::current_exe()
|
||||||
.parent()
|
.ok()?
|
||||||
.unwrap()
|
.parent()
|
||||||
.join("public")
|
.unwrap()
|
||||||
|
.join("public"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serve_dir_cached<S>(mut router: Router<S>, public_path: &Path, directory: &Path) -> Router<S>
|
fn serve_dir_cached<S>(mut router: Router<S>, public_path: &Path, directory: &Path) -> Router<S>
|
||||||
|
|||||||
@@ -680,13 +680,13 @@ impl SsrRendererPool {
|
|||||||
document.and_then(|document| document.title())
|
document.and_then(|document| document.title())
|
||||||
};
|
};
|
||||||
|
|
||||||
to.write_str(&cfg.index.as_ref().unwrap().head_before_title)?;
|
to.write_str(&cfg.index.head_before_title)?;
|
||||||
if let Some(title) = title {
|
if let Some(title) = title {
|
||||||
to.write_str(&title)?;
|
to.write_str(&title)?;
|
||||||
} else {
|
} else {
|
||||||
to.write_str(&cfg.index.as_ref().unwrap().title)?;
|
to.write_str(&cfg.index.title)?;
|
||||||
}
|
}
|
||||||
to.write_str(&cfg.index.as_ref().unwrap().head_after_title)?;
|
to.write_str(&cfg.index.head_after_title)?;
|
||||||
|
|
||||||
let document =
|
let document =
|
||||||
virtual_dom.in_scope(ScopeId::ROOT, try_consume_context::<Rc<ServerDocument>>);
|
virtual_dom.in_scope(ScopeId::ROOT, try_consume_context::<Rc<ServerDocument>>);
|
||||||
@@ -708,7 +708,7 @@ impl SsrRendererPool {
|
|||||||
cfg: &ServeConfig,
|
cfg: &ServeConfig,
|
||||||
to: &mut R,
|
to: &mut R,
|
||||||
) -> Result<(), IncrementalRendererError> {
|
) -> Result<(), IncrementalRendererError> {
|
||||||
to.write_str(&cfg.index.as_ref().unwrap().close_head)?;
|
to.write_str(&cfg.index.close_head)?;
|
||||||
|
|
||||||
// // #[cfg(feature = "document")]
|
// // #[cfg(feature = "document")]
|
||||||
// {
|
// {
|
||||||
@@ -749,7 +749,7 @@ impl SsrRendererPool {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
write!(to, r#"</script>"#,)?;
|
write!(to, r#"</script>"#,)?;
|
||||||
to.write_str(&cfg.index.as_ref().unwrap().post_main)?;
|
to.write_str(&cfg.index.post_main)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -759,7 +759,7 @@ impl SsrRendererPool {
|
|||||||
cfg: &ServeConfig,
|
cfg: &ServeConfig,
|
||||||
to: &mut R,
|
to: &mut R,
|
||||||
) -> Result<(), IncrementalRendererError> {
|
) -> Result<(), IncrementalRendererError> {
|
||||||
to.write_str(&cfg.index.as_ref().unwrap().after_closing_body_tag)?;
|
to.write_str(&cfg.index.after_closing_body_tag)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,20 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus::launch(|| {
|
// we split these two to ensure `dioxus::serve` works properly.
|
||||||
rsx! {
|
#[cfg(feature = "server")]
|
||||||
Comp {}
|
dioxus::serve(|| async move { Ok(dioxus::server::router(app)) });
|
||||||
Comp {}
|
|
||||||
Button {}
|
#[cfg(not(feature = "server"))]
|
||||||
}
|
dioxus::launch(app);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
fn app() -> Element {
|
||||||
|
rsx! {
|
||||||
|
Comp {}
|
||||||
|
Comp {}
|
||||||
|
Button {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
|||||||
Reference in new issue
Block a user