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

Generated
+11 -11
View File
@@ -5664,6 +5664,7 @@ dependencies = [
"dioxus-native-dom",
"futures-util",
"keyboard-types",
"openssl",
"rustc-hash 2.1.1",
"tokio",
"tracing",
@@ -14276,9 +14277,9 @@ dependencies = [
[[package]]
name = "serde_qs"
version = "0.14.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b417bedc008acbdf6d6b4bc482d29859924114bbe2650b7921fb68a261d0aa6"
checksum = "f3faaf9e727533a19351a43cc5a8de957372163c7d35cc48c90b75cdda13c352"
dependencies = [
"percent-encoding",
"serde",
@@ -14373,9 +14374,9 @@ dependencies = [
[[package]]
name = "server_fn"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09b0f92b9d3a62c73f238ac21f7a09f15bad335a9d1651514d9da80d2eaf8d4c"
checksum = "9c27fbd25ecc066481e383e2ed62ab2480e708aa3fe46cba36e95f58e61dfd04"
dependencies = [
"axum 0.8.4",
"base64 0.22.1",
@@ -14390,7 +14391,6 @@ dependencies = [
"hyper 1.6.0",
"inventory",
"js-sys",
"once_cell",
"pin-project-lite",
"reqwest 0.12.22",
"rustc_version",
@@ -14403,7 +14403,7 @@ dependencies = [
"thiserror 2.0.12",
"throw_error",
"tokio",
"tokio-tungstenite 0.26.2",
"tokio-tungstenite 0.27.0",
"tower 0.5.2",
"tower-layer",
"url",
@@ -14416,9 +14416,9 @@ dependencies = [
[[package]]
name = "server_fn_macro"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "341dd1087afe9f3e546c5979a4f0b6d55ac072e1201313f86e7fe364223835ac"
checksum = "b9d530c872590473016016679c94e3bddf47372941fc924f687ffd11a1778a71"
dependencies = [
"const_format",
"convert_case 0.8.0",
@@ -14431,9 +14431,9 @@ dependencies = [
[[package]]
name = "server_fn_macro_default"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5ab934f581482a66da82f2b57b15390ad67c9ab85bd9a6c54bb65060fb1380"
checksum = "ca7abc92ed696648275ed9ff171131a83d571af11748593dc2e6eb6a4e22a5b9"
dependencies = [
"server_fn_macro",
"syn 2.0.104",
@@ -16672,7 +16672,6 @@ checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
dependencies = [
"futures-util",
"log",
"rustls 0.23.29",
"tokio",
"tungstenite 0.26.2",
]
@@ -16685,6 +16684,7 @@ checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1"
dependencies = [
"futures-util",
"log",
"rustls 0.23.29",
"tokio",
"tungstenite 0.27.0",
]
+2 -2
View File
@@ -243,8 +243,8 @@ lru = "0.16.0"
async-trait = "0.1.88"
axum = { version = "0.8.4", default-features = false }
axum-server = { version = "0.7.2", default-features = false }
server_fn = { version = "0.8.2", default-features = false }
server_fn_macro = { version = "0.8.2" }
server_fn = { version = "=0.8.3", default-features = false }
server_fn_macro = { version = "=0.8.3" }
tower = "0.5.2"
http = "1.3.1"
notify = { version = "8.1.0" }
+41 -41
View File
@@ -1,5 +1,5 @@
use crate::{
serve::WebServer, BuildArtifacts, BuildRequest, BuildStage, BuilderUpdate, Platform,
serve::WebServer, BuildArtifacts, BuildRequest, BuildStage, BuilderUpdate, BundleFormat,
ProgressRx, ProgressTx, Result, RustcArgs, StructuredOutput,
};
use anyhow::{bail, Context};
@@ -16,6 +16,7 @@ use std::{
process::Stdio,
};
use subsecond_types::JumpTable;
use target_lexicon::Architecture;
use tokio::{
io::{AsyncBufReadExt, BufReader, Lines},
process::{Child, ChildStderr, ChildStdout, Command},
@@ -189,7 +190,7 @@ impl AppBuilder {
match bundle {
Ok(Ok(bundle)) => BuilderUpdate::BuildReady { bundle },
Ok(Err(err)) => BuilderUpdate::BuildFailed { err },
Err(err) => BuilderUpdate::BuildFailed { err: anyhow::anyhow!("Build panicked! {:#?}", err) },
Err(err) => BuilderUpdate::BuildFailed { err: anyhow::anyhow!("Build panicked! {err:#?}") },
}
},
Some(Ok(Some(msg))) = OptionFuture::from(self.stdout.as_mut().map(|f| f.next_line())) => {
@@ -300,7 +301,13 @@ impl AppBuilder {
// for all other platforms, we need to use the ASLR reference to know where to insert the patch.
let aslr_reference = match self.aslr_reference {
Some(val) => val,
None if self.build.platform == Platform::Web => 0,
None if matches!(
self.build.triple.architecture,
Architecture::Wasm32 | Architecture::Wasm64
) =>
{
0
}
None => {
tracing::warn!(
"Ignoring hotpatch since there is no ASLR reference. Is the client connected?"
@@ -542,18 +549,18 @@ impl AppBuilder {
);
// We try to use stdin/stdout to communicate with the app
match self.build.platform {
match self.build.bundle {
// Unfortunately web won't let us get a proc handle to it (to read its stdout/stderr) so instead
// use use the websocket to communicate with it. I wish we could merge the concepts here,
// like say, opening the socket as a subprocess, but alas, it's simpler to do that somewhere else.
Platform::Web => {
BundleFormat::Web => {
// Only the first build we open the web app, after that the user knows it's running
if open_browser {
self.open_web(open_address.unwrap_or(devserver_ip));
}
}
Platform::Ios => {
BundleFormat::Ios => {
if self.build.device {
self.codesign_ios().await?;
self.open_ios_device().await?
@@ -562,16 +569,15 @@ impl AppBuilder {
}
}
Platform::Android => {
BundleFormat::Android => {
self.open_android_sim(false, devserver_ip, envs).await?;
}
// These are all just basically running the main exe, but with slightly different resource dir paths
Platform::Server
| Platform::MacOS
| Platform::Windows
| Platform::Linux
| Platform::Liveview => self.open_with_main_exe(envs, args)?,
BundleFormat::Server
| BundleFormat::MacOS
| BundleFormat::Windows
| BundleFormat::Linux => self.open_with_main_exe(envs, args)?,
};
self.builds_opened += 1;
@@ -661,7 +667,7 @@ impl AppBuilder {
}
// If the emulator is android, we need to copy the asset to the device with `adb push asset /data/local/tmp/dx/assets/filename.ext`
if self.build.platform == Platform::Android {
if self.build.bundle == BundleFormat::Android {
let bundled_name = PathBuf::from(bundled.bundled_path());
_ = self.copy_file_to_android_tmp(&from, &bundled_name).await;
}
@@ -684,7 +690,7 @@ impl AppBuilder {
let mut jump_table = crate::build::create_jump_table(&new, &triple, cache)?;
// If it's android, we need to copy the assets to the device and then change the location of the patch
if self.build.platform == Platform::Android {
if self.build.bundle == BundleFormat::Android {
jump_table.lib = self
.copy_file_to_android_tmp(&new, &(PathBuf::from(new.file_name().unwrap())))
.await?;
@@ -777,7 +783,7 @@ impl AppBuilder {
}
// If the emulator is android, we need to copy the asset to the device with `adb push asset /data/local/tmp/dx/assets/filename.ext`
if self.build.platform == Platform::Android {
if self.build.bundle == BundleFormat::Android {
_ = self
.copy_file_to_android_tmp(&changed_file, &bundled_name)
.await;
@@ -972,7 +978,7 @@ impl AppBuilder {
.await?;
if !output.status.success() {
bail!("Failed to install app: {:?}", output);
bail!("Failed to install app: {output:?}");
}
let json: Value = serde_json::from_str(&std::fs::read_to_string(tmpfile.path())?)
@@ -1007,7 +1013,7 @@ impl AppBuilder {
.await?;
if !output.status.success() {
bail!("Failed to launch app: {:?}", output);
bail!("Failed to launch app: {output:?}");
}
let json: Value = serde_json::from_str(&std::fs::read_to_string(tmpfile.path())?)
@@ -1032,7 +1038,7 @@ impl AppBuilder {
.await?;
if !output.status.success() {
bail!("Failed to resume app: {:?}", output);
bail!("Failed to resume app: {output:?}");
}
Ok(())
@@ -1255,8 +1261,8 @@ We checked the folders:
let port = devserver_socket.port();
if let Err(e) = Command::new(&adb)
.arg("reverse")
.arg(format!("tcp:{}", port))
.arg(format!("tcp:{}", port))
.arg(format!("tcp:{port}"))
.arg(format!("tcp:{port}"))
.output()
.await
{
@@ -1362,18 +1368,14 @@ We checked the folders:
let mut main_exe = self.build.main_exe();
// The requirement here is based on the platform, not necessarily our current architecture.
let requires_entropy = match self.build.platform {
let requires_entropy = match self.build.bundle {
// When running "bundled", we don't need entropy
Platform::Web => false,
Platform::MacOS => false,
Platform::Ios => false,
Platform::Android => false,
BundleFormat::Web | BundleFormat::MacOS | BundleFormat::Ios | BundleFormat::Android => {
false
}
// But on platforms that aren't running as "bundled", we do.
Platform::Windows => true,
Platform::Linux => true,
Platform::Server => true,
Platform::Liveview => true,
BundleFormat::Windows | BundleFormat::Linux | BundleFormat::Server => true,
};
if requires_entropy || crate::devcfg::should_force_entropy() {
@@ -1443,24 +1445,22 @@ We checked the folders:
}
pub(crate) async fn open_debugger(&mut self, server: &WebServer) -> Result<()> {
let url = match self.build.platform {
Platform::MacOS
| Platform::Windows
| Platform::Linux
| Platform::Server
| Platform::Liveview => {
let url = match self.build.bundle {
BundleFormat::MacOS
| BundleFormat::Windows
| BundleFormat::Linux
| BundleFormat::Server => {
let Some(Some(pid)) = self.child.as_mut().map(|f| f.id()) else {
tracing::warn!("No process to attach debugger to");
return Ok(());
};
format!(
"vscode://vadimcn.vscode-lldb/launch/config?{{'request':'attach','pid':{}}}",
pid
"vscode://vadimcn.vscode-lldb/launch/config?{{'request':'attach','pid':{pid}}}"
)
}
Platform::Web => {
BundleFormat::Web => {
// code --open-url "vscode://DioxusLabs.dioxus/debugger?uri=http://127.0.0.1:8080"
// todo - debugger could open to the *current* page afaik we don't have a way to have that info
let address = server.devserver_address();
@@ -1474,7 +1474,7 @@ We checked the folders:
format!("vscode://DioxusLabs.dioxus/debugger?uri={protocol}://{address}{base_path}")
}
Platform::Ios => {
BundleFormat::Ios => {
let Some(pid) = self.pid else {
tracing::warn!("No process to attach debugger to");
return Ok(());
@@ -1513,7 +1513,7 @@ We checked the folders:
// (lldb) settings append target.exec-search-paths target/dx/tw6/debug/android/app/app/src/main/jniLibs/arm64-v8a/libdioxusmain.so
// (lldb) process handle SIGSEGV --pass true --stop false --notify true (otherwise the java threads cause crash)
//
Platform::Android => {
BundleFormat::Android => {
// adb push ./sdk/ndk/29.0.13113456/toolchains/llvm/prebuilt/darwin-x86_64/lib/clang/20/lib/linux/aarch64/lldb-server /tmp
// adb shell "/tmp/lldb-server --server --listen ..."
// "vscode://vadimcn.vscode-lldb/launch/config?{{'request':'connect','port': {}}}",
@@ -1601,7 +1601,7 @@ We checked the folders:
}
};
tracing::info!("Opening debugger for [{}]: {url}", self.build.platform);
tracing::info!("Opening debugger for [{}]: {url}", self.build.bundle);
_ = tokio::process::Command::new("code")
.arg("--open-url")
+1 -1
View File
@@ -105,7 +105,7 @@ impl BuildContext {
}
pub(crate) fn status_build_error(&self, line: String) {
tracing::debug!(dx_src = ?TraceSrc::Cargo, "{line}");
tracing::warn!(dx_src = ?TraceSrc::Cargo, "{line}");
}
pub(crate) fn status_build_message(&self, line: String) {
+2 -2
View File
@@ -775,7 +775,7 @@ pub fn create_undefined_symbol_stub(
let mut defined_symbols = HashSet::new();
for path in sorted {
let bytes = std::fs::read(path).with_context(|| format!("failed to read {:?}", path))?;
let bytes = std::fs::read(path).with_context(|| format!("failed to read {path:?}"))?;
let file = File::parse(bytes.deref() as &[u8])?;
for symbol in file.symbols() {
if symbol.is_undefined() {
@@ -853,7 +853,7 @@ pub fn create_undefined_symbol_stub(
if aslr_reference < aslr_ref_address {
return Err(PatchError::InvalidModule(
format!(
"ASLR reference is less than the main module's address - is there a `main`?. {:x} < {:x}", aslr_reference, aslr_ref_address )
"ASLR reference is less than the main module's address - is there a `main`?. {aslr_reference:x} < {aslr_ref_address:x}" )
));
}
File diff suppressed because it is too large. Load diff
+4 -4
View File
@@ -108,11 +108,11 @@ fn refactor_file(
let out = dioxus_autofmt::apply_formats(&s, edits);
if file == "-" {
print!("{}", out);
print!("{out}");
} else if let Err(e) = fs::write(&file, out) {
tracing::error!("failed to write formatted content to file: {e}",);
} else {
println!("formatted {}", file);
println!("formatted {file}");
}
Ok(())
@@ -191,7 +191,7 @@ fn autoformat_project(
let files_formatted: usize = counts.into_iter().flatten().sum();
if files_formatted > 0 && check {
bail!("{} files needed formatting", files_formatted);
bail!("{files_formatted} files needed formatting");
}
Ok(())
@@ -252,7 +252,7 @@ fn format_syn_error(err: syn::Error) -> Error {
let start = err.span().start();
let line = start.line;
let column = start.column;
anyhow::anyhow!("Syntax Error in line {} column {}:\n{}", line, column, err)
anyhow::anyhow!("Syntax Error in line {line} column {column}:\n{err}")
}
#[tokio::test]
+9 -5
View File
@@ -1,5 +1,5 @@
use crate::BuildMode;
use crate::{cli::*, AppBuilder, BuildRequest, Workspace};
use crate::{BuildMode, Platform};
use super::target::TargetArgs;
@@ -12,7 +12,11 @@ pub struct BuildArgs {
/// Enable fullstack mode [default: false]
///
/// This is automatically detected from `dx serve` if the "fullstack" feature is enabled by default.
#[clap(long)]
#[arg(
long,
default_missing_value = "true",
num_args = 0..=1,
)]
pub(crate) fullstack: Option<bool>,
/// Pre-render all routes returned from the app's `/static_routes` endpoint [default: false]
@@ -48,7 +52,7 @@ impl BuildArgs {
// This involves modifying the BuildRequest to add the client features and server features
// only if we can properly detect that it's a fullstack build. Careful with this, since
// we didn't build BuildRequest to be generally mutable.
let default_server = client.enabled_platforms.contains(&Platform::Server);
let default_server = client.enabled_renderers.contains(&crate::Renderer::Server);
// Make sure we set the fullstack platform so we actually build the fullstack variant
// Users need to enable "fullstack" in their default feature set.
@@ -119,8 +123,8 @@ impl CommandWithPlatformOverrides<BuildArgs> {
// Copy the main target from the client to the server
let main_target = client.main_target.clone();
let mut server_args = server_args.clone();
// The platform in the server build is always set to Server
server_args.platform = Some(Platform::Server);
// The renderer in the server build is always set to Server
server_args.renderer.renderer = Some(crate::Renderer::Server);
server =
Some(BuildRequest::new(&server_args, Some(main_target), workspace.clone()).await?);
}
+12 -13
View File
@@ -1,4 +1,4 @@
use crate::{AppBuilder, BuildArgs, BuildMode, BuildRequest, Platform};
use crate::{AppBuilder, BuildArgs, BuildMode, BuildRequest, BundleFormat};
use anyhow::{bail, Context};
use path_absolutize::Absolutize;
use std::collections::HashMap;
@@ -55,7 +55,7 @@ impl Bundle {
}
// If we're building for iOS, we need to bundle the iOS bundle
if client.platform == Platform::Ios && self.package_types.is_none() {
if client.bundle == BundleFormat::Ios && self.package_types.is_none() {
self.package_types = Some(vec![crate::PackageType::IosBundle]);
}
@@ -67,9 +67,9 @@ impl Bundle {
}
// Create a list of bundles that we might need to copy
match client.platform {
match client.bundle {
// By default, mac/win/linux work with tauri bundle
Platform::MacOS | Platform::Linux | Platform::Windows => {
BundleFormat::MacOS | BundleFormat::Linux | BundleFormat::Windows => {
tracing::info!("Running desktop bundler...");
for bundle in Self::bundle_desktop(&client, &self.package_types)? {
bundles.extend(bundle.bundle_paths);
@@ -77,15 +77,14 @@ impl Bundle {
}
// Web/ios can just use their root_dir
Platform::Web => bundles.push(client.root_dir()),
Platform::Ios => {
BundleFormat::Web => bundles.push(client.root_dir()),
BundleFormat::Ios => {
tracing::warn!("iOS bundles are not currently codesigned! You will need to codesign the app before distributing.");
bundles.push(client.root_dir())
}
Platform::Server => bundles.push(client.root_dir()),
Platform::Liveview => bundles.push(client.root_dir()),
BundleFormat::Server => bundles.push(client.root_dir()),
Platform::Android => {
BundleFormat::Android => {
let aab = client
.android_gradle_bundle()
.await
@@ -145,16 +144,16 @@ impl Bundle {
let krate = &build;
let exe = build.main_exe();
_ = std::fs::remove_dir_all(krate.bundle_dir(build.platform));
_ = std::fs::remove_dir_all(krate.bundle_dir(build.bundle));
let package = krate.package();
let mut name: PathBuf = krate.executable_name().into();
if cfg!(windows) {
name.set_extension("exe");
}
std::fs::create_dir_all(krate.bundle_dir(build.platform))
std::fs::create_dir_all(krate.bundle_dir(build.bundle))
.context("Failed to create bundle directory")?;
std::fs::copy(&exe, krate.bundle_dir(build.platform).join(&name))
std::fs::copy(&exe, krate.bundle_dir(build.bundle).join(&name))
.with_context(|| "Failed to copy the output executable into the bundle directory")?;
let binaries = vec![
@@ -223,7 +222,7 @@ impl Bundle {
}
let mut settings = SettingsBuilder::new()
.project_out_directory(krate.bundle_dir(build.platform))
.project_out_directory(krate.bundle_dir(build.bundle))
.package_settings(PackageSettings {
product_name: krate.bundled_app_name(),
version: package.version.to_string(),
+1 -1
View File
@@ -119,7 +119,7 @@ async fn check_files_and_report(files_to_check: Vec<PathBuf>) -> Result<()> {
Ok(())
}
1 => Err(anyhow!("1 issue found.")),
_ => Err(anyhow!("{} issues found.", total_issues)),
_ => Err(anyhow!("{total_issues} issues found.")),
}
}
+22 -20
View File
@@ -1,7 +1,7 @@
use super::*;
use crate::{
serve::{AppServer, ServeUpdate, WebServer},
BuilderUpdate, Platform, Result,
BuilderUpdate, BundleFormat, Result,
};
use anyhow::bail;
use dioxus_dx_wire_format::BuildStage;
@@ -43,7 +43,7 @@ impl RunArgs {
match msg {
ServeUpdate::BuilderUpdate { id, update } => {
let platform = builder.get_build(id).unwrap().build.platform;
let bundle_format = builder.get_build(id).unwrap().build.bundle;
// And then update the websocketed clients with the new build status in case they want it
devserver.new_build_update(&update).await;
@@ -59,7 +59,7 @@ impl RunArgs {
.await
.inspect_err(|e| tracing::error!("Failed to open app: {}", e));
if platform == Platform::Web {
if bundle_format == BundleFormat::Web {
tracing::info!(
"Serving app at http://{}:{}",
builder.devserver_bind_ip,
@@ -69,7 +69,7 @@ impl RunArgs {
}
BuilderUpdate::Progress { stage } => match stage {
BuildStage::Initializing => {
tracing::info!("[{platform}] Initializing build")
tracing::info!("[{bundle_format}] Initializing build")
}
BuildStage::Starting { .. } => {}
BuildStage::InstallingTooling => {}
@@ -79,15 +79,15 @@ impl RunArgs {
krate,
} => {
tracing::debug!(
"[{platform}] ({current}/{total}) Compiling {krate} ",
"[{bundle_format}] ({current}/{total}) Compiling {krate} ",
)
}
BuildStage::RunningBindgen => {
tracing::info!("[{platform}] Running WASM bindgen")
tracing::info!("[{bundle_format}] Running WASM bindgen")
}
BuildStage::SplittingBundle => {}
BuildStage::OptimizingWasm => {
tracing::info!("[{platform}] Optimizing WASM with `wasm-opt`")
tracing::info!("[{bundle_format}] Optimizing WASM with `wasm-opt`")
}
BuildStage::Linking => tracing::info!("Linking app"),
BuildStage::Hotpatching => {}
@@ -96,49 +96,51 @@ impl RunArgs {
total,
path,
} => tracing::info!(
"[{platform}] Copying asset {} ({current}/{total})",
"[{bundle_format}] Copying asset {} ({current}/{total})",
path.display(),
),
BuildStage::Bundling => tracing::info!("[{platform}] Bundling app"),
BuildStage::Bundling => {
tracing::info!("[{bundle_format}] Bundling app")
}
BuildStage::RunningGradle => {
tracing::info!("[{platform}] Running Gradle")
tracing::info!("[{bundle_format}] Running Gradle")
}
BuildStage::Success => {}
BuildStage::Restarting => {}
BuildStage::CompressingAssets => {}
BuildStage::ExtractingAssets => {}
BuildStage::Prerendering => {
tracing::info!("[{platform}] Prerendering app")
tracing::info!("[{bundle_format}] Prerendering app")
}
BuildStage::Failed => {
tracing::error!("[{platform}] Build failed");
bail!("Build failed for platform: {platform}");
tracing::error!("[{bundle_format}] Build failed");
bail!("Build failed for platform: {bundle_format}");
}
BuildStage::Aborted => {
tracing::error!("[{platform}] Build aborted");
bail!("Build aborted for platform: {platform}");
tracing::error!("[{bundle_format}] Build aborted");
bail!("Build aborted for platform: {bundle_format}");
}
_ => {}
},
BuilderUpdate::CompilerMessage { message } => {
print!("{}", message);
print!("{message}");
}
BuilderUpdate::BuildFailed { err } => {
tracing::error!("❌ Build failed: {}", err);
return Err(err);
}
BuilderUpdate::StdoutReceived { msg } => {
tracing::info!("[{platform}] {msg}");
tracing::info!("[{bundle_format}] {msg}");
}
BuilderUpdate::StderrReceived { msg } => {
tracing::error!("[{platform}] {msg}");
tracing::error!("[{bundle_format}] {msg}");
}
BuilderUpdate::ProcessExited { status } => {
if !status.success() {
tracing::error!(
"Application [{platform}] exited with error: {status}"
"Application [{bundle_format}] exited with error: {status}"
);
bail!("Application [{platform}] exited with error: {status}");
bail!("Application [{bundle_format}] exited with error: {status}");
}
break;
+3 -3
View File
@@ -6,15 +6,15 @@ use std::{backtrace::Backtrace, panic::AssertUnwindSafe};
/// Serve the project
///
/// `dx serve` takes cargo args by default, except with a required `--platform` arg:
/// `dx serve` takes cargo args by default with additional renderer args like `--web`, `--webview`, and `--native`:
///
/// ```sh
/// dx serve --example blah --target blah --platform android
/// dx serve --example blah --target blah --android
/// ```
///
/// A simple serve:
/// ```sh
/// dx serve --platform web
/// dx serve --web
/// ```
///
/// As of dioxus 0.7, `dx serve` allows independent customization of the client and server builds,
+22 -1
View File
@@ -1,5 +1,8 @@
use crate::cli::*;
use crate::BundleFormat;
use crate::Platform;
use crate::RendererArg;
use crate::TargetAlias;
use target_lexicon::Triple;
const HELP_HEADING: &str = "Target Options";
@@ -7,7 +10,25 @@ const HELP_HEADING: &str = "Target Options";
/// A single target to build for
#[derive(Clone, Debug, Default, Deserialize, Parser)]
pub(crate) struct TargetArgs {
/// Build platform: support Web & Desktop [default: "default_platform"]
/// The target alias to use for this build. Supports wasm, macos, windows, linux, ios, android, and host [default: "host"]
#[clap(flatten)]
pub(crate) target_alias: TargetAlias,
/// Build renderer: supports web, webview, native, server, and liveview
#[clap(flatten)]
pub(crate) renderer: RendererArg,
/// The bundle format to target for the build: supports web, macos, windows, linux, ios, android, and server
#[clap(long, value_enum, help_heading = HELP_HEADING)]
pub(crate) bundle: Option<BundleFormat>,
/// Build platform: supports Web, MacOS, Windows, Linux, iOS, Android, and Server
///
/// The platform implies a combination of the target alias, renderer, and bundle format flags.
///
/// You should generally prefer to use the `--web`, `--webview`, or `--native` flags to set the renderer
/// or the `--wasm`, `--macos`, `--windows`, `--linux`, `--ios`, or `--android` flags to set the target alias
/// instead of this flag. The renderer, target alias, and bundle format will be inferred if you only pass one.
#[clap(long, value_enum, help_heading = HELP_HEADING)]
pub(crate) platform: Option<Platform>,
+1 -1
View File
@@ -41,7 +41,7 @@ impl Translate {
// instead we should be printing as json (or maybe even a different format) if we're not interactive
match self.output {
Some(output) => std::fs::write(output, &html)?,
None => print!("{}", html),
None => print!("{html}"),
}
Ok(StructuredOutput::HtmlTranslate { html })
+1 -1
View File
@@ -341,7 +341,7 @@ impl FromStr for PackageType {
"appimage" => Ok(PackageType::AppImage),
"dmg" => Ok(PackageType::Dmg),
"updater" => Ok(PackageType::Updater),
_ => Err(format!("{} is not a valid package type", s)),
_ => Err(format!("{s} is not a valid package type")),
}
}
}
+11 -21
View File
@@ -14,10 +14,12 @@
//! 3. Build CLI layer for routing tracing logs to the TUI.
//! 4. Build fmt layer for non-interactive logging with a custom writer that prevents output during interactive mode.
use crate::{serve::ServeUpdate, Cli, Commands, Platform as TargetPlatform, Verbosity};
use crate::BundleFormat;
use crate::{serve::ServeUpdate, Cli, Commands, Verbosity};
use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel};
use clap::Parser;
use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use std::str::FromStr;
use std::sync::OnceLock;
use std::{
collections::HashMap,
@@ -315,7 +317,7 @@ impl Visit for CollectVisitor {
let name = field.name();
let mut value_string = String::new();
write!(value_string, "{:?}", value).unwrap();
write!(value_string, "{value:?}").unwrap();
if name == "message" {
self.message = value_string;
@@ -335,8 +337,8 @@ impl Visit for CollectVisitor {
fn format_field(field_name: &str, value: &dyn Debug) -> String {
let mut out = String::new();
match field_name {
"message" => write!(out, "{:?}", value),
_ => write!(out, "{}={:?}", field_name, value),
"message" => write!(out, "{value:?}"),
_ => write!(out, "{field_name}={value:?}"),
}
.unwrap();
@@ -391,7 +393,7 @@ impl TraceMsg {
#[derive(Clone, PartialEq)]
pub enum TraceSrc {
App(TargetPlatform),
App(BundleFormat),
Dev,
Build,
Bundle,
@@ -412,12 +414,9 @@ impl From<String> for TraceSrc {
"dev" => Self::Dev,
"bld" => Self::Build,
"cargo" => Self::Cargo,
"app" => Self::App(TargetPlatform::Web),
"windows" => Self::App(TargetPlatform::Windows),
"macos" => Self::App(TargetPlatform::MacOS),
"linux" => Self::App(TargetPlatform::Linux),
"server" => Self::App(TargetPlatform::Server),
_ => Self::Unknown,
other => BundleFormat::from_str(other)
.map(Self::App)
.unwrap_or_else(|_| Self::Unknown),
}
}
}
@@ -425,16 +424,7 @@ impl From<String> for TraceSrc {
impl Display for TraceSrc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::App(platform) => match platform {
TargetPlatform::Web => write!(f, "web"),
TargetPlatform::MacOS => write!(f, "macos"),
TargetPlatform::Windows => write!(f, "windows"),
TargetPlatform::Linux => write!(f, "linux"),
TargetPlatform::Server => write!(f, "server"),
TargetPlatform::Ios => write!(f, "ios"),
TargetPlatform::Android => write!(f, "android"),
TargetPlatform::Liveview => write!(f, "liveview"),
},
Self::App(bundle) => write!(f, "{bundle}"),
Self::Dev => write!(f, "dev"),
Self::Build => write!(f, "build"),
Self::Cargo => write!(f, "cargo"),
+507 -143
View File
@@ -1,6 +1,484 @@
use anyhow::{Context, Result};
use clap::{arg, ArgMatches, Args, FromArgMatches};
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::str::FromStr;
use target_lexicon::{Architecture, Environment, OperatingSystem, Triple};
use crate::Workspace;
#[derive(
Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default,
)]
#[non_exhaustive]
pub(crate) enum TargetAlias {
/// Targeting the WASM architecture
Wasm,
/// Targeting macos desktop
MacOS,
/// Targeting windows desktop
Windows,
/// Targeting linux desktop
Linux,
/// Targeting the ios platform
///
/// Can't work properly if you're not building from an Apple device.
Ios,
/// Targeting the android platform
Android,
/// Targeting the current target
Host,
/// An unknown target platform
#[default]
Unknown,
}
impl Args for TargetAlias {
fn augment_args(cmd: clap::Command) -> clap::Command {
const HELP_HEADING: &str = "Target Alias";
cmd.arg(arg!(--wasm "Target the wasm triple").help_heading(HELP_HEADING))
.arg(arg!(--macos "Target the macos triple").help_heading(HELP_HEADING))
.arg(arg!(--windows "Target the windows triple").help_heading(HELP_HEADING))
.arg(arg!(--linux "Target the linux triple").help_heading(HELP_HEADING))
.arg(arg!(--ios "Target the ios triple").help_heading(HELP_HEADING))
.arg(arg!(--android "Target the android triple").help_heading(HELP_HEADING))
.arg(arg!(--host "Target the host triple").help_heading(HELP_HEADING))
.arg(arg!(--desktop "Target the host triple").help_heading(HELP_HEADING))
.group(
clap::ArgGroup::new("target_alias")
.args([
"wasm", "macos", "windows", "linux", "ios", "android", "host", "host",
])
.multiple(false)
.required(false),
)
}
fn augment_args_for_update(cmd: clap::Command) -> clap::Command {
Self::augment_args(cmd)
}
}
impl FromArgMatches for TargetAlias {
fn from_arg_matches(matches: &ArgMatches) -> Result<Self, clap::Error> {
if let Some(platform) = matches.get_one::<clap::Id>("target_alias") {
match platform.as_str() {
"wasm" => Ok(Self::Wasm),
"macos" => Ok(Self::MacOS),
"windows" => Ok(Self::Windows),
"linux" => Ok(Self::Linux),
"ios" => Ok(Self::Ios),
"android" => Ok(Self::Android),
"host" => Ok(Self::Host),
_ => Err(clap::Error::raw(
clap::error::ErrorKind::InvalidValue,
format!("Unknown target alias: {platform}"),
)),
}
} else {
Ok(Self::Unknown)
}
}
fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), clap::Error> {
*self = Self::from_arg_matches(matches)?;
Ok(())
}
}
impl TargetAlias {
#[cfg(target_os = "macos")]
pub(crate) const TARGET_PLATFORM: Option<Self> = Some(Self::MacOS);
#[cfg(target_os = "windows")]
pub(crate) const TARGET_PLATFORM: Option<Self> = Some(Self::Windows);
#[cfg(target_os = "linux")]
pub(crate) const TARGET_PLATFORM: Option<Self> = Some(Self::Linux);
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
pub(crate) const TARGET_PLATFORM: Option<Self> = None;
pub(crate) async fn into_target(self, device: bool, workspace: &Workspace) -> Result<Triple> {
match self {
// Generally just use the host's triple for native executables unless specified otherwise
Self::MacOS | Self::Windows | Self::Linux | Self::Host | Self::Unknown => {
Ok(Triple::host())
}
// We currently assume unknown-unknown for web, but we might want to eventually
// support emscripten
Self::Wasm => Ok("wasm32-unknown-unknown".parse()?),
// For iOS we should prefer the actual architecture for the simulator, but in lieu of actually
// figuring that out, we'll assume aarch64 on m-series and x86_64 otherwise
Self::Ios => {
// use the host's architecture and sim if --device is passed
use target_lexicon::{Architecture, HOST};
let triple_str = match HOST.architecture {
Architecture::Aarch64(_) if device => "aarch64-apple-ios",
Architecture::Aarch64(_) => "aarch64-apple-ios-sim",
_ if device => "x86_64-apple-ios",
_ => "x86_64-apple-ios",
};
Ok(triple_str.parse()?)
}
// Same idea with android but we figure out the connected device using adb
Self::Android => Ok(workspace
.android_tools()?
.autodetect_android_device_triple()
.await),
}
}
pub(crate) fn or(self, other: Self) -> Self {
if self == Self::Unknown {
other
} else {
self
}
}
}
#[derive(
Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default,
)]
pub(crate) struct RendererArg {
pub(crate) renderer: Option<Renderer>,
}
impl Args for RendererArg {
fn augment_args(cmd: clap::Command) -> clap::Command {
const HELP_HEADING: &str = "Renderer";
cmd.arg(arg!(--web "Enable the dioxus web renderer").help_heading(HELP_HEADING))
.arg(arg!(--webview "Enable the dioxus webview renderer").help_heading(HELP_HEADING))
.arg(arg!(--native "Enable the dioxus native renderer").help_heading(HELP_HEADING))
.arg(arg!(--server "Enable the dioxus server renderer").help_heading(HELP_HEADING))
.arg(arg!(--liveview "Enable the dioxus liveview renderer").help_heading(HELP_HEADING))
.group(
clap::ArgGroup::new("renderer")
.args(["web", "webview", "native", "server", "liveview"])
.multiple(false)
.required(false),
)
}
fn augment_args_for_update(cmd: clap::Command) -> clap::Command {
Self::augment_args(cmd)
}
}
impl FromArgMatches for RendererArg {
fn from_arg_matches(matches: &ArgMatches) -> Result<Self, clap::Error> {
if let Some(renderer) = matches.get_one::<clap::Id>("renderer") {
match renderer.as_str() {
"web" => Ok(Self {
renderer: Some(Renderer::Web),
}),
"webview" => Ok(Self {
renderer: Some(Renderer::Webview),
}),
"native" => Ok(Self {
renderer: Some(Renderer::Native),
}),
"server" => Ok(Self {
renderer: Some(Renderer::Server),
}),
"liveview" => Ok(Self {
renderer: Some(Renderer::Liveview),
}),
_ => Err(clap::Error::raw(
clap::error::ErrorKind::InvalidValue,
format!("Unknown platform: {renderer}"),
)),
}
} else {
Ok(Self { renderer: None })
}
}
fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), clap::Error> {
*self = Self::from_arg_matches(matches)?;
Ok(())
}
}
impl From<RendererArg> for Option<Renderer> {
fn from(val: RendererArg) -> Self {
val.renderer
}
}
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug)]
#[non_exhaustive]
pub(crate) enum Renderer {
/// Targeting webview renderer
Webview,
/// Targeting native renderer
Native,
/// Targeting the server platform using Axum and Dioxus-Fullstack
///
/// This is implicitly passed if `fullstack` is enabled as a feature. Using this variant simply
/// means you're only building the server variant without the `.wasm` to serve.
Server,
/// Targeting the static generation platform using SSR and Dioxus-Fullstack
Liveview,
/// Targeting the web renderer
Web,
}
impl Renderer {
/// Get the feature name for the platform in the dioxus crate
pub(crate) fn feature_name(&self, target: &Triple) -> &str {
match self {
Renderer::Webview => match (target.environment, target.operating_system) {
(Environment::Android, _) | (_, OperatingSystem::IOS(_)) => "mobile",
_ => "desktop",
},
Renderer::Native => "native",
Renderer::Server => "server",
Renderer::Liveview => "liveview",
Renderer::Web => "web",
}
}
pub(crate) fn autodetect_from_cargo_feature(feature: &str) -> Option<Self> {
match feature {
"web" => Some(Self::Web),
"desktop" | "mobile" => Some(Self::Webview),
"native" => Some(Self::Native),
"liveview" => Some(Self::Liveview),
"server" => Some(Self::Server),
_ => None,
}
}
pub(crate) fn default_platform(&self) -> TargetAlias {
match self {
Renderer::Webview | Renderer::Native | Renderer::Server | Renderer::Liveview => {
TargetAlias::TARGET_PLATFORM.unwrap()
}
Renderer::Web => TargetAlias::Wasm,
}
}
pub(crate) fn from_target(triple: &Triple) -> Self {
match triple.architecture {
// Assume any wasm32 or wasm64 target is a web target
Architecture::Wasm32 | Architecture::Wasm64 => Self::Web,
// Otherwise, assume webview for native targets
_ => Self::Webview,
}
}
}
#[derive(Debug)]
pub(crate) struct UnknownRendererError;
impl std::error::Error for UnknownRendererError {}
impl std::fmt::Display for UnknownRendererError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Unknown renderer")
}
}
impl FromStr for Renderer {
type Err = UnknownRendererError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"webview" => Ok(Self::Webview),
"native" => Ok(Self::Native),
"server" => Ok(Self::Server),
"liveview" => Ok(Self::Liveview),
"web" => Ok(Self::Web),
_ => Err(UnknownRendererError),
}
}
}
impl Display for Renderer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Renderer::Webview => "webview",
Renderer::Native => "native",
Renderer::Server => "server",
Renderer::Liveview => "liveview",
Renderer::Web => "web",
})
}
}
#[derive(
Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default,
)]
#[non_exhaustive]
pub(crate) enum BundleFormat {
/// Targeting the web bundle structure
#[serde(rename = "web")]
#[default]
Web,
/// Targeting the macos desktop bundle structure
#[cfg_attr(target_os = "macos", serde(alias = "desktop"))]
#[serde(rename = "macos")]
MacOS,
/// Targeting the windows desktop bundle structure
#[cfg_attr(target_os = "windows", serde(alias = "desktop"))]
#[serde(rename = "windows")]
Windows,
/// Targeting the linux desktop bundle structure
#[cfg_attr(target_os = "linux", serde(alias = "desktop"))]
#[serde(rename = "linux")]
Linux,
/// Targeting the server bundle structure (a single binary placed next to the web build)
#[serde(rename = "server")]
Server,
/// Targeting the ios bundle structure
///
/// Can't work properly if you're not building from an Apple device.
#[serde(rename = "ios")]
Ios,
/// Targeting the android bundle structure
#[serde(rename = "android")]
Android,
}
#[derive(Debug)]
pub(crate) struct UnknownBundleFormatError;
impl std::error::Error for UnknownBundleFormatError {}
impl std::fmt::Display for UnknownBundleFormatError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Unknown bundle format")
}
}
impl FromStr for BundleFormat {
type Err = UnknownBundleFormatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"web" => Ok(Self::Web),
"macos" => Ok(Self::MacOS),
"windows" => Ok(Self::Windows),
"linux" => Ok(Self::Linux),
"server" => Ok(Self::Server),
"ios" => Ok(Self::Ios),
"android" => Ok(Self::Android),
_ => Err(UnknownBundleFormatError),
}
}
}
impl Display for BundleFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
BundleFormat::Web => "web",
BundleFormat::MacOS => "macos",
BundleFormat::Windows => "windows",
BundleFormat::Linux => "linux",
BundleFormat::Server => "server",
BundleFormat::Ios => "ios",
BundleFormat::Android => "android",
})
}
}
impl BundleFormat {
#[cfg(target_os = "macos")]
pub(crate) const TARGET_PLATFORM: Option<Self> = Some(Self::MacOS);
#[cfg(target_os = "windows")]
pub(crate) const TARGET_PLATFORM: Option<Self> = Some(Self::Windows);
#[cfg(target_os = "linux")]
pub(crate) const TARGET_PLATFORM: Option<Self> = Some(Self::Linux);
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
pub(crate) const TARGET_PLATFORM: Option<Self> = None;
/// Get the name of the folder we need to generate for this platform
///
/// Note that web and server share the same platform folder since we'll export the web folder as a bundle on its own
pub(crate) fn build_folder_name(&self) -> &'static str {
match self {
Self::Web => "web",
Self::Server => "web",
Self::Ios => "ios",
Self::Android => "android",
Self::Windows => "windows",
Self::Linux => "linux",
Self::MacOS => "macos",
}
}
pub(crate) fn profile_name(&self, release: bool) -> String {
let base_profile = match self {
Self::MacOS | Self::Windows | Self::Linux => "desktop",
Self::Web => "wasm",
Self::Ios => "ios",
Self::Android => "android",
Self::Server => "server",
};
let opt_level = if release { "release" } else { "dev" };
format!("{base_profile}-{opt_level}")
}
pub(crate) fn expected_name(&self) -> &'static str {
match self {
Self::Web => "Web",
Self::MacOS => "MacOS",
Self::Windows => "Windows",
Self::Linux => "Linux",
Self::Ios => "iOS",
Self::Android => "Android",
Self::Server => "Server",
}
}
pub(crate) fn from_target(target: &Triple, renderer: Option<Renderer>) -> Result<BundleFormat> {
match (
renderer,
target.architecture,
target.environment,
target.operating_system,
) {
// The server always uses the server bundle format
(Some(Renderer::Server), _, _, _) => Ok(BundleFormat::Server),
// The web renderer always uses the web bundle format
(Some(Renderer::Web), _, _, _) => Ok(BundleFormat::Web),
// Otherwise, guess it based on the target
// Assume any wasm32 or wasm64 target is a web target
(_, Architecture::Wasm32 | Architecture::Wasm64, _, _) => Ok(BundleFormat::Web),
// For native targets, we need to determine the bundle format based on the OS
(_, _, Environment::Android, _) => Ok(BundleFormat::Android),
(_, _, _, OperatingSystem::IOS(_)) => Ok(BundleFormat::Ios),
(_, _, _, OperatingSystem::MacOSX(_) | OperatingSystem::Darwin(_)) => {
Ok(BundleFormat::MacOS)
}
(_, _, _, OperatingSystem::Linux) => Ok(BundleFormat::Linux),
(_, _, _, OperatingSystem::Windows) => Ok(BundleFormat::Windows),
// If we don't recognize the target, default to desktop
_ => BundleFormat::TARGET_PLATFORM.context(
"failed to determine bundle format. Try setting the `--bundle` flag manually",
),
}
}
}
#[derive(
Copy,
@@ -18,188 +496,74 @@ use std::str::FromStr;
)]
#[non_exhaustive]
pub(crate) enum Platform {
/// Targeting the web platform using WASM
/// Alias for `--wasm --web --bundle-format web`
#[clap(name = "web")]
#[serde(rename = "web")]
#[default]
Web,
/// Targeting macos desktop
/// When running on macos, you can also use `--platform desktop` to build for the desktop
/// Alias for `--macos --webview --bundle-format macos`
#[cfg_attr(target_os = "macos", clap(alias = "desktop"))]
#[clap(name = "macos")]
#[serde(rename = "macos")]
MacOS,
/// Targeting windows desktop
/// When running on windows, you can also use `--platform desktop` to build for the desktop
/// Alias for `--windows --webview --bundle-format windows`
#[cfg_attr(target_os = "windows", clap(alias = "desktop"))]
#[clap(name = "windows")]
#[serde(rename = "windows")]
Windows,
/// Targeting linux desktop
/// When running on linux, you can also use `--platform desktop` to build for the desktop
/// Alias for `--linux --webview --bundle-format linux`
#[cfg_attr(target_os = "linux", clap(alias = "desktop"))]
#[clap(name = "linux")]
#[serde(rename = "linux")]
Linux,
/// Targeting the ios platform
///
/// Can't work properly if you're not building from an Apple device.
/// Alias for `--ios --webview --bundle-format ios`
#[clap(name = "ios")]
#[serde(rename = "ios")]
Ios,
/// Targeting the android platform
/// Alias for `--android --webview --bundle-format android`
#[clap(name = "android")]
#[serde(rename = "android")]
Android,
/// Targeting the server platform using Axum and Dioxus-Fullstack
///
/// This is implicitly passed if `fullstack` is enabled as a feature. Using this variant simply
/// means you're only building the server variant without the `.wasm` to serve.
/// Alias for `--host --server --bundle-format server`
#[clap(name = "server")]
#[serde(rename = "server")]
Server,
/// Targeting the static generation platform using SSR and Dioxus-Fullstack
/// Alias for `--host --liveview --bundle-format host`
#[clap(name = "liveview")]
#[serde(rename = "liveview")]
Liveview,
}
/// An error that occurs when a platform is not recognized
pub(crate) struct UnknownPlatformError;
impl std::fmt::Display for UnknownPlatformError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Unknown platform")
}
}
impl FromStr for Platform {
type Err = UnknownPlatformError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"web" => Ok(Self::Web),
"macos" => Ok(Self::MacOS),
"windows" => Ok(Self::Windows),
"linux" => Ok(Self::Linux),
"liveview" => Ok(Self::Liveview),
"server" => Ok(Self::Server),
"ios" => Ok(Self::Ios),
"android" => Ok(Self::Android),
_ => Err(UnknownPlatformError),
}
}
}
impl Display for Platform {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Platform::Web => "web",
Platform::MacOS => "macos",
Platform::Windows => "windows",
Platform::Linux => "linux",
Platform::Ios => "ios",
Platform::Android => "android",
Platform::Server => "server",
Platform::Liveview => "liveview",
})
}
}
impl Platform {
/// Get the feature name for the platform in the dioxus crate
pub(crate) fn feature_name(&self) -> &str {
pub(crate) fn into_triple(self) -> (TargetAlias, Renderer, BundleFormat) {
match self {
Platform::Web => "web",
Platform::MacOS => "desktop",
Platform::Windows => "desktop",
Platform::Linux => "desktop",
Platform::Server => "server",
Platform::Liveview => "liveview",
Platform::Ios => "mobile",
Platform::Android => "mobile",
}
}
/// Get the name of the folder we need to generate for this platform
///
/// Note that web and server share the same platform folder since we'll export the web folder as a bundle on its own
pub(crate) fn build_folder_name(&self) -> &'static str {
match self {
Platform::Web => "web",
Platform::Server => "web",
Platform::Liveview => "liveview",
Platform::Ios => "ios",
Platform::Android => "android",
Platform::Windows => "windows",
Platform::Linux => "linux",
Platform::MacOS => "macos",
}
}
pub(crate) fn expected_name(&self) -> &'static str {
match self {
Platform::Web => "Web",
Platform::MacOS => "Desktop MacOS",
Platform::Windows => "Desktop Windows",
Platform::Linux => "Desktop Linux",
Platform::Ios => "Mobile iOS",
Platform::Android => "Mobile Android",
Platform::Server => "Server",
Platform::Liveview => "Liveview",
}
}
pub(crate) fn autodetect_from_cargo_feature(feature: &str) -> Option<Self> {
match feature {
"web" => Some(Platform::Web),
"desktop" | "native" => {
#[cfg(target_os = "macos")]
{
Some(Platform::MacOS)
}
#[cfg(target_os = "windows")]
{
Some(Platform::Windows)
}
#[cfg(target_os = "linux")]
{
Some(Platform::Linux)
}
// Possibly need a something for freebsd? Maybe default to Linux?
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
{
None
}
}
"mobile" => None,
"liveview" => Some(Platform::Liveview),
"server" => Some(Platform::Server),
_ => None,
}
}
pub(crate) fn profile_name(&self, release: bool) -> String {
let base_profile = match self {
Platform::MacOS | Platform::Windows | Platform::Linux => "desktop",
Platform::Web => "web",
Platform::Ios => "ios",
Platform::Android => "android",
Platform::Server => "server",
Platform::Liveview => "liveview",
};
if release {
format!("{}-release", base_profile)
} else {
format!("{}-dev", base_profile)
Platform::Web => (TargetAlias::Wasm, Renderer::Web, BundleFormat::Web),
Platform::MacOS => (TargetAlias::MacOS, Renderer::Webview, BundleFormat::MacOS),
Platform::Windows => (
TargetAlias::Windows,
Renderer::Webview,
BundleFormat::Windows,
),
Platform::Linux => (TargetAlias::Linux, Renderer::Webview, BundleFormat::Linux),
Platform::Ios => (TargetAlias::Ios, Renderer::Webview, BundleFormat::Ios),
Platform::Android => (
TargetAlias::Android,
Renderer::Webview,
BundleFormat::Android,
),
Platform::Server => (TargetAlias::Host, Renderer::Server, BundleFormat::Server),
Platform::Liveview => (
TargetAlias::Host,
Renderer::Liveview,
BundleFormat::TARGET_PLATFORM.unwrap(),
),
}
}
}
+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,
},
+1 -1
View File
@@ -220,7 +220,7 @@ impl TailwindCli {
_ => return None,
};
Some(format!("tailwindcss-{}-{}", platform, arch))
Some(format!("tailwindcss-{platform}-{arch}"))
}
fn install_dir(&self) -> Result<PathBuf> {
+2 -1
View File
@@ -11,6 +11,7 @@
</div>
---
Dioxus is a framework for building cross-platform apps in Rust. With one codebase, you can build web, desktop, and mobile apps with fullstack server functions. Dioxus is designed to be easy to learn for developers familiar with web technologies like HTML, CSS, and JavaScript.
<div align="center">
@@ -42,7 +43,7 @@ cargo binstall dioxus-cli
dx new my-app && cd my-app
# and then serve!
dx serve --platform desktop
dx serve --desktop
```
## Your first app
+5 -1
View File
@@ -10,13 +10,14 @@ homepage = "https://dioxuslabs.com/learn/0.6/getting_started"
keywords = ["dom", "ui", "gui", "react"]
[features]
default = ["accessibility", "hot-reload", "tracing", "net", "svg", "system-fonts"]
default = ["accessibility", "hot-reload", "tracing", "net", "svg", "system-fonts", "openssl-vendored-android"]
svg = ["blitz-dom/svg", "blitz-paint/svg"]
net = ["dep:tokio", "dep:blitz-net"]
accessibility = ["blitz-shell/accessibility", "blitz-dom/accessibility"]
tracing = ["dep:tracing", "blitz-shell/tracing", "blitz-dom/tracing"]
hot-reload = ["dep:dioxus-cli-config", "dep:dioxus-devtools"]
system-fonts = ["blitz-dom/system_fonts"]
openssl-vendored-android = ["openssl"]
autofocus = []
[dependencies]
@@ -51,6 +52,9 @@ tracing = { workspace = true, optional = true }
rustc-hash = { workspace = true }
futures-util = { workspace = true }
[target.'cfg(target_os = "android")'.dependencies]
openssl = { version = "0.10", features = ["vendored"], optional = true }
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
@@ -18,8 +18,8 @@ Run the following command in the root of your project to start developing with t
dx serve
```
To run for a different platform, use the `--platform platform` flag. E.g.
```bash
dx serve --platform desktop
```
To run for a different platform, use the `--webview`, `--native`, or `--web` flags. E.g.
```bash
dx serve --webview
```
+11 -11
View File
@@ -87,7 +87,7 @@ module.exports = defineConfig({
{
cwd: path.join(process.cwd(), "web"),
command:
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 9990',
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 9990',
port: 9990,
timeout: 50 * 60 * 1000,
reuseExistingServer: !process.env.CI,
@@ -96,7 +96,7 @@ module.exports = defineConfig({
{
cwd: path.join(process.cwd(), "web-routing"),
command:
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 2020',
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 2020',
port: 2020,
timeout: 50 * 60 * 1000,
reuseExistingServer: !process.env.CI,
@@ -105,7 +105,7 @@ module.exports = defineConfig({
{
cwd: path.join(process.cwd(), "web-hash-routing"),
command:
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 2021',
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 2021',
port: 2021,
timeout: 50 * 60 * 1000,
reuseExistingServer: !process.env.CI,
@@ -114,7 +114,7 @@ module.exports = defineConfig({
{
cwd: path.join(process.cwd(), "fullstack"),
command:
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 3333',
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 3333',
port: 3333,
timeout: 50 * 60 * 1000,
reuseExistingServer: !process.env.CI,
@@ -123,7 +123,7 @@ module.exports = defineConfig({
{
cwd: path.join(process.cwd(), "fullstack-mounted"),
command:
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 7777',
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 7777',
port: 7777,
timeout: 50 * 60 * 1000,
reuseExistingServer: !process.env.CI,
@@ -132,7 +132,7 @@ module.exports = defineConfig({
{
cwd: path.join(process.cwd(), "fullstack-routing"),
command:
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 8888',
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 8888',
port: 8888,
timeout: 50 * 60 * 1000,
reuseExistingServer: !process.env.CI,
@@ -141,7 +141,7 @@ module.exports = defineConfig({
{
cwd: path.join(process.cwd(), "suspense-carousel"),
command:
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 4040',
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 4040',
port: 4040,
timeout: 50 * 60 * 1000,
reuseExistingServer: !process.env.CI,
@@ -150,7 +150,7 @@ module.exports = defineConfig({
{
cwd: path.join(process.cwd(), "nested-suspense"),
command:
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 5050',
'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --web --addr "127.0.0.1" --port 5050',
port: 5050,
timeout: 50 * 60 * 1000,
reuseExistingServer: !process.env.CI,
@@ -159,7 +159,7 @@ module.exports = defineConfig({
{
cwd: path.join(process.cwd(), "nested-suspense"),
command:
'cargo run --package dioxus-cli --release -- run --verbose --bin nested-suspense-ssg --force-sequential --platform web --ssg --addr "127.0.0.1" --port 6060',
'cargo run --package dioxus-cli --release -- run --verbose --bin nested-suspense-ssg --force-sequential --web --ssg --addr "127.0.0.1" --port 6060',
port: 6060,
timeout: 50 * 60 * 1000,
reuseExistingServer: !process.env.CI,
@@ -178,7 +178,7 @@ module.exports = defineConfig({
{
cwd: path.join(process.cwd(), "wasm-split-harness"),
command:
'cargo run --package dioxus-cli --release -- run --verbose --bin wasm-split-harness --platform web --addr "127.0.0.1" --port 8001 --wasm-split --profile wasm-split-release',
'cargo run --package dioxus-cli --release -- run --verbose --bin wasm-split-harness --web --addr "127.0.0.1" --port 8001 --wasm-split --profile wasm-split-release',
port: 8001,
timeout: 50 * 60 * 1000,
reuseExistingServer: !process.env.CI,
@@ -204,7 +204,7 @@ module.exports = defineConfig({
},
{
command:
'rm -rf ./web-hot-patch-temp && cp -r ./web-hot-patch ./web-hot-patch-temp && cd web-hot-patch-temp && cargo run --manifest-path ../../cli/Cargo.toml --release -- serve --verbose --force-sequential --platform web --addr "127.0.0.1" --port 9980 --hot-patch --exit-on-error',
'rm -rf ./web-hot-patch-temp && cp -r ./web-hot-patch ./web-hot-patch-temp && cd web-hot-patch-temp && cargo run --manifest-path ../../cli/Cargo.toml --release -- serve --verbose --force-sequential --web --addr "127.0.0.1" --port 9980 --hot-patch --exit-on-error',
port: 9980,
timeout: 50 * 60 * 1000,
reuseExistingServer: !process.env.CI,