CLI: add --renderer argument (#4320)

* Add --renderer argument to the CLI

Signed-off-by: Nico Burns <nico@nicoburns.com>

* Make `--platform native` work

Signed-off-by: Nico Burns <nico@nicoburns.com>

* Emit `krates` error when cargo metadata fails

* Reinstate default "desktop" feature for examples

* Remove commented 'no platform specified' error

Signed-off-by: Nico Burns <nico@nicoburns.com>

* use the target tripple and bundle format as the source of truth for the build

* resolve renderer and bundle format

* add shorthand flags

* add headings to the help for shorthands

* resolve the renderer from the target triple if it isn't passed

* make value after --fullstack optional

* convert desktop -> mobile when targeting mobile webview

* use the new shorthand flags in the playwright tests

* fix clippy

* merge fix

* cleanup merge conflicts

* fix clippy

* remove nit

* restore platform as a legacy argument

* improve help message

* vendor openssl on native, pin server_fn

* print build errors as warnings

* add --desktop arg compat

* adjust cli headings

* prefer self.is_wasm_or_wasi()

---------

Signed-off-by: Nico Burns <nico@nicoburns.com>
Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
Co-authored-by: Evan Almloff <evanalmloff@gmail.com>
This commit is contained in:
authored and GitHub committed 2025-07-22 14:32:54 -07:00
1 parent 4848e714ae
commit b257e7e8ce
28 files changed
+998 -549

No files matched your search

+15 -12
View File
@@ -1,6 +1,7 @@
use crate::{
styles::{GLOW_STYLE, LINK_STYLE},
AppBuilder, BuildId, BuildMode, BuilderUpdate, Platform, Result, ServeArgs, TraceController,
AppBuilder, BuildId, BuildMode, BuilderUpdate, BundleFormat, Result, ServeArgs,
TraceController,
};
mod ansi_buffer;
@@ -122,15 +123,15 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) ->
// Received a message from the devtools server - currently we only use this for
// logging, so we just forward it the tui
ServeUpdate::WsMessage { msg, platform } => {
screen.push_ws_message(platform, &msg);
ServeUpdate::WsMessage { msg, bundle } => {
screen.push_ws_message(bundle, &msg);
}
// Wait for logs from the build engine
// These will cause us to update the screen
// We also can check the status of the builds here in case we have multiple ongoing builds
ServeUpdate::BuilderUpdate { id, update } => {
let platform = builder.get_build(id).unwrap().build.platform;
let bundle_format = builder.get_build(id).unwrap().build.bundle;
// Queue any logs to be printed if need be
screen.new_build_update(&update);
@@ -147,14 +148,14 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) ->
stage: BuildStage::Failed,
} => {
if exit_on_error {
bail!("Build failed for platform: {platform}");
bail!("Build failed for platform: {bundle_format}");
}
}
BuilderUpdate::Progress {
stage: BuildStage::Aborted,
} => {
if exit_on_error {
bail!("Build aborted for platform: {platform}");
bail!("Build aborted for platform: {bundle_format}");
}
}
BuilderUpdate::Progress { .. } => {}
@@ -206,22 +207,24 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) ->
}
},
BuilderUpdate::StdoutReceived { msg } => {
screen.push_stdio(platform, msg, tracing::Level::INFO);
screen.push_stdio(bundle_format, msg, tracing::Level::INFO);
}
BuilderUpdate::StderrReceived { msg } => {
screen.push_stdio(platform, msg, tracing::Level::ERROR);
screen.push_stdio(bundle_format, msg, tracing::Level::ERROR);
}
BuilderUpdate::ProcessExited { status } => {
if status.success() {
tracing::info!(
r#"Application [{platform}] exited gracefully.
r#"Application [{bundle_format}] exited gracefully.
• To restart the app, press `r` to rebuild or `o` to open
• To exit the server, press `ctrl+c`"#
);
} else {
tracing::error!("Application [{platform}] exited with error: {status}");
tracing::error!(
"Application [{bundle_format}] exited with error: {status}"
);
if exit_on_error {
bail!("Application [{platform}] exited with error: {status}");
bail!("Application [{bundle_format}] exited with error: {status}");
}
}
}
@@ -241,7 +244,7 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) ->
}
ServeUpdate::OpenApp => match builder.use_hotpatch_engine {
true if !matches!(builder.client.build.platform, Platform::Web) => {
true if !matches!(builder.client.build.bundle, BundleFormat::Web) => {
tracing::warn!(
"Opening a native app with hotpatching enabled requires a full rebuild..."
);
+10 -10
View File
@@ -1,6 +1,6 @@
use crate::{
serve::{ansi_buffer::ansi_string_to_line, ServeUpdate, WebServer},
BuildId, BuildStage, BuilderUpdate, Platform, TraceContent, TraceMsg, TraceSrc,
BuildId, BuildStage, BuilderUpdate, BundleFormat, TraceContent, TraceMsg, TraceSrc,
};
use cargo_metadata::diagnostic::Diagnostic;
use crossterm::{
@@ -316,12 +316,12 @@ impl Output {
/// Add a message from stderr to the logs
/// This will queue the stderr message as a TraceMsg and print it on the next render
/// We'll use the `App` TraceSrc for the msg, and whatever level is provided
pub fn push_stdio(&mut self, platform: Platform, msg: String, level: Level) {
self.push_log(TraceMsg::text(TraceSrc::App(platform), level, msg));
pub fn push_stdio(&mut self, bundle: BundleFormat, msg: String, level: Level) {
self.push_log(TraceMsg::text(TraceSrc::App(bundle), level, msg));
}
/// Push a message from the websocket to the logs
pub fn push_ws_message(&mut self, platform: Platform, message: &axum::extract::ws::Message) {
pub fn push_ws_message(&mut self, bundle: BundleFormat, message: &axum::extract::ws::Message) {
use dioxus_devtools_types::ClientMsg;
// We can only handle text messages from the websocket...
@@ -336,7 +336,7 @@ impl Output {
let msg = match res {
Ok(msg) => msg,
Err(err) => {
tracing::error!(dx_src = ?TraceSrc::Dev, "Error parsing message from {}: {} -> {:?}", platform, err, text.as_str());
tracing::error!(dx_src = ?TraceSrc::Dev, "Error parsing message from {}: {} -> {:?}", bundle, err, text.as_str());
return;
}
};
@@ -358,7 +358,7 @@ impl Output {
};
// We don't care about logging the app's message so we directly push it instead of using tracing.
self.push_log(TraceMsg::text(TraceSrc::App(platform), level, content));
self.push_log(TraceMsg::text(TraceSrc::App(bundle), level, content));
}
/// Change internal state based on the build engine's update
@@ -641,7 +641,7 @@ impl Output {
frame.render_widget(
Paragraph::new(Line::from(vec![
"Platform: ".gray(),
client.build.platform.expected_name().yellow(),
client.build.bundle.expected_name().yellow(),
if state.runner.is_fullstack() {
" + fullstack".yellow()
} else {
@@ -672,7 +672,7 @@ impl Output {
frame.render_widget_ref(
Paragraph::new(Line::from(vec![
if client.build.platform == Platform::Web {
if client.build.bundle == BundleFormat::Web {
"Serving at: ".gray()
} else {
"Server at: ".gray()
@@ -755,7 +755,7 @@ impl Output {
);
let server_address = match state.server.server_address() {
Some(address) => format!("http://{}", address).yellow(),
Some(address) => format!("http://{address}").yellow(),
None => "no address".dark_gray(),
};
frame.render_widget(
@@ -770,7 +770,7 @@ impl Output {
frame.render_widget(
Paragraph::new(Line::from(vec![
"Read the docs: ".gray(),
"https://dioxuslabs.com/0.6/docs".blue(),
"https://dioxuslabs.com/learn/0.7/".blue(),
])),
links_list[0],
);
+3 -4
View File
@@ -163,8 +163,7 @@ pub(crate) fn handle_proxy_error(e: Error) -> axum::http::Response<axum::body::B
axum::http::Response::builder()
.status(axum::http::StatusCode::INTERNAL_SERVER_ERROR)
.body(axum::body::Body::from(format!(
"Proxy connection failed: {:#?}",
e
"Proxy connection failed: {e:#?}"
)))
.unwrap()
}
@@ -229,7 +228,7 @@ mod test {
let server_addr = setup_servers(config).await;
assert_eq!(
reqwest::get(format!("http://{}/api", server_addr))
reqwest::get(format!("http://{server_addr}/api"))
.await
.unwrap()
.text()
@@ -239,7 +238,7 @@ mod test {
);
assert_eq!(
reqwest::get(format!("http://{}/api/", server_addr))
reqwest::get(format!("http://{server_addr}/api/"))
.await
.unwrap()
.text()
+11 -10
View File
@@ -1,7 +1,7 @@
use super::{AppBuilder, ServeUpdate, WebServer};
use crate::{
platform_override::CommandWithPlatformOverrides, BuildArtifacts, BuildId, BuildMode,
BuildTargets, BuilderUpdate, HotpatchModuleCache, Platform, Result, ServeArgs, TailwindCli,
BuildTargets, BuilderUpdate, BundleFormat, HotpatchModuleCache, Result, ServeArgs, TailwindCli,
TraceSrc, Workspace,
};
use anyhow::{bail, Context};
@@ -151,8 +151,8 @@ impl AppServer {
// All servers will end up behind us (the devserver) but on a different port
// This is so we can serve a loading screen as well as devtools without anything particularly fancy
let fullstack = server.is_some();
let should_proxy_port = match client.platform {
Platform::Server => true,
let should_proxy_port = match client.bundle {
BundleFormat::Server => true,
_ => fullstack && !ssg,
};
@@ -168,7 +168,7 @@ impl AppServer {
// Only start Tailwind watcher for client builds that serve assets (not server builds or fullstack mode)
// In fullstack mode, the client build's prebuild() handles Tailwind generation to avoid race conditions
let tw_watcher = if client.build.platform != Platform::Server && !fullstack {
let tw_watcher = if client.build.bundle != BundleFormat::Server && !fullstack {
TailwindCli::serve(
client.build.package_manifest_dir(),
client.build.config.application.tailwind_input.clone(),
@@ -536,7 +536,8 @@ impl AppServer {
use crate::styles::NOTE_STYLE;
tracing::info!(dx_src = ?TraceSrc::Dev, "Hotreloading: {NOTE_STYLE}{}{NOTE_STYLE:#}", file);
if !server.has_hotreload_sockets() && self.client.build.platform != Platform::Web {
if !server.has_hotreload_sockets() && self.client.build.bundle != BundleFormat::Web
{
tracing::warn!("No clients to hotreload - try reloading the app!");
}
@@ -554,8 +555,8 @@ impl AppServer {
devserver: &mut WebServer,
) -> Result<()> {
// Make sure to save artifacts regardless of if we're opening the app or not
match artifacts.platform {
Platform::Server => {
match artifacts.bundle {
BundleFormat::Server => {
if let Some(server) = self.server.as_mut() {
server.artifacts = Some(artifacts.clone());
}
@@ -661,7 +662,7 @@ impl AppServer {
// If the client is running on Android, we need to remove the port forwarding
// todo: use the android tools "adb"
if matches!(self.client.build.platform, Platform::Android) {
if matches!(self.client.build.bundle, BundleFormat::Android) {
if let Err(err) = Command::new(&self.workspace.android_tools()?.adb)
.arg("reverse")
.arg("--remove")
@@ -786,7 +787,7 @@ impl AppServer {
BuildId::CLIENT => {
// multiple tabs on web can cause this to be called incorrectly, and it doesn't
// make any sense anyways
if self.client.build.platform != Platform::Web {
if self.client.build.bundle != BundleFormat::Web {
if let Some(aslr_reference) = aslr_reference {
self.client.aslr_reference = Some(aslr_reference);
}
@@ -804,7 +805,7 @@ impl AppServer {
}
// Assign the runtime asset dir to the runner
if self.client.build.platform == Platform::Ios {
if self.client.build.bundle == BundleFormat::Ios {
// xcrun simctl get_app_container booted com.dioxuslabs
let res = Command::new("xcrun")
.arg("simctl")
+10 -11
View File
@@ -1,5 +1,5 @@
use crate::{
config::WebHttpsConfig, serve::ServeUpdate, BuildId, BuildStage, BuilderUpdate, Platform,
config::WebHttpsConfig, serve::ServeUpdate, BuildId, BuildStage, BuilderUpdate, BundleFormat,
Result, TraceSrc,
};
use anyhow::{bail, Context};
@@ -62,7 +62,7 @@ pub(crate) struct WebServer {
new_build_status_sockets: UnboundedReceiver<ConnectedWsClient>,
build_status: SharedStatus,
application_name: String,
platform: Platform,
bundle: BundleFormat,
}
pub(crate) struct ConnectedWsClient {
@@ -128,7 +128,7 @@ impl WebServer {
new_hot_reload_sockets: hot_reload_sockets_rx,
new_build_status_sockets: build_status_sockets_rx,
application_name: runner.app_name().to_string(),
platform: runner.client.build.platform,
bundle: runner.client.build.bundle,
})
}
@@ -163,7 +163,7 @@ impl WebServer {
drop(new_message);
// Update the socket with project info and current build status
let project_info = SharedStatus::new(Status::ClientInit { application_name: self.application_name.clone(), platform: self.platform });
let project_info = SharedStatus::new(Status::ClientInit { application_name: self.application_name.clone(), bundle: self.bundle });
if project_info.send_to(&mut new_socket.socket).await.is_ok() {
_ = self.build_status.send_to(&mut new_socket.socket).await;
self.build_status_sockets.push(new_socket);
@@ -175,7 +175,7 @@ impl WebServer {
}
Some((idx, message)) = new_message.next() => {
match message {
Some(Ok(msg)) => return ServeUpdate::WsMessage { msg, platform: Platform::Web },
Some(Ok(msg)) => return ServeUpdate::WsMessage { msg, bundle: BundleFormat::Web },
_ => {
drop(new_message);
_ = self.hot_reload_sockets.remove(idx);
@@ -375,8 +375,8 @@ impl WebServer {
}
pub fn server_address(&self) -> Option<SocketAddr> {
match self.platform {
Platform::Web | Platform::Server => Some(self.devserver_address()),
match self.bundle {
BundleFormat::Web | BundleFormat::Server => Some(self.devserver_address()),
_ => self.proxied_server_address(),
}
}
@@ -461,8 +461,7 @@ fn build_devserver_router(
Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::from(format!(
"Backend connection failed. The backend is likely still starting up. Please try again in a few seconds. Error: {:#?}",
error
"Backend connection failed. The backend is likely still starting up. Please try again in a few seconds. Error: {error:#?}"
)))
.unwrap()
},
@@ -577,7 +576,7 @@ fn build_serve_dir(runner: &AppServer) -> axum::routing::MethodRouter {
.handle_error(|error: Infallible| async move {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", error),
format!("Unhandled internal error: {error}"),
)
})
}
@@ -730,7 +729,7 @@ struct SharedStatus(Arc<RwLock<Status>>);
enum Status {
ClientInit {
application_name: String,
platform: Platform,
bundle: BundleFormat,
},
Building {
progress: f64,
+2 -2
View File
@@ -1,4 +1,4 @@
use crate::{BuildId, BuilderUpdate, Error, Platform, TraceMsg};
use crate::{BuildId, BuilderUpdate, BundleFormat, Error, TraceMsg};
use axum::extract::ws::Message as WsMessage;
use std::path::PathBuf;
@@ -13,7 +13,7 @@ pub(crate) enum ServeUpdate {
pid: Option<u32>,
},
WsMessage {
platform: Platform,
bundle: BundleFormat,
msg: WsMessage,
},