Merge branch 'master' into cli-config-library
This commit is contained in:
commit
23509ada9f
285 files changed
+13088
-4835
No files matched your search
@@ -1,4 +1,6 @@
|
||||
use crate::server::Platform;
|
||||
use crate::{
|
||||
cfg::ConfigOptsServe,
|
||||
server::{
|
||||
output::{print_console_info, PrettierOptions},
|
||||
setup_file_watcher,
|
||||
@@ -23,7 +25,14 @@ use plugin::PluginManager;
|
||||
|
||||
use super::HotReloadState;
|
||||
|
||||
pub async fn startup(config: CrateConfig) -> Result<()> {
|
||||
pub async fn startup(config: CrateConfig, serve: &ConfigOptsServe) -> Result<()> {
|
||||
startup_with_platform::<DesktopPlatform>(config, serve).await
|
||||
}
|
||||
|
||||
pub(crate) async fn startup_with_platform<P: Platform + Send + 'static>(
|
||||
config: CrateConfig,
|
||||
serve_cfg: &ConfigOptsServe,
|
||||
) -> Result<()> {
|
||||
// ctrl-c shutdown checker
|
||||
let _crate_config = config.clone();
|
||||
let _ = ctrlc::set_handler(move || {
|
||||
@@ -45,8 +54,6 @@ pub async fn startup(config: CrateConfig) -> Result<()> {
|
||||
|
||||
let hot_reload_tx = broadcast::channel(100).0;
|
||||
|
||||
clear_paths();
|
||||
|
||||
Some(HotReloadState {
|
||||
messages: hot_reload_tx.clone(),
|
||||
file_map: file_map.clone(),
|
||||
@@ -55,15 +62,18 @@ pub async fn startup(config: CrateConfig) -> Result<()> {
|
||||
false => None,
|
||||
};
|
||||
|
||||
serve(config, hot_reload_state).await?;
|
||||
serve::<P>(config, serve_cfg, hot_reload_state).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start the server without hot reload
|
||||
pub async fn serve(config: CrateConfig, hot_reload_state: Option<HotReloadState>) -> Result<()> {
|
||||
let (child, first_build_result) = start_desktop(&config)?;
|
||||
let currently_running_child: RwLock<Child> = RwLock::new(child);
|
||||
async fn serve<P: Platform + Send + 'static>(
|
||||
config: CrateConfig,
|
||||
serve: &ConfigOptsServe,
|
||||
hot_reload_state: Option<HotReloadState>,
|
||||
) -> Result<()> {
|
||||
let platform = RwLock::new(P::start(&config, serve)?);
|
||||
|
||||
log::info!("🚀 Starting development server...");
|
||||
|
||||
@@ -72,14 +82,7 @@ pub async fn serve(config: CrateConfig, hot_reload_state: Option<HotReloadState>
|
||||
let _watcher = setup_file_watcher(
|
||||
{
|
||||
let config = config.clone();
|
||||
|
||||
move || {
|
||||
let mut current_child = currently_running_child.write().unwrap();
|
||||
current_child.kill()?;
|
||||
let (child, result) = start_desktop(&config)?;
|
||||
*current_child = child;
|
||||
Ok(result)
|
||||
}
|
||||
move || platform.write().unwrap().rebuild(&config)
|
||||
},
|
||||
&config,
|
||||
None,
|
||||
@@ -87,19 +90,9 @@ pub async fn serve(config: CrateConfig, hot_reload_state: Option<HotReloadState>
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Print serve info
|
||||
print_console_info(
|
||||
&config,
|
||||
PrettierOptions {
|
||||
changed: vec![],
|
||||
warnings: first_build_result.warnings,
|
||||
elapsed_time: first_build_result.elapsed_time,
|
||||
},
|
||||
None,
|
||||
);
|
||||
|
||||
match hot_reload_state {
|
||||
Some(hot_reload_state) => {
|
||||
// The open interprocess sockets
|
||||
start_desktop_hot_reload(hot_reload_state).await?;
|
||||
}
|
||||
None => {
|
||||
@@ -111,7 +104,14 @@ pub async fn serve(config: CrateConfig, hot_reload_state: Option<HotReloadState>
|
||||
}
|
||||
|
||||
async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<()> {
|
||||
match LocalSocketListener::bind("@dioxusin") {
|
||||
let metadata = cargo_metadata::MetadataCommand::new()
|
||||
.no_deps()
|
||||
.exec()
|
||||
.unwrap();
|
||||
let target_dir = metadata.target_directory.as_std_path();
|
||||
let path = target_dir.join("dioxusin");
|
||||
clear_paths(&path);
|
||||
match LocalSocketListener::bind(path) {
|
||||
Ok(local_socket_stream) => {
|
||||
let aborted = Arc::new(Mutex::new(false));
|
||||
// States
|
||||
@@ -150,7 +150,11 @@ async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<()
|
||||
println!("Connected to hot reloading 🚀");
|
||||
}
|
||||
Err(err) => {
|
||||
if err.kind() != std::io::ErrorKind::WouldBlock {
|
||||
let error_string = err.to_string();
|
||||
// Filter out any error messages about a operation that may block and an error message that triggers on some operating systems that says "Waiting for a process to open the other end of the pipe" without WouldBlock being set
|
||||
let display_error = err.kind() != std::io::ErrorKind::WouldBlock
|
||||
&& !error_string.contains("Waiting for a process");
|
||||
if display_error {
|
||||
println!("Error connecting to hot reloading: {} (Hot reloading is a feature of the dioxus-cli. If you are not using the CLI, this error can be ignored)", err);
|
||||
}
|
||||
}
|
||||
@@ -183,17 +187,14 @@ async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_paths() {
|
||||
if cfg!(target_os = "macos") {
|
||||
fn clear_paths(file_socket_path: &std::path::Path) {
|
||||
if cfg!(unix) {
|
||||
// On unix, if you force quit the application, it can leave the file socket open
|
||||
// This will cause the local socket listener to fail to open
|
||||
// We check if the file socket is already open from an old session and then delete it
|
||||
let paths = ["./dioxusin", "./@dioxusin"];
|
||||
for path in paths {
|
||||
let path = std::path::PathBuf::from(path);
|
||||
if path.exists() {
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
|
||||
if file_socket_path.exists() {
|
||||
let _ = std::fs::remove_file(file_socket_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,9 +213,9 @@ fn send_msg(msg: HotReloadMsg, channel: &mut impl std::io::Write) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_desktop(config: &CrateConfig) -> Result<(Child, BuildResult)> {
|
||||
fn start_desktop(config: &CrateConfig, skip_assets: bool) -> Result<(RAIIChild, BuildResult)> {
|
||||
// Run the desktop application
|
||||
let result = crate::builder::build_desktop(config, true)?;
|
||||
let result = crate::builder::build_desktop(config, true, skip_assets)?;
|
||||
|
||||
match &config.executable {
|
||||
ExecutableType::Binary(name)
|
||||
@@ -224,9 +225,58 @@ pub fn start_desktop(config: &CrateConfig) -> Result<(Child, BuildResult)> {
|
||||
if cfg!(windows) {
|
||||
file.set_extension("exe");
|
||||
}
|
||||
let child = Command::new(file.to_str().unwrap()).spawn()?;
|
||||
let active = "DIOXUS_ACTIVE";
|
||||
let child = RAIIChild(
|
||||
Command::new(file.to_str().unwrap())
|
||||
.env(active, "true")
|
||||
.spawn()?,
|
||||
);
|
||||
|
||||
Ok((child, result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DesktopPlatform {
|
||||
currently_running_child: RAIIChild,
|
||||
skip_assets: bool,
|
||||
}
|
||||
|
||||
impl Platform for DesktopPlatform {
|
||||
fn start(config: &CrateConfig, serve: &ConfigOptsServe) -> Result<Self> {
|
||||
let (child, first_build_result) = start_desktop(config, serve.skip_assets)?;
|
||||
|
||||
log::info!("🚀 Starting development server...");
|
||||
|
||||
// Print serve info
|
||||
print_console_info(
|
||||
config,
|
||||
PrettierOptions {
|
||||
changed: vec![],
|
||||
warnings: first_build_result.warnings,
|
||||
elapsed_time: first_build_result.elapsed_time,
|
||||
},
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
currently_running_child: child,
|
||||
skip_assets: serve.skip_assets,
|
||||
})
|
||||
}
|
||||
|
||||
fn rebuild(&mut self, config: &CrateConfig) -> Result<BuildResult> {
|
||||
self.currently_running_child.0.kill()?;
|
||||
let (child, result) = start_desktop(config, self.skip_assets)?;
|
||||
self.currently_running_child = child;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
struct RAIIChild(Child);
|
||||
|
||||
impl Drop for RAIIChild {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.0.kill();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
use crate::{
|
||||
cfg::{ConfigOptsBuild, ConfigOptsServe},
|
||||
CrateConfig, Result, WebAssetConfigDropGuard,
|
||||
};
|
||||
|
||||
use super::{desktop, Platform};
|
||||
|
||||
pub async fn startup(config: CrateConfig, serve: &ConfigOptsServe) -> Result<()> {
|
||||
desktop::startup_with_platform::<FullstackPlatform>(config, serve).await
|
||||
}
|
||||
|
||||
fn start_web_build_thread(
|
||||
config: &CrateConfig,
|
||||
serve: &ConfigOptsServe,
|
||||
) -> std::thread::JoinHandle<Result<()>> {
|
||||
let serve = serve.clone();
|
||||
let target_directory = config.crate_dir.join(".dioxus").join("web");
|
||||
std::fs::create_dir_all(&target_directory).unwrap();
|
||||
std::thread::spawn(move || build_web(serve, &target_directory))
|
||||
}
|
||||
|
||||
struct FullstackPlatform {
|
||||
serve: ConfigOptsServe,
|
||||
desktop: desktop::DesktopPlatform,
|
||||
_config: WebAssetConfigDropGuard,
|
||||
}
|
||||
|
||||
impl Platform for FullstackPlatform {
|
||||
fn start(config: &CrateConfig, serve: &ConfigOptsServe) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let thread_handle = start_web_build_thread(config, serve);
|
||||
|
||||
let mut desktop_config = config.clone();
|
||||
let desktop_feature = serve.server_feature.clone();
|
||||
let features = &mut desktop_config.features;
|
||||
match features {
|
||||
Some(features) => {
|
||||
features.push(desktop_feature);
|
||||
}
|
||||
None => desktop_config.features = Some(vec![desktop_feature]),
|
||||
};
|
||||
let config = WebAssetConfigDropGuard::new();
|
||||
let desktop = desktop::DesktopPlatform::start(&desktop_config, serve)?;
|
||||
thread_handle
|
||||
.join()
|
||||
.map_err(|_| anyhow::anyhow!("Failed to join thread"))??;
|
||||
|
||||
Ok(Self {
|
||||
desktop,
|
||||
serve: serve.clone(),
|
||||
_config: config,
|
||||
})
|
||||
}
|
||||
|
||||
fn rebuild(&mut self, crate_config: &CrateConfig) -> Result<crate::BuildResult> {
|
||||
let thread_handle = start_web_build_thread(crate_config, &self.serve);
|
||||
let result = {
|
||||
let mut desktop_config = crate_config.clone();
|
||||
let desktop_feature = self.serve.server_feature.clone();
|
||||
let features = &mut desktop_config.features;
|
||||
match features {
|
||||
Some(features) => {
|
||||
features.push(desktop_feature);
|
||||
}
|
||||
None => desktop_config.features = Some(vec![desktop_feature]),
|
||||
};
|
||||
let _gaurd = FullstackServerEnvGuard::new(self.serve.force_debug, self.serve.release);
|
||||
self.desktop.rebuild(&desktop_config)
|
||||
};
|
||||
thread_handle
|
||||
.join()
|
||||
.map_err(|_| anyhow::anyhow!("Failed to join thread"))??;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fn build_web(serve: ConfigOptsServe, target_directory: &std::path::Path) -> Result<()> {
|
||||
let mut web_config: ConfigOptsBuild = serve.into();
|
||||
let web_feature = web_config.client_feature.clone();
|
||||
let features = &mut web_config.features;
|
||||
match features {
|
||||
Some(features) => {
|
||||
features.push(web_feature);
|
||||
}
|
||||
None => web_config.features = Some(vec![web_feature]),
|
||||
};
|
||||
web_config.platform = Some(crate::cfg::Platform::Web);
|
||||
|
||||
let _gaurd = FullstackWebEnvGuard::new(&web_config);
|
||||
crate::cli::build::Build { build: web_config }.build(None, Some(target_directory))
|
||||
}
|
||||
|
||||
// Debug mode web builds have a very large size by default. If debug mode is not enabled, we strip some of the debug info by default
|
||||
// This reduces a hello world from ~40MB to ~2MB
|
||||
pub(crate) struct FullstackWebEnvGuard {
|
||||
old_rustflags: Option<String>,
|
||||
}
|
||||
|
||||
impl FullstackWebEnvGuard {
|
||||
pub fn new(serve: &ConfigOptsBuild) -> Self {
|
||||
Self {
|
||||
old_rustflags: (!serve.force_debug).then(|| {
|
||||
let old_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default();
|
||||
let debug_assertions = if serve.release {
|
||||
""
|
||||
} else {
|
||||
" -C debug-assertions"
|
||||
};
|
||||
|
||||
std::env::set_var(
|
||||
"RUSTFLAGS",
|
||||
format!(
|
||||
"{old_rustflags} -C debuginfo=none -C strip=debuginfo{debug_assertions}"
|
||||
),
|
||||
);
|
||||
old_rustflags
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FullstackWebEnvGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(old_rustflags) = self.old_rustflags.take() {
|
||||
std::env::set_var("RUSTFLAGS", old_rustflags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Debug mode web builds have a very large size by default. If debug mode is not enabled, we strip some of the debug info by default
|
||||
// This reduces a hello world from ~40MB to ~2MB
|
||||
pub(crate) struct FullstackServerEnvGuard {
|
||||
old_rustflags: Option<String>,
|
||||
}
|
||||
|
||||
impl FullstackServerEnvGuard {
|
||||
pub fn new(debug: bool, release: bool) -> Self {
|
||||
Self {
|
||||
old_rustflags: (!debug).then(|| {
|
||||
let old_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default();
|
||||
let debug_assertions = if release { "" } else { " -C debug-assertions" };
|
||||
|
||||
std::env::set_var(
|
||||
"RUSTFLAGS",
|
||||
format!("{old_rustflags} -C opt-level=2 {debug_assertions}"),
|
||||
);
|
||||
old_rustflags
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FullstackServerEnvGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(old_rustflags) = self.old_rustflags.take() {
|
||||
std::env::set_var("RUSTFLAGS", old_rustflags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::{cfg::ConfigOptsServe, BuildResult, Result};
|
||||
use crate::{BuildResult, Result};
|
||||
use dioxus_cli_config::CrateConfig;
|
||||
|
||||
@@ -12,6 +13,7 @@ use tokio::sync::broadcast::{self};
|
||||
mod output;
|
||||
use output::*;
|
||||
pub mod desktop;
|
||||
pub mod fullstack;
|
||||
pub mod web;
|
||||
|
||||
/// Sets up a file watcher
|
||||
@@ -133,6 +135,13 @@ async fn setup_file_watcher<F: Fn() -> Result<BuildResult> + Send + 'static>(
|
||||
Ok(watcher)
|
||||
}
|
||||
|
||||
pub(crate) trait Platform {
|
||||
fn start(config: &CrateConfig, serve: &ConfigOptsServe) -> Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
fn rebuild(&mut self, config: &CrateConfig) -> Result<BuildResult>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HotReloadState {
|
||||
pub messages: broadcast::Sender<Template<'static>>,
|
||||
|
||||
@@ -23,17 +23,20 @@ pub fn print_console_info(
|
||||
options: PrettierOptions,
|
||||
web_info: Option<WebServerInfo>,
|
||||
) {
|
||||
if let Ok(native_clearseq) = Command::new(if cfg!(target_os = "windows") {
|
||||
"cls"
|
||||
} else {
|
||||
"clear"
|
||||
})
|
||||
.output()
|
||||
{
|
||||
print!("{}", String::from_utf8_lossy(&native_clearseq.stdout));
|
||||
} else {
|
||||
// Try ANSI-Escape characters
|
||||
print!("\x1b[2J\x1b[H");
|
||||
// Don't clear the screen if the user has set the DIOXUS_LOG environment variable to "trace" so that we can see the logs
|
||||
if Some("trace") != std::env::var("DIOXUS_LOG").ok().as_deref() {
|
||||
if let Ok(native_clearseq) = Command::new(if cfg!(target_os = "windows") {
|
||||
"cls"
|
||||
} else {
|
||||
"clear"
|
||||
})
|
||||
.output()
|
||||
{
|
||||
print!("{}", String::from_utf8_lossy(&native_clearseq.stdout));
|
||||
} else {
|
||||
// Try ANSI-Escape characters
|
||||
print!("\x1b[2J\x1b[H");
|
||||
}
|
||||
}
|
||||
|
||||
let mut profile = if config.release { "Release" } else { "Debug" }.to_string();
|
||||
|
||||
@@ -11,6 +11,7 @@ use axum::{
|
||||
body::{Full, HttpBody},
|
||||
extract::{ws::Message, Extension, TypedHeader, WebSocketUpgrade},
|
||||
http::{
|
||||
self,
|
||||
header::{HeaderName, HeaderValue},
|
||||
Method, Response, StatusCode,
|
||||
},
|
||||
@@ -49,7 +50,12 @@ struct WsReloadState {
|
||||
update: broadcast::Sender<()>,
|
||||
}
|
||||
|
||||
pub async fn startup(port: u16, config: CrateConfig, start_browser: bool) -> Result<()> {
|
||||
pub async fn startup(
|
||||
port: u16,
|
||||
config: CrateConfig,
|
||||
start_browser: bool,
|
||||
skip_assets: bool,
|
||||
) -> Result<()> {
|
||||
// ctrl-c shutdown checker
|
||||
let _crate_config = config.clone();
|
||||
let _ = ctrlc::set_handler(move || {
|
||||
@@ -81,7 +87,15 @@ pub async fn startup(port: u16, config: CrateConfig, start_browser: bool) -> Res
|
||||
false => None,
|
||||
};
|
||||
|
||||
serve(ip, port, config, start_browser, hot_reload_state).await?;
|
||||
serve(
|
||||
ip,
|
||||
port,
|
||||
config,
|
||||
start_browser,
|
||||
skip_assets,
|
||||
hot_reload_state,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -92,9 +106,10 @@ pub async fn serve(
|
||||
port: u16,
|
||||
config: CrateConfig,
|
||||
start_browser: bool,
|
||||
skip_assets: bool,
|
||||
hot_reload_state: Option<HotReloadState>,
|
||||
) -> Result<()> {
|
||||
let first_build_result = crate::builder::build(&config, true)?;
|
||||
let first_build_result = crate::builder::build(&config, false, skip_assets)?;
|
||||
|
||||
log::info!("🚀 Starting development server...");
|
||||
|
||||
@@ -107,7 +122,7 @@ pub async fn serve(
|
||||
{
|
||||
let config = config.clone();
|
||||
let reload_tx = reload_tx.clone();
|
||||
move || build(&config, &reload_tx)
|
||||
move || build(&config, &reload_tx, skip_assets)
|
||||
},
|
||||
&config,
|
||||
Some(WebServerInfo {
|
||||
@@ -264,7 +279,7 @@ async fn setup_router(
|
||||
.override_response_header(HeaderName::from_static("cross-origin-opener-policy"), coop)
|
||||
.and_then(
|
||||
move |response: Response<ServeFileSystemResponseBody>| async move {
|
||||
let response = if file_service_config.dioxus_config.web.watcher.index_on_404
|
||||
let mut response = if file_service_config.dioxus_config.web.watcher.index_on_404
|
||||
&& response.status() == StatusCode::NOT_FOUND
|
||||
{
|
||||
let body = Full::from(
|
||||
@@ -287,6 +302,13 @@ async fn setup_router(
|
||||
} else {
|
||||
response.map(|body| body.boxed())
|
||||
};
|
||||
let headers = response.headers_mut();
|
||||
headers.insert(
|
||||
http::header::CACHE_CONTROL,
|
||||
HeaderValue::from_static("no-cache"),
|
||||
);
|
||||
headers.insert(http::header::PRAGMA, HeaderValue::from_static("no-cache"));
|
||||
headers.insert(http::header::EXPIRES, HeaderValue::from_static("0"));
|
||||
Ok(response)
|
||||
},
|
||||
)
|
||||
@@ -421,12 +443,12 @@ async fn ws_handler(
|
||||
})
|
||||
}
|
||||
|
||||
fn build(config: &CrateConfig, reload_tx: &Sender<()>) -> Result<BuildResult> {
|
||||
let result = builder::build(config, true)?;
|
||||
fn build(config: &CrateConfig, reload_tx: &Sender<()>, skip_assets: bool) -> Result<BuildResult> {
|
||||
let result = builder::build(config, true, skip_assets)?;
|
||||
// change the websocket reload state to true;
|
||||
// the page will auto-reload.
|
||||
if config.dioxus_config.web.watcher.reload_html {
|
||||
let _ = Serve::regen_dev_page(config);
|
||||
let _ = Serve::regen_dev_page(config, skip_assets);
|
||||
}
|
||||
let _ = reload_tx.send(());
|
||||
Ok(result)
|
||||
|
||||
Reference in new issue
Block a user