diff --git a/Cargo.toml b/Cargo.toml
index 7ef10d612..df558e69e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,7 @@ members = [
"packages/dioxus",
"packages/core",
"packages/cli",
+ "packages/cli-config",
"packages/core-macro",
"packages/router-macro",
"packages/extension",
@@ -79,6 +80,7 @@ dioxus-native-core = { path = "packages/native-core", version = "0.4.0" }
dioxus-native-core-macro = { path = "packages/native-core-macro", version = "0.4.0" }
rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.4.0" }
dioxus-signals = { path = "packages/signals" }
+dioxus-cli-config = { path = "packages/cli-config", version = "0.4.1" }
generational-box = { path = "packages/generational-box", version = "0.4.3" }
dioxus-hot-reload = { path = "packages/hot-reload", version = "0.4.0" }
dioxus-fullstack = { path = "packages/fullstack", version = "0.4.1" }
diff --git a/packages/cli-config/.gitignore b/packages/cli-config/.gitignore
new file mode 100644
index 000000000..6700b1332
--- /dev/null
+++ b/packages/cli-config/.gitignore
@@ -0,0 +1,4 @@
+/target
+Cargo.lock
+.DS_Store
+.idea/
diff --git a/packages/cli-config/Cargo.toml b/packages/cli-config/Cargo.toml
new file mode 100644
index 000000000..d6f422b3c
--- /dev/null
+++ b/packages/cli-config/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "dioxus-cli-config"
+version = "0.4.1"
+authors = ["Jonathan Kelley"]
+edition = "2021"
+description = "Configuration for the Dioxus CLI"
+repository = "https://github.com/DioxusLabs/dioxus/"
+license = "MIT OR Apache-2.0"
+keywords = ["react", "gui", "cli", "dioxus", "wasm"]
+
+[dependencies]
+clap = { version = "4.2", features = ["derive"], optional = true }
+serde = { version = "1.0.136", features = ["derive"] }
+serde_json = "1.0.79"
+toml = { version = "0.5.8", optional = true }
+cargo_toml = { version = "0.16.0", optional = true }
+once_cell = "1.18.0"
+tracing.workspace = true
+
+# bundling
+tauri-bundler = { version = "=1.4.0", features = ["native-tls-vendored"], optional = true }
+tauri-utils = { version = "=1.5.*", optional = true }
+
+[features]
+default = []
+cli = ["tauri-bundler", "tauri-utils", "clap", "toml", "cargo_toml"]
diff --git a/packages/cli-config/README.md b/packages/cli-config/README.md
new file mode 100644
index 000000000..ef2435713
--- /dev/null
+++ b/packages/cli-config/README.md
@@ -0,0 +1,5 @@
+
+
📦✨ Dioxus CLI Configuration
+
+
+The **dioxus-cli-config** contains the configuration for the **dioxus-cli**.
diff --git a/packages/cli-config/src/bundle.rs b/packages/cli-config/src/bundle.rs
new file mode 100644
index 000000000..07c4fec21
--- /dev/null
+++ b/packages/cli-config/src/bundle.rs
@@ -0,0 +1,260 @@
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+use std::path::PathBuf;
+
+#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+pub struct BundleConfig {
+ pub identifier: Option,
+ pub publisher: Option,
+ pub icon: Option>,
+ pub resources: Option>,
+ pub copyright: Option,
+ pub category: Option,
+ pub short_description: Option,
+ pub long_description: Option,
+ pub external_bin: Option>,
+ pub deb: Option,
+ pub macos: Option,
+ pub windows: Option,
+}
+
+#[cfg(feature = "cli")]
+impl From for tauri_bundler::BundleSettings {
+ fn from(val: BundleConfig) -> Self {
+ tauri_bundler::BundleSettings {
+ identifier: val.identifier,
+ publisher: val.publisher,
+ icon: val.icon,
+ resources: val.resources,
+ copyright: val.copyright,
+ category: val.category.and_then(|c| c.parse().ok()),
+ short_description: val.short_description,
+ long_description: val.long_description,
+ external_bin: val.external_bin,
+ deb: val.deb.map(Into::into).unwrap_or_default(),
+ macos: val.macos.map(Into::into).unwrap_or_default(),
+ windows: val.windows.map(Into::into).unwrap_or_default(),
+ ..Default::default()
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+pub struct DebianSettings {
+ pub depends: Option>,
+ pub files: HashMap,
+ pub nsis: Option,
+}
+
+#[cfg(feature = "cli")]
+impl From for tauri_bundler::DebianSettings {
+ fn from(val: DebianSettings) -> Self {
+ tauri_bundler::DebianSettings {
+ depends: val.depends,
+ files: val.files,
+ desktop_template: None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+pub struct WixSettings {
+ pub language: Vec<(String, Option)>,
+ pub template: Option,
+ pub fragment_paths: Vec,
+ pub component_group_refs: Vec,
+ pub component_refs: Vec,
+ pub feature_group_refs: Vec,
+ pub feature_refs: Vec,
+ pub merge_refs: Vec,
+ pub skip_webview_install: bool,
+ pub license: Option,
+ pub enable_elevated_update_task: bool,
+ pub banner_path: Option,
+ pub dialog_image_path: Option,
+ pub fips_compliant: bool,
+}
+
+#[cfg(feature = "cli")]
+impl From for tauri_bundler::WixSettings {
+ fn from(val: WixSettings) -> Self {
+ tauri_bundler::WixSettings {
+ language: tauri_bundler::bundle::WixLanguage({
+ let mut languages: Vec<_> = val
+ .language
+ .iter()
+ .map(|l| {
+ (
+ l.0.clone(),
+ tauri_bundler::bundle::WixLanguageConfig {
+ locale_path: l.1.clone(),
+ },
+ )
+ })
+ .collect();
+ if languages.is_empty() {
+ languages.push(("en-US".into(), Default::default()));
+ }
+ languages
+ }),
+ template: val.template,
+ fragment_paths: val.fragment_paths,
+ component_group_refs: val.component_group_refs,
+ component_refs: val.component_refs,
+ feature_group_refs: val.feature_group_refs,
+ feature_refs: val.feature_refs,
+ merge_refs: val.merge_refs,
+ skip_webview_install: val.skip_webview_install,
+ license: val.license,
+ enable_elevated_update_task: val.enable_elevated_update_task,
+ banner_path: val.banner_path,
+ dialog_image_path: val.dialog_image_path,
+ fips_compliant: val.fips_compliant,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+pub struct MacOsSettings {
+ pub frameworks: Option>,
+ pub minimum_system_version: Option,
+ pub license: Option,
+ pub exception_domain: Option,
+ pub signing_identity: Option,
+ pub provider_short_name: Option,
+ pub entitlements: Option,
+ pub info_plist_path: Option,
+}
+
+#[cfg(feature = "cli")]
+impl From for tauri_bundler::MacOsSettings {
+ fn from(val: MacOsSettings) -> Self {
+ tauri_bundler::MacOsSettings {
+ frameworks: val.frameworks,
+ minimum_system_version: val.minimum_system_version,
+ license: val.license,
+ exception_domain: val.exception_domain,
+ signing_identity: val.signing_identity,
+ provider_short_name: val.provider_short_name,
+ entitlements: val.entitlements,
+ info_plist_path: val.info_plist_path,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct WindowsSettings {
+ pub digest_algorithm: Option,
+ pub certificate_thumbprint: Option,
+ pub timestamp_url: Option,
+ pub tsp: bool,
+ pub wix: Option,
+ pub icon_path: Option,
+ pub webview_install_mode: WebviewInstallMode,
+ pub webview_fixed_runtime_path: Option,
+ pub allow_downgrades: bool,
+ pub nsis: Option,
+}
+
+#[cfg(feature = "cli")]
+impl From for tauri_bundler::WindowsSettings {
+ fn from(val: WindowsSettings) -> Self {
+ tauri_bundler::WindowsSettings {
+ digest_algorithm: val.digest_algorithm,
+ certificate_thumbprint: val.certificate_thumbprint,
+ timestamp_url: val.timestamp_url,
+ tsp: val.tsp,
+ wix: val.wix.map(Into::into),
+ icon_path: val.icon_path.unwrap_or("icons/icon.ico".into()),
+ webview_install_mode: val.webview_install_mode.into(),
+ webview_fixed_runtime_path: val.webview_fixed_runtime_path,
+ allow_downgrades: val.allow_downgrades,
+ nsis: val.nsis.map(Into::into),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct NsisSettings {
+ pub template: Option,
+ pub license: Option,
+ pub header_image: Option,
+ pub sidebar_image: Option,
+ pub installer_icon: Option,
+ pub install_mode: NSISInstallerMode,
+ pub languages: Option>,
+ pub custom_language_files: Option>,
+ pub display_language_selector: bool,
+}
+
+#[cfg(feature = "cli")]
+impl From for tauri_bundler::NsisSettings {
+ fn from(val: NsisSettings) -> Self {
+ tauri_bundler::NsisSettings {
+ license: val.license,
+ header_image: val.header_image,
+ sidebar_image: val.sidebar_image,
+ installer_icon: val.installer_icon,
+ install_mode: val.install_mode.into(),
+ languages: val.languages,
+ display_language_selector: val.display_language_selector,
+ custom_language_files: None,
+ template: None,
+ compression: None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum NSISInstallerMode {
+ CurrentUser,
+ PerMachine,
+ Both,
+}
+
+#[cfg(feature = "cli")]
+impl From for tauri_utils::config::NSISInstallerMode {
+ fn from(val: NSISInstallerMode) -> Self {
+ match val {
+ NSISInstallerMode::CurrentUser => tauri_utils::config::NSISInstallerMode::CurrentUser,
+ NSISInstallerMode::PerMachine => tauri_utils::config::NSISInstallerMode::PerMachine,
+ NSISInstallerMode::Both => tauri_utils::config::NSISInstallerMode::Both,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum WebviewInstallMode {
+ Skip,
+ DownloadBootstrapper { silent: bool },
+ EmbedBootstrapper { silent: bool },
+ OfflineInstaller { silent: bool },
+ FixedRuntime { path: PathBuf },
+}
+
+#[cfg(feature = "cli")]
+impl WebviewInstallMode {
+ fn into(self) -> tauri_utils::config::WebviewInstallMode {
+ match self {
+ Self::Skip => tauri_utils::config::WebviewInstallMode::Skip,
+ Self::DownloadBootstrapper { silent } => {
+ tauri_utils::config::WebviewInstallMode::DownloadBootstrapper { silent }
+ }
+ Self::EmbedBootstrapper { silent } => {
+ tauri_utils::config::WebviewInstallMode::EmbedBootstrapper { silent }
+ }
+ Self::OfflineInstaller { silent } => {
+ tauri_utils::config::WebviewInstallMode::OfflineInstaller { silent }
+ }
+ Self::FixedRuntime { path } => {
+ tauri_utils::config::WebviewInstallMode::FixedRuntime { path }
+ }
+ }
+ }
+}
+
+impl Default for WebviewInstallMode {
+ fn default() -> Self {
+ Self::OfflineInstaller { silent: false }
+ }
+}
diff --git a/packages/cli/src/cargo.rs b/packages/cli-config/src/cargo.rs
similarity index 69%
rename from packages/cli/src/cargo.rs
rename to packages/cli-config/src/cargo.rs
index b52fa4b09..97e4040db 100644
--- a/packages/cli/src/cargo.rs
+++ b/packages/cli-config/src/cargo.rs
@@ -1,12 +1,33 @@
//! Utilities for working with cargo and rust files
-use crate::error::{Error, Result};
+use std::error::Error;
use std::{
- env, fs,
+ env,
+ fmt::{Display, Formatter},
+ fs,
path::{Path, PathBuf},
process::Command,
str,
};
+#[derive(Debug, Clone)]
+pub struct CargoError {
+ msg: String,
+}
+
+impl CargoError {
+ pub fn new(msg: String) -> Self {
+ Self { msg }
+ }
+}
+
+impl Display for CargoError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "CargoError: {}", self.msg)
+ }
+}
+
+impl Error for CargoError {}
+
/// How many parent folders are searched for a `Cargo.toml`
const MAX_ANCESTORS: u32 = 10;
@@ -19,7 +40,7 @@ pub struct Metadata {
/// Returns the root of the crate that the command is run from
///
/// If the command is run from the workspace root, this will return the top-level Cargo.toml
-pub fn crate_root() -> Result {
+pub fn crate_root() -> Result {
// From the current directory we work our way up, looking for `Cargo.toml`
env::current_dir()
.ok()
@@ -35,7 +56,7 @@ pub fn crate_root() -> Result {
None
})
.ok_or_else(|| {
- Error::CargoError("Failed to find directory containing Cargo.toml".to_string())
+ CargoError::new("Failed to find directory containing Cargo.toml".to_string())
})
}
@@ -53,11 +74,11 @@ fn contains_manifest(path: &Path) -> bool {
impl Metadata {
/// Returns the struct filled from `cargo metadata` output
/// TODO @Jon, find a different way that doesn't rely on the cargo metadata command (it's slow)
- pub fn get() -> Result {
+ pub fn get() -> Result {
let output = Command::new("cargo")
.args(["metadata"])
.output()
- .map_err(|_| Error::CargoError("Manifset".to_string()))?;
+ .map_err(|_| CargoError::new("Manifset".to_string()))?;
if !output.status.success() {
let mut msg = str::from_utf8(&output.stderr).unwrap().trim();
@@ -65,22 +86,22 @@ impl Metadata {
msg = &msg[7..];
}
- return Err(Error::CargoError(msg.to_string()));
+ return Err(CargoError::new(msg.to_string()));
}
let stdout = str::from_utf8(&output.stdout).unwrap();
if let Some(line) = stdout.lines().next() {
let meta: serde_json::Value = serde_json::from_str(line)
- .map_err(|_| Error::CargoError("InvalidOutput".to_string()))?;
+ .map_err(|_| CargoError::new("InvalidOutput".to_string()))?;
let workspace_root = meta["workspace_root"]
.as_str()
- .ok_or_else(|| Error::CargoError("InvalidOutput".to_string()))?
+ .ok_or_else(|| CargoError::new("InvalidOutput".to_string()))?
.into();
let target_directory = meta["target_directory"]
.as_str()
- .ok_or_else(|| Error::CargoError("InvalidOutput".to_string()))?
+ .ok_or_else(|| CargoError::new("InvalidOutput".to_string()))?
.into();
return Ok(Self {
@@ -89,6 +110,6 @@ impl Metadata {
});
}
- Err(Error::CargoError("InvalidOutput".to_string()))
+ Err(CargoError::new("InvalidOutput".to_string()))
}
}
diff --git a/packages/cli-config/src/config.rs b/packages/cli-config/src/config.rs
new file mode 100644
index 000000000..716b2bac4
--- /dev/null
+++ b/packages/cli-config/src/config.rs
@@ -0,0 +1,492 @@
+use crate::BundleConfig;
+use crate::CargoError;
+use core::fmt::{Display, Formatter};
+use serde::{Deserialize, Serialize};
+use std::path::PathBuf;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug)]
+#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
+pub enum Platform {
+ #[cfg_attr(feature = "cli", clap(name = "web"))]
+ #[serde(rename = "web")]
+ Web,
+ #[cfg_attr(feature = "cli", clap(name = "desktop"))]
+ #[serde(rename = "desktop")]
+ Desktop,
+ #[cfg_attr(feature = "cli", clap(name = "fullstack"))]
+ #[serde(rename = "fullstack")]
+ Fullstack,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct DioxusConfig {
+ pub application: ApplicationConfig,
+
+ pub web: WebConfig,
+
+ #[serde(default)]
+ pub bundle: BundleConfig,
+
+ #[cfg(feature = "cli")]
+ #[serde(default = "default_plugin")]
+ pub plugin: toml::Value,
+}
+
+#[cfg(feature = "cli")]
+fn default_plugin() -> toml::Value {
+ toml::Value::Boolean(true)
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct LoadDioxusConfigError {
+ location: String,
+ error: String,
+}
+
+impl std::fmt::Display for LoadDioxusConfigError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{} {}", self.location, self.error)
+ }
+}
+
+impl std::error::Error for LoadDioxusConfigError {}
+
+#[derive(Debug)]
+pub enum CrateConfigError {
+ Cargo(CargoError),
+ Io(std::io::Error),
+ #[cfg(feature = "cli")]
+ Toml(toml::de::Error),
+ LoadDioxusConfig(LoadDioxusConfigError),
+}
+
+impl From for CrateConfigError {
+ fn from(err: CargoError) -> Self {
+ Self::Cargo(err)
+ }
+}
+
+impl From for CrateConfigError {
+ fn from(err: std::io::Error) -> Self {
+ Self::Io(err)
+ }
+}
+
+#[cfg(feature = "cli")]
+impl From for CrateConfigError {
+ fn from(err: toml::de::Error) -> Self {
+ Self::Toml(err)
+ }
+}
+
+impl From for CrateConfigError {
+ fn from(err: LoadDioxusConfigError) -> Self {
+ Self::LoadDioxusConfig(err)
+ }
+}
+
+impl Display for CrateConfigError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Cargo(err) => write!(f, "{}", err),
+ Self::Io(err) => write!(f, "{}", err),
+ #[cfg(feature = "cli")]
+ Self::Toml(err) => write!(f, "{}", err),
+ Self::LoadDioxusConfig(err) => write!(f, "{}", err),
+ }
+ }
+}
+
+impl std::error::Error for CrateConfigError {}
+
+impl DioxusConfig {
+ #[cfg(feature = "cli")]
+ /// Load the dioxus config from a path
+ pub fn load(bin: Option) -> Result