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
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user