Simplify the launch builder (#2967)
* improve documentation for the fullstack server context * Add a section about axum integration to the crate root docs * make serve_dioxus_application accept the cfg builder directly * remove unused server_fn module * improve fullstack config docs * improve documentation for the server function macro * fix axum router extension link * Fix doc tests * Fix launch builder * Simplify the launch builder * don't re-export launch in the prelude * refactor fullstack launch * Fix fullstack launch builder * Update static generation with the new builder api * fix some formatting/overly broad launch replacements * fix custom menu example * fix fullstack/static generation examples * Fix static generation launch * A few small formatting fixes * Fix a few doc tests * implement LaunchConfig for serve configs * fix fullstack launch with separate web and server launch methods * fix check with all features * dont expose inner core module * clippy and check * fix readme --------- Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
1 parent
2d7bef44e8
commit
20d146d9bd
126 files changed
+618
-989
No files matched your search
@@ -8,7 +8,7 @@ use std::{
|
||||
use tokio::time::sleep;
|
||||
|
||||
fn main() {
|
||||
LaunchBuilder::desktop().launch(app);
|
||||
dioxus::LaunchBuilder::desktop().launch(app);
|
||||
}
|
||||
|
||||
struct WindowPreferences {
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::{
|
||||
use tokio::time::sleep;
|
||||
|
||||
fn main() {
|
||||
LaunchBuilder::desktop().launch(app);
|
||||
dioxus::LaunchBuilder::desktop().launch(app);
|
||||
}
|
||||
|
||||
struct WindowPreferences {
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::{
|
||||
use tokio::time::sleep;
|
||||
|
||||
fn main() {
|
||||
LaunchBuilder::desktop().launch(app);
|
||||
dioxus::LaunchBuilder::desktop().launch(app);
|
||||
}
|
||||
|
||||
struct WindowPreferences {
|
||||
|
||||
@@ -150,6 +150,49 @@ impl BuildRequest {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub(crate) struct OpenArguments {
|
||||
fullstack_address: Option<SocketAddr>,
|
||||
devserver_addr: Option<SocketAddr>,
|
||||
always_on_top: Option<bool>,
|
||||
workspace: PathBuf,
|
||||
asset_root: PathBuf,
|
||||
app_title: String,
|
||||
out_dir: PathBuf,
|
||||
serve: bool,
|
||||
}
|
||||
|
||||
impl OpenArguments {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
serve: &ServeArguments,
|
||||
fullstack_address: Option<SocketAddr>,
|
||||
dioxus_crate: &DioxusCrate,
|
||||
) -> Self {
|
||||
Self {
|
||||
devserver_addr: Some(serve.address.address()),
|
||||
always_on_top: Some(serve.always_on_top.unwrap_or(true)),
|
||||
serve: true,
|
||||
fullstack_address,
|
||||
workspace: dioxus_crate.workspace_dir().to_path_buf(),
|
||||
asset_root: dioxus_crate.asset_dir().to_path_buf(),
|
||||
app_title: dioxus_crate.dioxus_config.application.name.clone(),
|
||||
out_dir: dioxus_crate.out_dir().to_path_buf(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new_for_static_generation_build(dioxus_crate: &DioxusCrate) -> Self {
|
||||
Self {
|
||||
workspace: dioxus_crate.workspace_dir().to_path_buf(),
|
||||
asset_root: dioxus_crate.asset_dir().to_path_buf(),
|
||||
app_title: dioxus_crate.dioxus_config.application.name.clone(),
|
||||
out_dir: dioxus_crate.out_dir().to_path_buf(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct BuildResult {
|
||||
pub executable: PathBuf,
|
||||
@@ -158,20 +201,12 @@ pub(crate) struct BuildResult {
|
||||
|
||||
impl BuildResult {
|
||||
/// Open the executable if this is a native build
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn open(
|
||||
&self,
|
||||
serve: &ServeArguments,
|
||||
fullstack_address: Option<SocketAddr>,
|
||||
workspace: &std::path::Path,
|
||||
asset_root: &std::path::Path,
|
||||
devserver_addr: SocketAddr,
|
||||
app_title: String,
|
||||
out_dir: PathBuf,
|
||||
) -> std::io::Result<Option<Child>> {
|
||||
pub fn open(&self, arguments: OpenArguments) -> std::io::Result<Option<Child>> {
|
||||
match self.target_platform {
|
||||
TargetPlatform::Web => {
|
||||
tracing::info!(dx_src = ?TraceSrc::Dev, "Serving web app on http://{} 🎉", serve.address.address());
|
||||
if let Some(address) = arguments.fullstack_address {
|
||||
tracing::info!(dx_src = ?TraceSrc::Dev, "Serving web app on http://{} 🎉", address);
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
TargetPlatform::Desktop => {
|
||||
@@ -181,7 +216,7 @@ impl BuildResult {
|
||||
// shut this up for now - the web app will take priority in logging
|
||||
}
|
||||
TargetPlatform::Liveview => {
|
||||
if let Some(fullstack_address) = fullstack_address {
|
||||
if let Some(fullstack_address) = arguments.fullstack_address {
|
||||
tracing::info!(
|
||||
dx_src = ?TraceSrc::Dev,
|
||||
"Launching liveview server on http://{:?} 🎉",
|
||||
@@ -191,7 +226,9 @@ impl BuildResult {
|
||||
}
|
||||
}
|
||||
|
||||
tracing::info!(dx_src = ?TraceSrc::Dev, "Press [o] to open the app manually.");
|
||||
if arguments.serve {
|
||||
tracing::info!(dx_src = ?TraceSrc::Dev, "Press [o] to open the app manually.");
|
||||
}
|
||||
|
||||
let executable = self.executable.canonicalize()?;
|
||||
let mut cmd = Command::new(executable);
|
||||
@@ -199,29 +236,36 @@ impl BuildResult {
|
||||
// Set the env vars that the clients will expect
|
||||
// These need to be stable within a release version (ie 0.6.0)
|
||||
cmd.env(dioxus_cli_config::CLI_ENABLED_ENV, "true");
|
||||
if let Some(addr) = fullstack_address {
|
||||
if let Some(addr) = arguments.fullstack_address {
|
||||
cmd.env(dioxus_cli_config::SERVER_IP_ENV, addr.ip().to_string());
|
||||
cmd.env(dioxus_cli_config::SERVER_PORT_ENV, addr.port().to_string());
|
||||
}
|
||||
cmd.env(
|
||||
dioxus_cli_config::ALWAYS_ON_TOP_ENV,
|
||||
serve.always_on_top.unwrap_or(true).to_string(),
|
||||
);
|
||||
if let Some(always_on_top) = arguments.always_on_top {
|
||||
cmd.env(
|
||||
dioxus_cli_config::ALWAYS_ON_TOP_ENV,
|
||||
always_on_top.to_string(),
|
||||
);
|
||||
}
|
||||
cmd.env(
|
||||
dioxus_cli_config::ASSET_ROOT_ENV,
|
||||
asset_root.display().to_string(),
|
||||
arguments.asset_root.display().to_string(),
|
||||
);
|
||||
if let Some(devserver_addr) = arguments.devserver_addr {
|
||||
cmd.env(
|
||||
dioxus_cli_config::DEVSERVER_RAW_ADDR_ENV,
|
||||
devserver_addr.to_string(),
|
||||
);
|
||||
}
|
||||
cmd.env(dioxus_cli_config::APP_TITLE_ENV, arguments.app_title);
|
||||
cmd.env(
|
||||
dioxus_cli_config::DEVSERVER_RAW_ADDR_ENV,
|
||||
devserver_addr.to_string(),
|
||||
dioxus_cli_config::OUT_DIR,
|
||||
arguments.out_dir.display().to_string(),
|
||||
);
|
||||
cmd.env(dioxus_cli_config::APP_TITLE_ENV, app_title);
|
||||
cmd.env(dioxus_cli_config::OUT_DIR, out_dir.display().to_string());
|
||||
|
||||
cmd.stderr(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.kill_on_drop(true)
|
||||
.current_dir(workspace);
|
||||
.current_dir(arguments.workspace);
|
||||
|
||||
Ok(Some(cmd.spawn()?))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::config::Platform;
|
||||
use crate::{builder::OpenArguments, config::Platform};
|
||||
use anyhow::Context;
|
||||
|
||||
use crate::{
|
||||
@@ -115,7 +115,20 @@ impl Build {
|
||||
pub async fn build(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> {
|
||||
self.resolve(dioxus_crate)?;
|
||||
let build_requests = BuildRequest::create(false, dioxus_crate, self.clone())?;
|
||||
BuildRequest::build_all_parallel(build_requests).await?;
|
||||
let builds = BuildRequest::build_all_parallel(build_requests).await?;
|
||||
|
||||
// If this is a static generation build, building involves running the server to generate static files
|
||||
if self.platform.unwrap() == Platform::StaticGeneration {
|
||||
println!("Building static site...");
|
||||
for build in builds {
|
||||
if let Some(mut result) =
|
||||
build.open(OpenArguments::new_for_static_generation_build(dioxus_crate))?
|
||||
{
|
||||
result.wait().await?;
|
||||
}
|
||||
}
|
||||
println!("Static site built!");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::future::{poll_fn, Future, IntoFuture};
|
||||
use std::task::Poll;
|
||||
|
||||
use crate::builder::OpenArguments;
|
||||
use crate::cli::serve::Serve;
|
||||
use crate::dioxus_crate::DioxusCrate;
|
||||
use crate::tracer::CLILogControl;
|
||||
@@ -156,23 +157,14 @@ pub async fn serve_all(
|
||||
builder.children.clear();
|
||||
}
|
||||
|
||||
let asset_dir = dioxus_crate
|
||||
.dioxus_config
|
||||
.application
|
||||
.asset_dir
|
||||
.canonicalize()
|
||||
.unwrap_or(std::path::PathBuf::from("./assets"));
|
||||
|
||||
// If we have a build result, open it
|
||||
for build_result in results.iter() {
|
||||
let child = build_result.open(
|
||||
OpenArguments::new(
|
||||
&serve.server_arguments,
|
||||
server.fullstack_address(),
|
||||
&dioxus_crate.workspace_dir(),
|
||||
&asset_dir,
|
||||
server.ip,
|
||||
dioxus_crate.dioxus_config.application.name.clone(),
|
||||
dioxus_crate.out_dir()
|
||||
&dioxus_crate
|
||||
)
|
||||
);
|
||||
match child {
|
||||
Ok(Some(child_proc)) => builder.children.push((build_result.target_platform, child_proc)),
|
||||
@@ -192,12 +184,12 @@ pub async fn serve_all(
|
||||
server.send_reload_command().await;
|
||||
},
|
||||
|
||||
// If the process exited *cleanly*, we can exit
|
||||
// If the desktop process exited *cleanly*, we can exit
|
||||
Ok(BuilderUpdate::ProcessExited { status, target_platform }) => {
|
||||
// Then remove the child process
|
||||
builder.children.retain(|(platform, _)| *platform != target_platform);
|
||||
match status {
|
||||
Ok(status) => {
|
||||
match (target_platform, status) {
|
||||
(TargetPlatform::Desktop, Ok(status)) => {
|
||||
if status.success() {
|
||||
break;
|
||||
}
|
||||
@@ -205,7 +197,9 @@ pub async fn serve_all(
|
||||
tracing::error!(dx_src = ?TraceSrc::Dev, "Application exited with status: {status}");
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
// Ignore the static generation platform exiting
|
||||
(_ , Ok(_)) => {},
|
||||
(_, Err(e)) => {
|
||||
tracing::error!(dx_src = ?TraceSrc::Dev, "Application exited with error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ impl Server {
|
||||
// If we're serving a fullstack app, we need to find a port to proxy to
|
||||
let fullstack_port = if matches!(
|
||||
serve.build_arguments.platform(),
|
||||
Platform::Liveview | Platform::Fullstack | Platform::StaticGeneration
|
||||
Platform::Liveview | Platform::Fullstack
|
||||
) {
|
||||
get_available_port(addr.ip())
|
||||
} else {
|
||||
@@ -368,7 +368,7 @@ fn setup_router(
|
||||
|
||||
// server the dir if it's web, otherwise let the fullstack server itself handle it
|
||||
match platform {
|
||||
Platform::Web => {
|
||||
Platform::Web | Platform::StaticGeneration => {
|
||||
// Route file service to output the .wasm and assets if this is a web build
|
||||
let base_path = format!(
|
||||
"/{}",
|
||||
@@ -382,9 +382,9 @@ fn setup_router(
|
||||
.trim_matches('/')
|
||||
);
|
||||
|
||||
router = router.nest_service(&base_path, build_serve_dir(serve, config));
|
||||
router = router.nest_service(&base_path, build_serve_dir(serve, config, platform));
|
||||
}
|
||||
Platform::Liveview | Platform::Fullstack | Platform::StaticGeneration => {
|
||||
Platform::Liveview | Platform::Fullstack => {
|
||||
// For fullstack and static generation, forward all requests to the server
|
||||
let address = fullstack_address.unwrap();
|
||||
|
||||
@@ -449,7 +449,11 @@ fn setup_router(
|
||||
Ok(router)
|
||||
}
|
||||
|
||||
fn build_serve_dir(serve: &Serve, cfg: &DioxusCrate) -> axum::routing::MethodRouter {
|
||||
fn build_serve_dir(
|
||||
serve: &Serve,
|
||||
cfg: &DioxusCrate,
|
||||
platform: Platform,
|
||||
) -> axum::routing::MethodRouter {
|
||||
static CORS_UNSAFE: (HeaderValue, HeaderValue) = (
|
||||
HeaderValue::from_static("unsafe-none"),
|
||||
HeaderValue::from_static("unsafe-none"),
|
||||
@@ -465,7 +469,11 @@ fn build_serve_dir(serve: &Serve, cfg: &DioxusCrate) -> axum::routing::MethodRou
|
||||
false => CORS_UNSAFE.clone(),
|
||||
};
|
||||
|
||||
let out_dir = cfg.out_dir();
|
||||
let out_dir = match platform {
|
||||
// Static generation only serves files from the public directory
|
||||
Platform::StaticGeneration => cfg.out_dir().join("public"),
|
||||
_ => cfg.out_dir(),
|
||||
};
|
||||
let index_on_404 = cfg.dioxus_config.web.watcher.index_on_404;
|
||||
|
||||
get_service(
|
||||
@@ -479,7 +487,7 @@ fn build_serve_dir(serve: &Serve, cfg: &DioxusCrate) -> axum::routing::MethodRou
|
||||
let out_dir = out_dir.clone();
|
||||
move |response| async move { Ok(no_cache(index_on_404, &out_dir, response)) }
|
||||
})
|
||||
.service(ServeDir::new(out_dir)),
|
||||
.service(ServeDir::new(&out_dir)),
|
||||
)
|
||||
.handle_error(|error: Infallible| async move {
|
||||
(
|
||||
@@ -500,7 +508,18 @@ fn no_cache(
|
||||
// If there's a 404 and we're supposed to index on 404, upgrade that failed request to the index.html
|
||||
// We might want to isnert a header here saying we *did* that but oh well
|
||||
if response.status() == StatusCode::NOT_FOUND && index_on_404 {
|
||||
let body = Body::from(std::fs::read_to_string(out_dir.join("index.html")).unwrap());
|
||||
// First try to find a 404.html or 404/index.html file
|
||||
let out_dir_404_html = out_dir.join("404.html");
|
||||
let out_dir_404_index_html = out_dir.join("404").join("index.html");
|
||||
let path = if out_dir_404_html.exists() {
|
||||
out_dir_404_html
|
||||
} else if out_dir_404_index_html.exists() {
|
||||
out_dir_404_index_html
|
||||
} else {
|
||||
// If we can't find a 404.html or 404/index.html, just use the index.html
|
||||
out_dir.join("index.html")
|
||||
};
|
||||
let body = Body::from(std::fs::read_to_string(path).unwrap());
|
||||
|
||||
response = Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
//! This module contains utilities renderers use to integrate with the launch function.
|
||||
|
||||
/// A marker trait for platform configs. We use this marker to
|
||||
/// make sure that the user doesn't accidentally pass in a config
|
||||
/// builder instead of the config
|
||||
pub trait LaunchConfig: 'static {}
|
||||
|
||||
impl LaunchConfig for () {}
|
||||
@@ -12,6 +12,7 @@ mod events;
|
||||
mod fragment;
|
||||
mod generational_box;
|
||||
mod global_context;
|
||||
mod launch;
|
||||
mod mutations;
|
||||
mod nodes;
|
||||
mod properties;
|
||||
@@ -51,6 +52,7 @@ pub(crate) mod innerlude {
|
||||
pub use crate::fragment::*;
|
||||
pub use crate::generational_box::*;
|
||||
pub use crate::global_context::*;
|
||||
pub use crate::launch::*;
|
||||
pub use crate::mutations::*;
|
||||
pub use crate::nodes::*;
|
||||
pub use crate::properties::*;
|
||||
@@ -75,10 +77,10 @@ pub(crate) mod innerlude {
|
||||
pub use crate::innerlude::{
|
||||
fc_to_builder, generation, schedule_update, schedule_update_any, use_hook, vdom_is_rendering,
|
||||
AnyValue, Attribute, AttributeValue, CapturedError, Component, ComponentFunction, DynamicNode,
|
||||
Element, ElementId, Event, Fragment, HasAttributes, IntoDynNode, MarkerWrapper, Mutation,
|
||||
Mutations, NoOpMutations, Ok, Properties, Result, Runtime, ScopeId, ScopeState, SpawnIfAsync,
|
||||
Task, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VNodeInner, VPlaceholder,
|
||||
VText, VirtualDom, WriteMutations,
|
||||
Element, ElementId, Event, Fragment, HasAttributes, IntoDynNode, LaunchConfig, MarkerWrapper,
|
||||
Mutation, Mutations, NoOpMutations, Ok, Properties, Result, Runtime, ScopeId, ScopeState,
|
||||
SpawnIfAsync, Task, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VNodeInner,
|
||||
VPlaceholder, VText, VirtualDom, WriteMutations,
|
||||
};
|
||||
|
||||
/// The purpose of this module is to alleviate imports of many common types
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn check_app_exits(app: fn() -> Element) {
|
||||
}
|
||||
});
|
||||
|
||||
LaunchBuilder::desktop()
|
||||
dioxus::LaunchBuilder::desktop()
|
||||
.with_cfg(Config::new().with_window(WindowBuilder::new().with_visible(false)))
|
||||
.launch(app);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use dioxus_core::LaunchConfig;
|
||||
use std::borrow::Cow;
|
||||
use std::path::PathBuf;
|
||||
use tao::window::{Icon, WindowBuilder};
|
||||
@@ -51,6 +52,8 @@ pub struct Config {
|
||||
pub(crate) last_window_close_behavior: WindowCloseBehaviour,
|
||||
}
|
||||
|
||||
impl LaunchConfig for Config {}
|
||||
|
||||
pub(crate) type WryProtocol = (
|
||||
String,
|
||||
Box<dyn Fn(HttpRequest<Vec<u8>>) -> HttpResponse<Cow<'static, [u8]>> + 'static>,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub use crate::Config;
|
||||
use crate::Config;
|
||||
use crate::{
|
||||
app::App,
|
||||
ipc::{IpcMethod, UserWindowEvent},
|
||||
@@ -82,8 +82,8 @@ pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) -> !
|
||||
/// Launches the WebView and runs the event loop, with configuration and root props.
|
||||
pub fn launch(
|
||||
root: fn() -> Element,
|
||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any>>>,
|
||||
platform_config: Config,
|
||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
||||
platform_config: Vec<Box<dyn Any>>,
|
||||
) -> ! {
|
||||
let mut virtual_dom = VirtualDom::new(root);
|
||||
|
||||
@@ -91,5 +91,10 @@ pub fn launch(
|
||||
virtual_dom.insert_any_root_context(context());
|
||||
}
|
||||
|
||||
let platform_config = *platform_config
|
||||
.into_iter()
|
||||
.find_map(|cfg| cfg.downcast::<Config>().ok())
|
||||
.unwrap_or_default();
|
||||
|
||||
launch_virtual_dom(virtual_dom, platform_config)
|
||||
}
|
||||
@@ -35,11 +35,11 @@ fn main() {
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
rsx!{
|
||||
rsx! {
|
||||
div {
|
||||
"hello world!"
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ To launch an app, we use the `launch` method and use features in `Cargo.toml` to
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch(App);
|
||||
dioxus::launch(App);
|
||||
}
|
||||
|
||||
// The #[component] attribute streamlines component creation.
|
||||
@@ -239,7 +239,7 @@ Using components, templates, and hooks, we can build a simple app.
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch(App);
|
||||
dioxus::launch(App);
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
||||
@@ -42,7 +42,7 @@ To launch an app, we use the `launch` method and use features in `Cargo.toml` to
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch(App);
|
||||
dioxus::launch(App);
|
||||
}
|
||||
|
||||
// The #[component] attribute streamlines component creation.
|
||||
@@ -184,7 +184,7 @@ Using components, rsx, and hooks, we can build a simple app.
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch(App);
|
||||
dioxus::launch(App);
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
||||
+127
-251
@@ -2,20 +2,22 @@
|
||||
#![allow(clippy::new_without_default)]
|
||||
#![allow(unused)]
|
||||
use dioxus_config_macro::*;
|
||||
use dioxus_core::LaunchConfig;
|
||||
use std::any::Any;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A builder for a fullstack app.
|
||||
#[must_use]
|
||||
pub struct LaunchBuilder<Cfg: 'static = (), ContextFn: ?Sized = ValidContext> {
|
||||
launch_fn: LaunchFn<Cfg, ContextFn>,
|
||||
contexts: Vec<Box<ContextFn>>,
|
||||
|
||||
platform_config: Option<Cfg>,
|
||||
pub struct LaunchBuilder {
|
||||
launch_fn: LaunchFn,
|
||||
contexts: Vec<ContextFn>,
|
||||
configs: Vec<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
pub type LaunchFn<Cfg, Context> = fn(fn() -> Element, Vec<Box<Context>>, Cfg);
|
||||
pub type LaunchFn = fn(fn() -> Element, Vec<ContextFn>, Vec<Box<dyn Any>>);
|
||||
/// A context function is a Send and Sync closure that returns a boxed trait object
|
||||
pub type ContextFn = Box<dyn Fn() -> Box<dyn Any> + Send + Sync + 'static>;
|
||||
|
||||
#[cfg(any(
|
||||
feature = "fullstack",
|
||||
@@ -54,69 +56,74 @@ impl LaunchBuilder {
|
||||
note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```"
|
||||
)
|
||||
)]
|
||||
pub fn new() -> LaunchBuilder<current_platform::Config, ValidContext> {
|
||||
pub fn new() -> LaunchBuilder {
|
||||
LaunchBuilder {
|
||||
// We can't use the `current_platform::launch` function directly because it may return ! or ()
|
||||
launch_fn: |root, contexts, cfg| current_platform::launch(root, contexts, cfg),
|
||||
contexts: Vec::new(),
|
||||
platform_config: None,
|
||||
configs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Launch your web application.
|
||||
#[cfg(feature = "web")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "web")))]
|
||||
pub fn web() -> LaunchBuilder<dioxus_web::Config, UnsendContext> {
|
||||
pub fn web() -> LaunchBuilder {
|
||||
LaunchBuilder {
|
||||
launch_fn: dioxus_web::launch::launch,
|
||||
launch_fn: web_launch,
|
||||
contexts: Vec::new(),
|
||||
platform_config: None,
|
||||
configs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Launch your desktop application.
|
||||
#[cfg(feature = "desktop")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
|
||||
pub fn desktop() -> LaunchBuilder<dioxus_desktop::Config, UnsendContext> {
|
||||
pub fn desktop() -> LaunchBuilder {
|
||||
LaunchBuilder {
|
||||
launch_fn: |root, contexts, cfg| dioxus_desktop::launch::launch(root, contexts, cfg),
|
||||
contexts: Vec::new(),
|
||||
platform_config: None,
|
||||
configs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Launch your fullstack application.
|
||||
#[cfg(feature = "fullstack")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))]
|
||||
pub fn fullstack() -> LaunchBuilder<dioxus_fullstack::Config, SendContext> {
|
||||
/// Launch your fullstack axum server.
|
||||
#[cfg(all(feature = "fullstack", feature = "server"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "fullstack", feature = "server"))))]
|
||||
pub fn server() -> LaunchBuilder {
|
||||
LaunchBuilder {
|
||||
launch_fn: |root, contexts, cfg| dioxus_fullstack::launch::launch(root, contexts, cfg),
|
||||
launch_fn: |root, contexts, cfg| {
|
||||
dioxus_fullstack::server::launch::launch(root, contexts, cfg)
|
||||
},
|
||||
contexts: Vec::new(),
|
||||
platform_config: None,
|
||||
configs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Launch your static site generation application.
|
||||
#[cfg(feature = "static-generation")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "static-generation")))]
|
||||
pub fn static_generation() -> LaunchBuilder<dioxus_static_site_generation::Config, SendContext>
|
||||
{
|
||||
#[cfg(all(feature = "static-generation", feature = "server"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(all(feature = "static-generation", feature = "server")))
|
||||
)]
|
||||
pub fn static_generation() -> LaunchBuilder {
|
||||
LaunchBuilder {
|
||||
launch_fn: |root, contexts, cfg| {
|
||||
dioxus_static_site_generation::launch::launch(root, contexts, cfg)
|
||||
},
|
||||
contexts: Vec::new(),
|
||||
platform_config: None,
|
||||
configs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Launch your fullstack application.
|
||||
#[cfg(feature = "mobile")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
|
||||
pub fn mobile() -> LaunchBuilder<dioxus_mobile::Config, UnsendContext> {
|
||||
pub fn mobile() -> LaunchBuilder {
|
||||
LaunchBuilder {
|
||||
launch_fn: |root, contexts, cfg| dioxus_mobile::launch::launch(root, contexts, cfg),
|
||||
contexts: Vec::new(),
|
||||
platform_config: None,
|
||||
configs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +139,7 @@ impl LaunchBuilder {
|
||||
/// #[derive(Default)]
|
||||
/// struct Config;
|
||||
///
|
||||
/// fn my_custom_launcher(root: fn() -> Element, contexts: Vec<Box<dyn Any>>, cfg: Config) {
|
||||
/// fn my_custom_launcher(root: fn() -> Element, contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>, cfg: Vec<Box<dyn Any>>) {
|
||||
/// println!("launching with root: {:?}", root());
|
||||
/// loop {
|
||||
/// println!("running...");
|
||||
@@ -145,42 +152,24 @@ impl LaunchBuilder {
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// LaunchBuilder::custom(my_custom_launcher).launch(app);
|
||||
/// dioxus::LaunchBuilder::custom(my_custom_launcher).launch(app);
|
||||
/// ```
|
||||
pub fn custom<Config, Context: ?Sized>(
|
||||
launch_fn: LaunchFn<Config, Context>,
|
||||
) -> LaunchBuilder<Config, Context> {
|
||||
pub fn custom(launch_fn: LaunchFn) -> LaunchBuilder {
|
||||
LaunchBuilder {
|
||||
launch_fn,
|
||||
contexts: vec![],
|
||||
platform_config: None,
|
||||
configs: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fullstack platform builder
|
||||
impl<Cfg> LaunchBuilder<Cfg, UnsendContext> {
|
||||
/// Inject state into the root component's context that is created on the thread that the app is launched on.
|
||||
pub fn with_context_provider(mut self, state: impl Fn() -> Box<dyn Any> + 'static) -> Self {
|
||||
self.contexts.push(Box::new(state) as Box<UnsendContext>);
|
||||
self
|
||||
}
|
||||
|
||||
/// Inject state into the root component's context.
|
||||
pub fn with_context(mut self, state: impl Any + Clone + 'static) -> Self {
|
||||
self.contexts
|
||||
.push(Box::new(move || Box::new(state.clone())));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Cfg> LaunchBuilder<Cfg, SendContext> {
|
||||
impl LaunchBuilder {
|
||||
/// Inject state into the root component's context that is created on the thread that the app is launched on.
|
||||
pub fn with_context_provider(
|
||||
mut self,
|
||||
state: impl Fn() -> Box<dyn Any + Send + Sync> + Send + Sync + 'static,
|
||||
state: impl Fn() -> Box<dyn Any> + Send + Sync + 'static,
|
||||
) -> Self {
|
||||
self.contexts.push(Box::new(state) as Box<SendContext>);
|
||||
self.contexts.push(Box::new(state));
|
||||
self
|
||||
}
|
||||
|
||||
@@ -192,38 +181,10 @@ impl<Cfg> LaunchBuilder<Cfg, SendContext> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for converting a type into a platform-specific config:
|
||||
/// - A unit value will be converted into `None`
|
||||
/// - Any config will be converted into `Some(config)`
|
||||
/// - If the config is for another platform, it will be converted into `None`
|
||||
pub trait TryIntoConfig<Config = Self> {
|
||||
fn into_config(self, config: &mut Option<Config>);
|
||||
}
|
||||
|
||||
// A config can always be converted into itself
|
||||
impl<Cfg> TryIntoConfig<Cfg> for Cfg {
|
||||
fn into_config(self, config: &mut Option<Cfg>) {
|
||||
*config = Some(self);
|
||||
}
|
||||
}
|
||||
|
||||
// The unit type can be converted into the current platform config.
|
||||
// This makes it possible to use the `desktop!`, `web!`, etc macros with the launch API.
|
||||
#[cfg(any(
|
||||
feature = "liveview",
|
||||
feature = "desktop",
|
||||
feature = "mobile",
|
||||
feature = "web",
|
||||
feature = "fullstack"
|
||||
))]
|
||||
impl TryIntoConfig<current_platform::Config> for () {
|
||||
fn into_config(self, config: &mut Option<current_platform::Config>) {}
|
||||
}
|
||||
|
||||
impl<Cfg: Default + 'static, ContextFn: ?Sized> LaunchBuilder<Cfg, ContextFn> {
|
||||
impl LaunchBuilder {
|
||||
/// Provide a platform-specific config to the builder.
|
||||
pub fn with_cfg(mut self, config: impl TryIntoConfig<Cfg>) -> Self {
|
||||
config.into_config(&mut self.platform_config);
|
||||
pub fn with_cfg(mut self, config: impl LaunchConfig) -> Self {
|
||||
self.configs.push(Box::new(config));
|
||||
self
|
||||
}
|
||||
|
||||
@@ -231,7 +192,7 @@ impl<Cfg: Default + 'static, ContextFn: ?Sized> LaunchBuilder<Cfg, ContextFn> {
|
||||
#[cfg(any(feature = "static-generation", feature = "web"))]
|
||||
/// Launch your application.
|
||||
pub fn launch(self, app: fn() -> Element) {
|
||||
let cfg = self.platform_config.unwrap_or_default();
|
||||
let cfg = self.configs;
|
||||
|
||||
(self.launch_fn)(app, self.contexts, cfg)
|
||||
}
|
||||
@@ -239,7 +200,7 @@ impl<Cfg: Default + 'static, ContextFn: ?Sized> LaunchBuilder<Cfg, ContextFn> {
|
||||
#[cfg(not(any(feature = "static-generation", feature = "web")))]
|
||||
/// Launch your application.
|
||||
pub fn launch(self, app: fn() -> Element) -> ! {
|
||||
let cfg = self.platform_config.unwrap_or_default();
|
||||
let cfg = self.configs;
|
||||
|
||||
(self.launch_fn)(app, self.contexts, cfg);
|
||||
unreachable!("Launching an application will never exit")
|
||||
@@ -256,147 +217,67 @@ impl<Cfg: Default + 'static, ContextFn: ?Sized> LaunchBuilder<Cfg, ContextFn> {
|
||||
/// - `web`
|
||||
/// - `liveview`
|
||||
mod current_platform {
|
||||
macro_rules! if_else_cfg {
|
||||
(if $attr:meta { $($then:item)* } else { $($else:item)* }) => {
|
||||
$(
|
||||
#[cfg($attr)]
|
||||
$then
|
||||
)*
|
||||
$(
|
||||
#[cfg(not($attr))]
|
||||
$else
|
||||
)*
|
||||
};
|
||||
}
|
||||
use crate::prelude::TryIntoConfig;
|
||||
#[cfg(all(feature = "fullstack", feature = "server"))]
|
||||
pub use dioxus_fullstack::server::launch::*;
|
||||
|
||||
#[cfg(feature = "fullstack")]
|
||||
pub use dioxus_fullstack::launch::*;
|
||||
#[cfg(all(
|
||||
feature = "desktop",
|
||||
not(all(feature = "fullstack", feature = "server"))
|
||||
))]
|
||||
pub use dioxus_desktop::launch::*;
|
||||
|
||||
#[cfg(all(feature = "fullstack", feature = "axum"))]
|
||||
impl TryIntoConfig<crate::launch::current_platform::Config>
|
||||
for ::dioxus_fullstack::prelude::ServeConfigBuilder
|
||||
{
|
||||
fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {
|
||||
match config {
|
||||
Some(config) => config.set_server_config(self),
|
||||
None => {
|
||||
*config = Some(
|
||||
crate::launch::current_platform::Config::new().with_server_config(self),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(all(
|
||||
feature = "mobile",
|
||||
not(feature = "desktop"),
|
||||
not(all(feature = "fullstack", feature = "server"))
|
||||
))]
|
||||
pub use dioxus_mobile::launch::*;
|
||||
|
||||
#[cfg(all(
|
||||
all(feature = "static-generation", feature = "server"),
|
||||
not(all(feature = "fullstack", feature = "server")),
|
||||
not(feature = "desktop"),
|
||||
not(feature = "mobile")
|
||||
))]
|
||||
pub use dioxus_static_site_generation::launch::*;
|
||||
|
||||
#[cfg(all(
|
||||
feature = "web",
|
||||
not(all(feature = "fullstack", feature = "server")),
|
||||
not(all(feature = "static-generation", feature = "server")),
|
||||
not(feature = "desktop"),
|
||||
not(feature = "mobile"),
|
||||
))]
|
||||
pub fn launch(
|
||||
root: fn() -> dioxus_core::Element,
|
||||
contexts: Vec<super::ContextFn>,
|
||||
platform_config: Vec<Box<dyn std::any::Any>>,
|
||||
) {
|
||||
super::web_launch(root, contexts, platform_config);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "desktop", feature = "mobile"))]
|
||||
if_else_cfg! {
|
||||
if not(feature = "fullstack") {
|
||||
#[cfg(feature = "desktop")]
|
||||
pub use dioxus_desktop::launch::*;
|
||||
#[cfg(not(feature = "desktop"))]
|
||||
pub use dioxus_mobile::launch::*;
|
||||
} else {
|
||||
if_else_cfg! {
|
||||
if feature = "desktop" {
|
||||
impl TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_desktop::Config {
|
||||
fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {
|
||||
match config {
|
||||
Some(config) => config.set_desktop_config(self),
|
||||
None => *config = Some(crate::launch::current_platform::Config::new().with_desktop_config(self)),
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
impl TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_mobile::Config {
|
||||
fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {
|
||||
match config {
|
||||
Some(config) => config.set_mobile_cfg(self),
|
||||
None => *config = Some(crate::launch::current_platform::Config::new().with_mobile_cfg(self)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "static-generation")]
|
||||
if_else_cfg! {
|
||||
if all(not(feature = "fullstack"), not(feature = "desktop"), not(feature = "mobile")) {
|
||||
pub use dioxus_static_site_generation::launch::*;
|
||||
} else {
|
||||
impl TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_static_site_generation::Config {
|
||||
fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "web")]
|
||||
if_else_cfg! {
|
||||
if not(any(feature = "desktop", feature = "mobile", feature = "fullstack", feature = "static-generation")) {
|
||||
pub use dioxus_web::launch::*;
|
||||
} else {
|
||||
if_else_cfg! {
|
||||
if feature = "fullstack" {
|
||||
impl TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_web::Config {
|
||||
fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {
|
||||
match config {
|
||||
Some(config) => config.set_web_config(self),
|
||||
None => *config = Some(crate::launch::current_platform::Config::new().with_web_config(self)),
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
impl TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_web::Config {
|
||||
fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "liveview")]
|
||||
if_else_cfg! {
|
||||
if
|
||||
not(any(
|
||||
feature = "web",
|
||||
feature = "desktop",
|
||||
feature = "mobile",
|
||||
feature = "fullstack",
|
||||
feature = "static-generation"
|
||||
))
|
||||
{
|
||||
pub use dioxus_liveview::launch::*;
|
||||
} else {
|
||||
impl<R: ::dioxus_liveview::LiveviewRouter> TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_liveview::Config<R> {
|
||||
fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(all(
|
||||
feature = "liveview",
|
||||
not(all(feature = "fullstack", feature = "server")),
|
||||
not(all(feature = "static-generation", feature = "server")),
|
||||
not(feature = "desktop"),
|
||||
not(feature = "mobile"),
|
||||
not(feature = "web"),
|
||||
))]
|
||||
pub use dioxus_liveview::launch::*;
|
||||
|
||||
#[cfg(not(any(
|
||||
feature = "liveview",
|
||||
all(feature = "fullstack", feature = "server"),
|
||||
all(feature = "static-generation", feature = "server"),
|
||||
feature = "desktop",
|
||||
feature = "mobile",
|
||||
feature = "web",
|
||||
feature = "fullstack",
|
||||
feature = "static-generation"
|
||||
)))]
|
||||
pub type Config = ();
|
||||
|
||||
#[cfg(not(any(
|
||||
feature = "liveview",
|
||||
feature = "desktop",
|
||||
feature = "mobile",
|
||||
feature = "web",
|
||||
feature = "fullstack",
|
||||
feature = "static-generation"
|
||||
)))]
|
||||
pub fn launch(
|
||||
root: fn() -> dioxus_core::Element,
|
||||
contexts: Vec<Box<super::ValidContext>>,
|
||||
platform_config: (),
|
||||
contexts: Vec<super::ContextFn>,
|
||||
platform_config: Vec<Box<dyn std::any::Any>>,
|
||||
) -> ! {
|
||||
#[cfg(feature = "third-party-renderer")]
|
||||
panic!("No first party renderer feature enabled. It looks like you are trying to use a third party renderer. You will need to use the launch function from the third party renderer crate.");
|
||||
@@ -409,22 +290,6 @@ mod current_platform {
|
||||
macro_rules! impl_launch {
|
||||
($($return_type:tt),*) => {
|
||||
/// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options.
|
||||
// If you aren't using a third party renderer and this is not a docs.rs build, generate a warning about no renderer being enabled
|
||||
#[cfg_attr(
|
||||
all(not(any(
|
||||
docsrs,
|
||||
feature = "third-party-renderer",
|
||||
feature = "liveview",
|
||||
feature = "desktop",
|
||||
feature = "mobile",
|
||||
feature = "web",
|
||||
feature = "fullstack",
|
||||
feature = "static-generation"
|
||||
))),
|
||||
deprecated(
|
||||
note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```"
|
||||
)
|
||||
)]
|
||||
pub fn launch(app: fn() -> Element) -> $($return_type)* {
|
||||
#[allow(deprecated)]
|
||||
LaunchBuilder::new().launch(app)
|
||||
@@ -439,29 +304,40 @@ impl_launch!(());
|
||||
impl_launch!(!);
|
||||
|
||||
#[cfg(feature = "web")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "web")))]
|
||||
/// Launch your web application without any additional configuration. See [`LaunchBuilder`] for more options.
|
||||
pub fn launch_web(app: fn() -> Element) {
|
||||
LaunchBuilder::web().launch(app)
|
||||
}
|
||||
fn web_launch(
|
||||
root: fn() -> dioxus_core::Element,
|
||||
contexts: Vec<super::ContextFn>,
|
||||
platform_config: Vec<Box<dyn std::any::Any>>,
|
||||
) {
|
||||
// If the server feature is enabled, launch the client with hydration enabled
|
||||
#[cfg(any(feature = "static-generation", feature = "fullstack"))]
|
||||
{
|
||||
let platform_config = platform_config
|
||||
.into_iter()
|
||||
.find_map(|cfg| cfg.downcast::<dioxus_web::Config>().ok())
|
||||
.unwrap_or_default()
|
||||
.hydrate(true);
|
||||
|
||||
#[cfg(feature = "desktop")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
|
||||
/// Launch your desktop application without any additional configuration. See [`LaunchBuilder`] for more options.
|
||||
pub fn launch_desktop(app: fn() -> Element) {
|
||||
LaunchBuilder::desktop().launch(app)
|
||||
}
|
||||
let factory = move || {
|
||||
let mut vdom = dioxus_core::VirtualDom::new(root);
|
||||
for context in contexts {
|
||||
vdom.insert_any_root_context(context());
|
||||
}
|
||||
#[cfg(feature = "document")]
|
||||
{
|
||||
#[cfg(feature = "fullstack")]
|
||||
use dioxus_fullstack::document;
|
||||
#[cfg(all(feature = "static-generation", not(feature = "fullstack")))]
|
||||
use dioxus_static_site_generation::document;
|
||||
let document = std::rc::Rc::new(document::web::FullstackWebDocument)
|
||||
as std::rc::Rc<dyn crate::prelude::Document>;
|
||||
vdom.provide_root_context(document);
|
||||
}
|
||||
vdom
|
||||
};
|
||||
|
||||
#[cfg(feature = "fullstack")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))]
|
||||
/// Launch your fullstack application without any additional configuration. See [`LaunchBuilder`] for more options.
|
||||
pub fn launch_fullstack(app: fn() -> Element) {
|
||||
LaunchBuilder::fullstack().launch(app)
|
||||
}
|
||||
|
||||
#[cfg(feature = "mobile")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
|
||||
/// Launch your mobile application without any additional configuration. See [`LaunchBuilder`] for more options.
|
||||
pub fn launch_mobile(app: fn() -> Element) {
|
||||
LaunchBuilder::mobile().launch(app)
|
||||
dioxus_web::launch::launch_virtual_dom(factory(), platform_config)
|
||||
}
|
||||
#[cfg(not(any(feature = "static-generation", feature = "fullstack")))]
|
||||
dioxus_web::launch::launch(root, contexts, platform_config);
|
||||
}
|
||||
@@ -34,8 +34,7 @@ mod launch;
|
||||
|
||||
#[cfg(feature = "launch")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "launch")))]
|
||||
#[allow(deprecated)]
|
||||
pub use launch::launch;
|
||||
pub use crate::launch::*;
|
||||
|
||||
#[cfg(feature = "hooks")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "hooks")))]
|
||||
@@ -60,9 +59,6 @@ pub use dioxus_html as html;
|
||||
pub use dioxus_core_macro as core_macro;
|
||||
|
||||
pub mod prelude {
|
||||
#[cfg(feature = "launch")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "launch")))]
|
||||
pub use crate::launch::*;
|
||||
|
||||
#[cfg(feature = "hooks")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "hooks")))]
|
||||
|
||||
@@ -37,7 +37,77 @@ Full stack Dioxus in under 30 lines of code
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch(App);
|
||||
dioxus::launch(App);
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn App() -> Element {
|
||||
let mut meaning = use_signal(|| None);
|
||||
|
||||
rsx! {
|
||||
h1 { "Meaning of life: {meaning:?}" }
|
||||
button {
|
||||
onclick: move |_| async move {
|
||||
if let Ok(data) = get_meaning("life the universe and everything".into()).await {
|
||||
meaning.set(data);
|
||||
}
|
||||
},
|
||||
"Run a server function"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[server]
|
||||
async fn get_meaning(of: String) -> Result<Option<u32>, ServerFnError> {
|
||||
Ok(of.contains("life").then(|| 42))
|
||||
}
|
||||
```
|
||||
|
||||
## Axum Integration
|
||||
|
||||
If you have an existing Axum router or you need more control over the server, you can use the [`DioxusRouterExt`](https://docs.rs/dioxus-fullstack/0.6.0-alpha.2/dioxus_fullstack/prelude/trait.DioxusRouterExt.html) trait to integrate with your existing Axum router.
|
||||
|
||||
First, make sure your `axum` dependency is optional and enabled by the server feature flag. Axum cannot be compiled to wasm, so if it is enabled by default, it will cause a compile error:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
dioxus = { version = "*", features = ["fullstack"] }
|
||||
axum = { version = "0.7.0", optional = true }
|
||||
|
||||
[features]
|
||||
server = ["dep:axum", "dioxus/server"]
|
||||
web = ["dioxus/web"]
|
||||
```
|
||||
|
||||
Then we can set up dioxus with the axum server:
|
||||
|
||||
```rust, no_run
|
||||
#![allow(non_snake_case)]
|
||||
use dioxus::prelude::*;
|
||||
|
||||
// The entry point for the server
|
||||
#[cfg(feature = "server")]
|
||||
fn main() {
|
||||
// Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address
|
||||
// and we use the generated address the CLI gives us
|
||||
let address = dioxus_cli_config::RuntimeCLIArguments::fullstack_address_or_localhost();
|
||||
|
||||
// Set up the axum router
|
||||
let router = axum::Router::new()
|
||||
// You can add a dioxus application to the router with the `serve_dioxus_application` method
|
||||
// This will add a fallback route to the router that will serve your component and server functions
|
||||
.serve_dioxus_application(ServeConfigBuilder::default(), App);
|
||||
|
||||
// Finally, we can launch the server
|
||||
let router = router.into_make_service();
|
||||
let listener = tokio::net::TcpListener::bind(address).await.unwrap();
|
||||
axum::serve(listener, router).await.unwrap();
|
||||
}
|
||||
|
||||
// For any other platform, we just launch the app
|
||||
#[cfg(not(feature = "server"))]
|
||||
fn main() {
|
||||
dioxus::launch(App);
|
||||
}
|
||||
|
||||
#[component]
|
||||
@@ -111,7 +181,7 @@ fn main() {
|
||||
// For any other platform, we just launch the app
|
||||
#[cfg(not(feature = "server"))]
|
||||
fn main() {
|
||||
launch(App);
|
||||
dioxus::launch(App);
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
||||
@@ -1,289 +0,0 @@
|
||||
//! Launch helper macros for fullstack apps
|
||||
#![allow(unused)]
|
||||
use crate::prelude::*;
|
||||
use dioxus_lib::prelude::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Settings for a fullstack app.
|
||||
///
|
||||
/// Depending on what features are enabled, you can pass in configurations for each client platform as well as the server:
|
||||
/// ```rust, no_run
|
||||
/// # fn app() -> Element { todo!() }
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// let mut cfg = dioxus::fullstack::Config::new();
|
||||
///
|
||||
/// // Only set the server config if the server feature is enabled
|
||||
/// server_only! {
|
||||
/// cfg = cfg.with_server_config(ServeConfigBuilder::default());
|
||||
/// }
|
||||
///
|
||||
/// // Only set the web config if the web feature is enabled
|
||||
/// web! {
|
||||
/// cfg = cfg.with_web_config(dioxus::web::Config::default());
|
||||
/// }
|
||||
///
|
||||
/// // Only set the desktop config if the desktop feature is enabled
|
||||
/// desktop! {
|
||||
/// cfg = cfg.with_desktop_config(dioxus::desktop::Config::default());
|
||||
/// }
|
||||
///
|
||||
/// // Finally, launch the app with the config
|
||||
/// LaunchBuilder::new()
|
||||
/// .with_cfg(cfg)
|
||||
/// .launch(app);
|
||||
/// ```
|
||||
pub struct Config {
|
||||
#[cfg(feature = "server")]
|
||||
pub(crate) server_cfg: ServeConfigBuilder,
|
||||
|
||||
#[cfg(feature = "web")]
|
||||
pub(crate) web_cfg: dioxus_web::Config,
|
||||
|
||||
#[cfg(feature = "desktop")]
|
||||
pub(crate) desktop_cfg: dioxus_desktop::Config,
|
||||
|
||||
#[cfg(feature = "mobile")]
|
||||
pub(crate) mobile_cfg: dioxus_mobile::Config,
|
||||
}
|
||||
|
||||
#[allow(clippy::derivable_impls)]
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "server")]
|
||||
server_cfg: ServeConfigBuilder::new(),
|
||||
#[cfg(feature = "web")]
|
||||
web_cfg: dioxus_web::Config::default(),
|
||||
#[cfg(feature = "desktop")]
|
||||
desktop_cfg: dioxus_desktop::Config::default(),
|
||||
#[cfg(feature = "mobile")]
|
||||
mobile_cfg: dioxus_mobile::Config::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Create a new config for a fullstack app.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Set the incremental renderer config. The incremental config can be used to improve
|
||||
/// performance of heavy routes by caching the rendered html in memory and/or the file system.
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # fn app() -> Element { todo!() }
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// let mut cfg = dioxus::fullstack::Config::new();
|
||||
///
|
||||
/// // Only set the server config if the server feature is enabled
|
||||
/// server_only! {
|
||||
/// cfg = cfg.incremental(IncrementalRendererConfig::default().with_memory_cache_limit(10000));
|
||||
/// }
|
||||
///
|
||||
/// // Finally, launch the app with the config
|
||||
/// LaunchBuilder::new()
|
||||
/// .with_cfg(cfg)
|
||||
/// .launch(app);
|
||||
/// ```
|
||||
#[cfg(feature = "server")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
|
||||
pub fn incremental(self, cfg: IncrementalRendererConfig) -> Self {
|
||||
Self {
|
||||
server_cfg: self.server_cfg.incremental(cfg),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the server config
|
||||
/// ```rust, no_run
|
||||
/// # fn app() -> Element { todo!() }
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// let mut cfg = dioxus::fullstack::Config::new();
|
||||
///
|
||||
/// // Only set the server config if the server feature is enabled
|
||||
/// server_only! {
|
||||
/// cfg = cfg.with_server_config(ServeConfigBuilder::default());
|
||||
/// }
|
||||
///
|
||||
/// // Finally, launch the app with the config
|
||||
/// LaunchBuilder::new()
|
||||
/// .with_cfg(cfg)
|
||||
/// .launch(app);
|
||||
/// ```
|
||||
#[cfg(feature = "server")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
|
||||
pub fn with_server_config(self, server_cfg: ServeConfigBuilder) -> Self {
|
||||
Self { server_cfg, ..self }
|
||||
}
|
||||
|
||||
/// Set the server config by modifying the config in place
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # fn app() -> Element { todo!() }
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// let mut cfg = dioxus::fullstack::Config::new();
|
||||
///
|
||||
/// // Only set the server config if the server feature is enabled
|
||||
/// server_only! {
|
||||
/// cfg.set_server_config(ServeConfigBuilder::default());
|
||||
/// }
|
||||
///
|
||||
/// // Finally, launch the app with the config
|
||||
/// LaunchBuilder::new()
|
||||
/// .with_cfg(cfg)
|
||||
/// .launch(app);
|
||||
/// ```
|
||||
#[cfg(feature = "server")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
|
||||
pub fn set_server_config(&mut self, server_cfg: ServeConfigBuilder) {
|
||||
self.server_cfg = server_cfg;
|
||||
}
|
||||
|
||||
/// Set the web config
|
||||
/// ```rust, no_run
|
||||
/// # fn app() -> Element { todo!() }
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// let mut cfg = dioxus::fullstack::Config::new();
|
||||
///
|
||||
/// // Only set the web config if the server feature is enabled
|
||||
/// web! {
|
||||
/// cfg = cfg.with_web_config(dioxus::web::Config::default());
|
||||
/// }
|
||||
///
|
||||
/// // Finally, launch the app with the config
|
||||
/// LaunchBuilder::new()
|
||||
/// .with_cfg(cfg)
|
||||
/// .launch(app);
|
||||
/// ```
|
||||
#[cfg(feature = "web")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "web")))]
|
||||
pub fn with_web_config(self, web_cfg: dioxus_web::Config) -> Self {
|
||||
Self { web_cfg, ..self }
|
||||
}
|
||||
|
||||
/// Set the server config by modifying the config in place
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # fn app() -> Element { todo!() }
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// let mut cfg = dioxus::fullstack::Config::new();
|
||||
///
|
||||
/// // Only set the web config if the server feature is enabled
|
||||
/// web! {
|
||||
/// cfg.set_web_config(dioxus::web::Config::default());
|
||||
/// }
|
||||
///
|
||||
/// // Finally, launch the app with the config
|
||||
/// LaunchBuilder::new()
|
||||
/// .with_cfg(cfg)
|
||||
/// .launch(app);
|
||||
/// ```
|
||||
#[cfg(feature = "web")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "web")))]
|
||||
pub fn set_web_config(&mut self, web_cfg: dioxus_web::Config) {
|
||||
self.web_cfg = web_cfg;
|
||||
}
|
||||
|
||||
/// Set the desktop config
|
||||
/// ```rust, no_run
|
||||
/// # fn app() -> Element { todo!() }
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// let mut cfg = dioxus::fullstack::Config::new();
|
||||
///
|
||||
/// // Only set the desktop config if the server feature is enabled
|
||||
/// desktop! {
|
||||
/// cfg = cfg.with_desktop_config(dioxus::desktop::Config::default());
|
||||
/// }
|
||||
///
|
||||
/// // Finally, launch the app with the config
|
||||
/// LaunchBuilder::new()
|
||||
/// .with_cfg(cfg)
|
||||
/// .launch(app);
|
||||
/// ```
|
||||
#[cfg(feature = "desktop")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
|
||||
pub fn with_desktop_config(self, desktop_cfg: dioxus_desktop::Config) -> Self {
|
||||
Self {
|
||||
desktop_cfg,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the desktop config by modifying the config in place
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # fn app() -> Element { todo!() }
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// let mut cfg = dioxus::fullstack::Config::new();
|
||||
///
|
||||
/// // Only set the desktop config if the server feature is enabled
|
||||
/// desktop! {
|
||||
/// cfg.set_desktop_config(dioxus::desktop::Config::default());
|
||||
/// }
|
||||
///
|
||||
/// // Finally, launch the app with the config
|
||||
/// LaunchBuilder::new()
|
||||
/// .with_cfg(cfg)
|
||||
/// .launch(app);
|
||||
/// ```
|
||||
#[cfg(feature = "desktop")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
|
||||
pub fn set_desktop_config(&mut self, desktop_cfg: dioxus_desktop::Config) {
|
||||
self.desktop_cfg = desktop_cfg;
|
||||
}
|
||||
|
||||
/// Set the mobile config
|
||||
/// ```rust, no_run
|
||||
/// # fn app() -> Element { todo!() }
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// let mut cfg = dioxus::fullstack::Config::new();
|
||||
///
|
||||
/// // Only set the mobile config if the server feature is enabled
|
||||
/// mobile! {
|
||||
/// cfg = cfg.with_mobile_cfg(dioxus::mobile::Config::default());
|
||||
/// }
|
||||
///
|
||||
/// // Finally, launch the app with the config
|
||||
/// LaunchBuilder::new()
|
||||
/// .with_cfg(cfg)
|
||||
/// .launch(app);
|
||||
/// Set the mobile config.
|
||||
#[cfg(feature = "mobile")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
|
||||
pub fn with_mobile_cfg(self, mobile_cfg: dioxus_mobile::Config) -> Self {
|
||||
Self { mobile_cfg, ..self }
|
||||
}
|
||||
|
||||
/// Set the mobile config by modifying the config in place
|
||||
/// ```rust, no_run
|
||||
/// # fn app() -> Element { todo!() }
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// let mut cfg = dioxus::fullstack::Config::new();
|
||||
///
|
||||
/// // Only set the mobile config if the server feature is enabled
|
||||
/// mobile! {
|
||||
/// cfg.set_mobile_cfg(dioxus::mobile::Config::default());
|
||||
/// }
|
||||
///
|
||||
/// // Finally, launch the app with the config
|
||||
/// LaunchBuilder::new()
|
||||
/// .with_cfg(cfg)
|
||||
/// .launch(app);
|
||||
/// Set the mobile config.
|
||||
#[cfg(feature = "mobile")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
|
||||
pub fn set_mobile_cfg(&mut self, mobile_cfg: dioxus_mobile::Config) {
|
||||
self.mobile_cfg = mobile_cfg;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
//! This module contains the document providers for the fullstack platform.
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub(crate) mod server;
|
||||
pub mod server;
|
||||
#[cfg(feature = "server")]
|
||||
pub use server::ServerDocument;
|
||||
#[cfg(all(feature = "web", feature = "document"))]
|
||||
pub(crate) mod web;
|
||||
pub mod web;
|
||||
@@ -11,7 +11,8 @@ fn head_element_written_on_server() -> bool {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) struct FullstackWebDocument;
|
||||
/// A document provider for fullstack web clients
|
||||
pub struct FullstackWebDocument;
|
||||
|
||||
impl Document for FullstackWebDocument {
|
||||
fn new_evaluator(
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
//! This module contains the `launch` function, which is the main entry point for dioxus fullstack
|
||||
|
||||
use std::{any::Any, sync::Arc};
|
||||
|
||||
use dioxus_lib::prelude::{Element, VirtualDom};
|
||||
|
||||
pub use crate::Config;
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) type ContextProviders = Arc<
|
||||
Vec<Box<dyn Fn() -> Box<dyn std::any::Any + Send + Sync + 'static> + Send + Sync + 'static>>,
|
||||
>;
|
||||
|
||||
#[allow(unused)]
|
||||
fn virtual_dom_factory(
|
||||
root: fn() -> Element,
|
||||
contexts: ContextProviders,
|
||||
) -> impl Fn() -> VirtualDom + 'static {
|
||||
move || {
|
||||
let mut vdom = VirtualDom::new(root);
|
||||
for context in &*contexts {
|
||||
vdom.insert_any_root_context(context());
|
||||
}
|
||||
vdom
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
/// Launch a fullstack app with the given root component, contexts, and config.
|
||||
#[allow(unused)]
|
||||
pub fn launch(
|
||||
root: fn() -> Element,
|
||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
|
||||
platform_config: Config,
|
||||
) -> ! {
|
||||
let contexts = Arc::new(contexts);
|
||||
let factory = virtual_dom_factory(root, contexts.clone());
|
||||
#[cfg(all(feature = "server", not(target_arch = "wasm32")))]
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(async move {
|
||||
launch_server(platform_config, factory, contexts).await;
|
||||
});
|
||||
|
||||
unreachable!("Launching a fullstack app should never return")
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "server"), feature = "web"))]
|
||||
/// Launch a fullstack app with the given root component, contexts, and config.
|
||||
#[allow(unused)]
|
||||
pub fn launch(
|
||||
root: fn() -> Element,
|
||||
#[allow(unused_mut)] mut contexts: Vec<
|
||||
Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>,
|
||||
>,
|
||||
platform_config: Config,
|
||||
) {
|
||||
let contexts = Arc::new(contexts);
|
||||
let mut factory = virtual_dom_factory(root, contexts);
|
||||
let cfg = platform_config.web_cfg.hydrate(true);
|
||||
|
||||
#[cfg(feature = "document")]
|
||||
let factory = move || {
|
||||
let mut vdom = factory();
|
||||
let document = std::rc::Rc::new(crate::document::web::FullstackWebDocument)
|
||||
as std::rc::Rc<dyn dioxus_lib::prelude::document::Document>;
|
||||
vdom.provide_root_context(document);
|
||||
vdom
|
||||
};
|
||||
|
||||
dioxus_web::launch::launch_virtual_dom(factory(), cfg)
|
||||
}
|
||||
|
||||
#[cfg(all(not(any(feature = "server", feature = "web")), feature = "desktop"))]
|
||||
/// Launch a fullstack app with the given root component, contexts, and config.
|
||||
#[allow(unused)]
|
||||
pub fn launch(
|
||||
root: fn() -> Element,
|
||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
|
||||
platform_config: Config,
|
||||
) -> ! {
|
||||
let contexts = Arc::new(contexts);
|
||||
let factory = virtual_dom_factory(root, contexts);
|
||||
let cfg = platform_config.desktop_cfg;
|
||||
dioxus_desktop::launch::launch_virtual_dom(factory(), cfg)
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
not(any(feature = "server", feature = "web", feature = "desktop")),
|
||||
feature = "mobile"
|
||||
))]
|
||||
/// Launch a fullstack app with the given root component, contexts, and config.
|
||||
#[allow(unused)]
|
||||
pub fn launch(
|
||||
root: fn() -> Element,
|
||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
|
||||
platform_config: Config,
|
||||
) -> ! {
|
||||
let contexts = Arc::new(contexts);
|
||||
let factory = virtual_dom_factory(root, contexts.clone());
|
||||
let cfg = platform_config.mobile_cfg;
|
||||
dioxus_mobile::launch::launch_virtual_dom(factory(), cfg)
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
feature = "server",
|
||||
feature = "web",
|
||||
feature = "desktop",
|
||||
feature = "mobile"
|
||||
)))]
|
||||
/// Launch a fullstack app with the given root component, contexts, and config.
|
||||
#[allow(unused)]
|
||||
pub fn launch(
|
||||
root: fn() -> Element,
|
||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
|
||||
platform_config: Config,
|
||||
) -> ! {
|
||||
panic!("No platform feature enabled. Please enable one of the following features: axum, desktop, or web to use the launch API.")
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
#[allow(unused)]
|
||||
/// Launch a server application
|
||||
async fn launch_server(
|
||||
platform_config: Config,
|
||||
build_virtual_dom: impl Fn() -> VirtualDom + Send + Sync + 'static,
|
||||
context_providers: ContextProviders,
|
||||
) {
|
||||
// Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address
|
||||
// and we use the generated address the CLI gives us
|
||||
let address = dioxus_cli_config::fullstack_address_or_localhost();
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
{
|
||||
use crate::axum_adapter::DioxusRouterExt;
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut router =
|
||||
axum::Router::new().register_server_functions_with_context(context_providers);
|
||||
|
||||
#[cfg(not(any(feature = "desktop", feature = "mobile")))]
|
||||
{
|
||||
use crate::prelude::RenderHandleState;
|
||||
use crate::prelude::SSRState;
|
||||
|
||||
match platform_config.server_cfg.build() {
|
||||
Ok(cfg) => {
|
||||
router = router.serve_static_assets();
|
||||
|
||||
router = router.fallback(
|
||||
axum::routing::get(crate::axum_adapter::render_handler).with_state(
|
||||
RenderHandleState::new_with_virtual_dom_factory(cfg, build_virtual_dom),
|
||||
),
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::trace!("Failed to create render handler. This is expected if you are only using fullstack for desktop/mobile server functions: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let router = router.into_make_service();
|
||||
let listener = tokio::net::TcpListener::bind(address).await.unwrap();
|
||||
|
||||
axum::serve(listener, router).await.unwrap();
|
||||
}
|
||||
#[cfg(not(feature = "axum"))]
|
||||
{
|
||||
panic!("Launching with dioxus fullstack requires the axum feature. If you are using a community fullstack adapter, please check the documentation for that adapter to see how to launch the application.");
|
||||
}
|
||||
}
|
||||
@@ -10,13 +10,9 @@ mod html_storage;
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "axum")))]
|
||||
mod axum_adapter;
|
||||
pub mod server;
|
||||
|
||||
mod config;
|
||||
mod hooks;
|
||||
pub mod launch;
|
||||
|
||||
pub use config::*;
|
||||
|
||||
pub mod document;
|
||||
#[cfg(feature = "server")]
|
||||
@@ -39,7 +35,7 @@ pub mod prelude {
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "axum")))]
|
||||
pub use crate::axum_adapter::*;
|
||||
pub use crate::server::*;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
|
||||
|
||||
@@ -5,6 +5,8 @@ use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use dioxus_lib::prelude::dioxus_core::LaunchConfig;
|
||||
|
||||
/// 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)]
|
||||
pub struct ServeConfigBuilder {
|
||||
@@ -14,6 +16,8 @@ pub struct ServeConfigBuilder {
|
||||
pub(crate) incremental: Option<dioxus_isrg::IncrementalRendererConfig>,
|
||||
}
|
||||
|
||||
impl LaunchConfig for ServeConfigBuilder {}
|
||||
|
||||
impl ServeConfigBuilder {
|
||||
/// Create a new ServeConfigBuilder with incremental static generation disabled and the default index.html settings
|
||||
pub fn new() -> Self {
|
||||
@@ -239,6 +243,8 @@ pub struct ServeConfig {
|
||||
pub(crate) incremental: Option<dioxus_isrg::IncrementalRendererConfig>,
|
||||
}
|
||||
|
||||
impl LaunchConfig for ServeConfig {}
|
||||
|
||||
impl ServeConfig {
|
||||
/// Create a new ServeConfig
|
||||
pub fn new() -> Result<Self, UnableToLoadIndex> {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
//! A launch function that creates an axum router for the LaunchBuilder
|
||||
|
||||
use std::any::Any;
|
||||
|
||||
use dioxus_lib::prelude::*;
|
||||
|
||||
/// Launch a fullstack app with the given root component, contexts, and config.
|
||||
#[allow(unused)]
|
||||
pub fn launch(
|
||||
root: fn() -> Element,
|
||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
||||
platform_config: Vec<Box<dyn Any>>,
|
||||
) -> ! {
|
||||
use crate::{ServeConfig, ServeConfigBuilder};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(async move {
|
||||
let platform_config = platform_config
|
||||
.into_iter()
|
||||
.find_map(|cfg| {
|
||||
cfg.downcast::<ServeConfig>()
|
||||
.map(|cfg| Result::Ok(*cfg))
|
||||
.or_else(|cfg| {
|
||||
cfg.downcast::<ServeConfigBuilder>()
|
||||
.map(|builder| builder.build())
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.unwrap_or_else(ServeConfig::new);
|
||||
|
||||
// Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address
|
||||
// and we use the generated address the CLI gives us
|
||||
let address = dioxus_cli_config::fullstack_address_or_localhost();
|
||||
|
||||
use crate::server::DioxusRouterExt;
|
||||
|
||||
struct TryIntoResult(Result<ServeConfig, crate::UnableToLoadIndex>);
|
||||
|
||||
impl TryInto<ServeConfig> for TryIntoResult {
|
||||
type Error = crate::UnableToLoadIndex;
|
||||
|
||||
fn try_into(self) -> Result<ServeConfig, Self::Error> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut router =
|
||||
axum::Router::new().serve_dioxus_application(TryIntoResult(platform_config), root);
|
||||
|
||||
let router = router.into_make_service();
|
||||
let listener = tokio::net::TcpListener::bind(address).await.unwrap();
|
||||
|
||||
axum::serve(listener, router).await.unwrap();
|
||||
});
|
||||
|
||||
unreachable!("Launching a fullstack app should never return")
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
//! fn main() {
|
||||
//! #[cfg(feature = "web")]
|
||||
//! // Hydrate the application on the client
|
||||
//! launch(app);
|
||||
//! dioxus::launch(app);
|
||||
//! #[cfg(feature = "server")]
|
||||
//! {
|
||||
//! tokio::runtime::Runtime::new()
|
||||
@@ -52,6 +52,12 @@
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
pub mod launch;
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) type ContextProviders =
|
||||
Arc<Vec<Box<dyn Fn() -> Box<dyn std::any::Any> + Send + Sync + 'static>>>;
|
||||
|
||||
use axum::routing::*;
|
||||
use axum::{
|
||||
body::{self, Body},
|
||||
@@ -64,7 +70,6 @@ use http::header::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::launch::ContextProviders;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A extension trait with utilities for integrating Dioxus with your Axum router.
|
||||
@@ -177,8 +182,6 @@ where
|
||||
) -> Self {
|
||||
use http::method::Method;
|
||||
|
||||
let context_providers = Arc::new(context_providers);
|
||||
|
||||
for (path, method) in server_fn::axum::server_fn_paths() {
|
||||
tracing::trace!("Registering server function: {} {}", method, path);
|
||||
let context_providers = context_providers.clone();
|
||||
@@ -186,9 +189,10 @@ where
|
||||
handle_server_fns_inner(
|
||||
path,
|
||||
move |server_context| {
|
||||
for context_provider in context_providers.iter() {
|
||||
let context = context_provider();
|
||||
server_context.insert_any(context);
|
||||
for index in 0..context_providers.len() {
|
||||
let context_providers = context_providers.clone();
|
||||
server_context
|
||||
.insert_boxed_factory(Box::new(move || context_providers[index]()));
|
||||
}
|
||||
},
|
||||
req,
|
||||
@@ -3,8 +3,7 @@ use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
type SendSyncAnyMap =
|
||||
std::collections::HashMap<std::any::TypeId, Box<dyn Any + Send + Sync + 'static>>;
|
||||
type SendSyncAnyMap = std::collections::HashMap<std::any::TypeId, ContextType>;
|
||||
|
||||
/// A shared context for server functions that contains information about the request and middleware state.
|
||||
///
|
||||
@@ -29,6 +28,20 @@ pub struct DioxusServerContext {
|
||||
pub(crate) parts: Arc<RwLock<http::request::Parts>>,
|
||||
}
|
||||
|
||||
enum ContextType {
|
||||
Factory(Box<dyn Fn() -> Box<dyn Any> + Send + Sync>),
|
||||
Value(Box<dyn Any + Send + Sync>),
|
||||
}
|
||||
|
||||
impl ContextType {
|
||||
fn downcast<T: Clone + 'static>(&self) -> Option<T> {
|
||||
match self {
|
||||
ContextType::Value(value) => value.downcast_ref::<T>().cloned(),
|
||||
ContextType::Factory(factory) => factory().downcast::<T>().ok().map(|v| *v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::derivable_impls)]
|
||||
impl Default for DioxusServerContext {
|
||||
fn default() -> Self {
|
||||
@@ -103,21 +116,38 @@ mod server_fn_impl {
|
||||
self.shared_context
|
||||
.read()
|
||||
.get(&TypeId::of::<T>())
|
||||
.map(|v| v.downcast_ref::<T>().unwrap().clone())
|
||||
.map(|v| v.downcast::<T>().unwrap())
|
||||
}
|
||||
|
||||
/// Insert a value into the shared server context
|
||||
///
|
||||
///
|
||||
pub fn insert<T: Any + Send + Sync + 'static>(&self, value: T) {
|
||||
self.insert_any(Box::new(value));
|
||||
}
|
||||
|
||||
/// Insert a Boxed `Any` value into the shared server context
|
||||
pub fn insert_any(&self, value: Box<dyn Any + Send + Sync>) {
|
||||
/// Insert a boxed `Any` value into the shared server context
|
||||
pub fn insert_any(&self, value: Box<dyn Any + Send + Sync + 'static>) {
|
||||
self.shared_context
|
||||
.write()
|
||||
.insert((*value).type_id(), value);
|
||||
.insert((*value).type_id(), ContextType::Value(value));
|
||||
}
|
||||
|
||||
/// Insert a factory that creates a non-sync value for the shared server context
|
||||
pub fn insert_factory<F, T>(&self, value: F)
|
||||
where
|
||||
F: Fn() -> T + Send + Sync + 'static,
|
||||
T: 'static,
|
||||
{
|
||||
self.shared_context.write().insert(
|
||||
TypeId::of::<T>(),
|
||||
ContextType::Factory(Box::new(move || Box::new(value()))),
|
||||
);
|
||||
}
|
||||
|
||||
/// Insert a boxed factory that creates a non-sync value for the shared server context
|
||||
pub fn insert_boxed_factory(&self, value: Box<dyn Fn() -> Box<dyn Any> + Send + Sync>) {
|
||||
self.shared_context
|
||||
.write()
|
||||
.insert((*value()).type_id(), ContextType::Factory(value));
|
||||
}
|
||||
|
||||
/// Get the response parts from the server context
|
||||
@@ -324,7 +354,7 @@ impl<T: 'static> std::error::Error for NotFoundInServerContext<T> {}
|
||||
/// ```rust, no_run
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// LaunchBuilder::new()
|
||||
/// dioxus::LaunchBuilder::new()
|
||||
/// // You can provide context to your whole app (including server functions) with the `with_context` method on the launch builder
|
||||
/// .with_context(server_only! {
|
||||
/// 1234567890u32
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use dioxus_core::VirtualDom;
|
||||
use dioxus_core::{LaunchConfig, VirtualDom};
|
||||
|
||||
use crate::LiveviewRouter;
|
||||
|
||||
@@ -13,6 +13,8 @@ pub struct Config<R: LiveviewRouter> {
|
||||
route: String,
|
||||
}
|
||||
|
||||
impl<R: LiveviewRouter + 'static> LaunchConfig for Config<R> {}
|
||||
|
||||
impl<R: LiveviewRouter> Default for Config<R> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
use dioxus::{prelude::*, CapturedError};
|
||||
|
||||
fn main() {
|
||||
LaunchBuilder::fullstack().launch(app);
|
||||
dioxus::launch(app);
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
|
||||
@@ -12,7 +12,7 @@ use dioxus::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
fn main() {
|
||||
LaunchBuilder::fullstack().launch(app);
|
||||
dioxus::launch(app);
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
|
||||
@@ -8,9 +8,13 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
LaunchBuilder::static_generation().launch(app);
|
||||
#[cfg(feature = "web")]
|
||||
dioxus::LaunchBuilder::web().launch(app);
|
||||
#[cfg(feature = "server")]
|
||||
dioxus::LaunchBuilder::static_generation().launch(app);
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn app() -> Element {
|
||||
let mut count = use_signal(|| 12345);
|
||||
let server_data = use_server_future(get_server_data)?;
|
||||
|
||||
@@ -107,5 +107,5 @@ fn NestedSuspendedComponent(id: i32) -> Element {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
launch(app);
|
||||
dioxus::launch(app);
|
||||
}
|
||||
@@ -77,5 +77,5 @@ fn main() {
|
||||
.set_max_level(tracing::Level::TRACE)
|
||||
.build(),
|
||||
);
|
||||
launch(app);
|
||||
dioxus::launch(app);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ async fn main() {
|
||||
|
||||
#[cfg(not(feature = "liveview"))]
|
||||
fn main() {
|
||||
launch(app)
|
||||
dioxus::launch(app)
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
|
||||
@@ -64,5 +64,5 @@ fn main() {
|
||||
// .set_max_level(tracing::Level::TRACE)
|
||||
// .build(),
|
||||
// );
|
||||
launch(app);
|
||||
dioxus::launch(app);
|
||||
}
|
||||
@@ -53,5 +53,5 @@ fn main() {
|
||||
// .set_max_level(tracing::Level::TRACE)
|
||||
// .build(),
|
||||
// );
|
||||
launch(app);
|
||||
dioxus::launch(app);
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch(app)
|
||||
dioxus::launch(app)
|
||||
}
|
||||
|
||||
// Because signal is never read in this component, this component will not rerun when the signal changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch(app);
|
||||
dioxus::launch(app);
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch(app);
|
||||
dioxus::launch(app);
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch(app);
|
||||
dioxus::launch(app);
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch(app)
|
||||
dioxus::launch(app)
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch(app);
|
||||
dioxus::launch(app);
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
|
||||
@@ -4,7 +4,7 @@ use dioxus::prelude::*;
|
||||
use dioxus_signals::Signal;
|
||||
|
||||
fn main() {
|
||||
launch(app);
|
||||
dioxus::launch(app);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
|
||||
@@ -37,7 +37,7 @@ Full stack Dioxus in under 30 lines of code
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch(App);
|
||||
dioxus::launch(App);
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use dioxus_lib::prelude::dioxus_core::LaunchConfig;
|
||||
|
||||
/// Settings for a statically generated site that may be hydrated in the browser
|
||||
pub struct Config {
|
||||
#[cfg(feature = "server")]
|
||||
@@ -25,12 +27,10 @@ pub struct Config {
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub(crate) github_pages: bool,
|
||||
|
||||
#[cfg(feature = "web")]
|
||||
#[allow(unused)]
|
||||
pub(crate) web_cfg: dioxus_web::Config,
|
||||
}
|
||||
|
||||
impl LaunchConfig for Config {}
|
||||
|
||||
#[allow(clippy::derivable_impls)]
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
@@ -49,8 +49,6 @@ impl Default for Config {
|
||||
additional_routes: vec!["/".to_string()],
|
||||
#[cfg(feature = "server")]
|
||||
github_pages: false,
|
||||
#[cfg(feature = "web")]
|
||||
web_cfg: dioxus_web::Config::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,16 +142,6 @@ impl Config {
|
||||
}
|
||||
myself
|
||||
}
|
||||
|
||||
/// Set the web config.
|
||||
///
|
||||
/// This method will only effect the hydrated web renderer.
|
||||
#[cfg(feature = "web")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "web")))]
|
||||
pub fn web_cfg(self, web_cfg: dioxus_web::Config) -> Self {
|
||||
#[allow(clippy::needless_update)]
|
||||
Self { web_cfg, ..self }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
|
||||
@@ -2,25 +2,15 @@
|
||||
|
||||
use std::any::Any;
|
||||
|
||||
use dioxus_lib::prelude::{Element, VirtualDom};
|
||||
|
||||
pub use crate::Config;
|
||||
use dioxus_lib::prelude::Element;
|
||||
|
||||
/// Launch a fullstack app with the given root component, contexts, and config.
|
||||
#[allow(unused)]
|
||||
pub fn launch(
|
||||
root: fn() -> Element,
|
||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
|
||||
platform_config: Config,
|
||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
||||
platform_config: Vec<Box<dyn Any>>,
|
||||
) {
|
||||
let virtual_dom_factory = move || {
|
||||
let mut vdom = VirtualDom::new(root);
|
||||
for context in &contexts {
|
||||
vdom.insert_any_root_context(context());
|
||||
}
|
||||
vdom
|
||||
};
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
@@ -33,56 +23,15 @@ pub fn launch(
|
||||
use http::StatusCode;
|
||||
use tower_http::services::ServeDir;
|
||||
use tower_http::services::ServeFile;
|
||||
let platform_config = platform_config
|
||||
.into_iter()
|
||||
.find_map(|cfg| cfg.downcast::<crate::Config>().map(|cfg| *cfg).ok())
|
||||
.unwrap_or_else(crate::Config::new);
|
||||
|
||||
let github_pages = platform_config.github_pages;
|
||||
let path = platform_config.output_dir.clone();
|
||||
crate::ssg::generate_static_site(root, platform_config)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Serve the program if we are running with cargo
|
||||
if std::env::var_os("CARGO").is_some() || dioxus_cli_config::is_cli_enabled() {
|
||||
// Get the address the server should run on. If the CLI is running, the CLI proxies static generation into the main address
|
||||
// and we use the generated address the CLI gives us
|
||||
let serve_address = dioxus_cli_config::fullstack_address_or_localhost();
|
||||
|
||||
// Point the user to the CLI address if the CLI is running or the fullstack address if not
|
||||
println!(
|
||||
"Serving static files from {} at http://{serve_address}",
|
||||
path.display()
|
||||
);
|
||||
|
||||
let mut serve_dir =
|
||||
ServeDir::new(path.clone()).call_fallback_on_method_not_allowed(true);
|
||||
|
||||
let mut router = axum::Router::new();
|
||||
|
||||
// If we are acting like github pages, we need to serve the 404 page if the user requests a directory that doesn't exist
|
||||
router = if github_pages {
|
||||
router.fallback_service(
|
||||
serve_dir.fallback(ServeFile::new(path.join("404/index.html"))),
|
||||
)
|
||||
} else {
|
||||
router.fallback_service(serve_dir.fallback(get(|| async move {
|
||||
"The requested path does not exist"
|
||||
.to_string()
|
||||
.into_response()
|
||||
})))
|
||||
};
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(serve_address).await.unwrap();
|
||||
axum::serve(listener, router.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(not(feature = "server"))]
|
||||
{
|
||||
#[cfg(feature = "web")]
|
||||
{
|
||||
let cfg = platform_config.web_cfg.hydrate(true);
|
||||
dioxus_web::launch::launch_virtual_dom(virtual_dom_factory(), cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,3 +16,8 @@ pub(crate) mod ssg;
|
||||
pub mod prelude {
|
||||
pub use dioxus_fullstack::prelude::*;
|
||||
}
|
||||
|
||||
/// A document provider for static generation apps
|
||||
pub mod document {
|
||||
pub use dioxus_fullstack::document::*;
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use dioxus_router::prelude::*;
|
||||
use dioxus_ssr::renderer;
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::Config;
|
||||
|
||||
@@ -65,20 +65,24 @@ pub async fn generate_static_site(
|
||||
}
|
||||
|
||||
// Copy over the web output dir into the static output dir
|
||||
let assets_path = dioxus_cli_config::out_dir().unwrap_or("./dist".into());
|
||||
let out_path = dioxus_cli_config::out_dir().unwrap_or("./dist".into());
|
||||
|
||||
let assets_path = assets_path.join("public");
|
||||
let assets_path = out_path.join("public");
|
||||
|
||||
copy_static_files(&assets_path, &config.output_dir)?;
|
||||
let index_path = assets_path.join("index.html");
|
||||
let skip = vec![index_path.clone()];
|
||||
copy_static_files(&assets_path, &config.output_dir, &skip)?;
|
||||
|
||||
// Copy the output of the SSG build into the public directory so the CLI serves it
|
||||
copy_static_files(&config.output_dir, &assets_path, &[])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_static_files(src: &Path, dst: &Path) -> Result<(), std::io::Error> {
|
||||
let index_path = src.join("index.html");
|
||||
fn copy_static_files(src: &Path, dst: &Path, skip: &[PathBuf]) -> Result<(), std::io::Error> {
|
||||
let mut queue = vec![src.to_path_buf()];
|
||||
while let Some(path) = queue.pop() {
|
||||
if path == index_path {
|
||||
if skip.contains(&path) {
|
||||
continue;
|
||||
}
|
||||
if path.is_dir() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use dioxus_core::LaunchConfig;
|
||||
use wasm_bindgen::JsCast as _;
|
||||
|
||||
/// Configuration for the WebSys renderer for the Dioxus VirtualDOM.
|
||||
@@ -15,6 +16,8 @@ pub struct Config {
|
||||
pub(crate) default_panic_hook: bool,
|
||||
}
|
||||
|
||||
impl LaunchConfig for Config {}
|
||||
|
||||
pub(crate) enum ConfigRoot {
|
||||
RootName(String),
|
||||
RootNode(web_sys::Node),
|
||||
|
||||
@@ -9,13 +9,17 @@ use std::any::Any;
|
||||
/// For a builder API, see `LaunchBuilder` defined in the `dioxus` crate.
|
||||
pub fn launch(
|
||||
root: fn() -> Element,
|
||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any>>>,
|
||||
platform_config: Config,
|
||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
||||
platform_config: Vec<Box<dyn Any>>,
|
||||
) {
|
||||
let mut vdom = VirtualDom::new(root);
|
||||
for context in contexts {
|
||||
vdom.insert_any_root_context(context());
|
||||
}
|
||||
let platform_config = *platform_config
|
||||
.into_iter()
|
||||
.find_map(|cfg| cfg.downcast::<Config>().ok())
|
||||
.unwrap_or_default();
|
||||
launch_virtual_dom(vdom, platform_config)
|
||||
}
|
||||
|
||||
@@ -30,5 +34,5 @@ pub fn launch_virtual_dom(vdom: VirtualDom, platform_config: Config) {
|
||||
|
||||
/// Launch the web application with the given root component and config
|
||||
pub fn launch_cfg(root: fn() -> Element, platform_config: Config) {
|
||||
launch(root, Vec::new(), platform_config);
|
||||
launch(root, Vec::new(), vec![Box::new(platform_config)])
|
||||
}
|
||||
Reference in new issue
Block a user