From 8a2d170d9622cb466f034f46ba3123e92fe7464c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 8 Nov 2023 12:48:25 -0600 Subject: [PATCH] pull out the CLI config data into a separate library --- Cargo.toml | 2 + packages/cli-config/.gitignore | 4 + packages/cli-config/Cargo.toml | 102 ++++ packages/cli-config/README.md | 5 + packages/cli-config/src/assets/dioxus.toml | 74 +++ packages/{cli => cli-config}/src/cargo.rs | 43 +- packages/cli-config/src/config.rs | 658 +++++++++++++++++++++ packages/cli-config/src/lib.rs | 9 + packages/cli/Cargo.toml | 1 + packages/cli/src/builder.rs | 3 +- packages/cli/src/cli/build.rs | 2 +- packages/cli/src/cli/cfg.rs | 13 +- packages/cli/src/cli/config.rs | 4 +- packages/cli/src/cli/serve.rs | 6 +- packages/cli/src/config.rs | 9 +- packages/cli/src/error.rs | 14 + packages/cli/src/lib.rs | 3 - packages/cli/src/server/output.rs | 3 +- 18 files changed, 919 insertions(+), 36 deletions(-) create mode 100644 packages/cli-config/.gitignore create mode 100644 packages/cli-config/Cargo.toml create mode 100644 packages/cli-config/README.md create mode 100644 packages/cli-config/src/assets/dioxus.toml rename packages/{cli => cli-config}/src/cargo.rs (69%) create mode 100644 packages/cli-config/src/config.rs create mode 100644 packages/cli-config/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index d687d74d0..c615e2e42 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", @@ -78,6 +79,7 @@ dioxus-native-core-macro = { path = "packages/native-core-macro", version = "0.4 rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.4.0" } dioxus-signals = { path = "packages/signals" } generational-box = { path = "packages/generational-box" } +dioxus-cli-config = { path = "packages/cli-config", version = "0.4.1" } dioxus-hot-reload = { path = "packages/hot-reload", version = "0.4.0" } dioxus-fullstack = { path = "packages/fullstack", version = "0.4.1" } dioxus_server_macro = { path = "packages/server-macro", 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..d89d411fd --- /dev/null +++ b/packages/cli-config/Cargo.toml @@ -0,0 +1,102 @@ +[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] +# cli core +clap = { version = "4.2", features = ["derive"] } +thiserror = { workspace = true } +wasm-bindgen-cli-support = "0.2" +colored = "2.0.0" + +# features +log = "0.4.14" +fern = { version = "0.6.0", features = ["colored"] } +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0.79" +toml = "0.5.8" +fs_extra = "1.2.0" +cargo_toml = "0.16.0" +futures = "0.3.21" +notify = { version = "5.0.0-pre.16", features = ["serde"] } +html_parser = { workspace = true } +cargo_metadata = "0.15.0" +tokio = { version = "1.16.1", features = ["fs", "sync", "rt", "macros"] } +atty = "0.2.14" +chrono = "0.4.19" +anyhow = "1.0.53" +hyper = "0.14.17" +hyper-rustls = "0.23.2" +indicatif = "0.17.5" +subprocess = "0.2.9" + +axum = { version = "0.5.1", features = ["ws", "headers"] } +axum-server = { version = "0.5.1", features = ["tls-rustls"] } +tower-http = { version = "0.2.2", features = ["full"] } +headers = "0.3.7" + +walkdir = "2" + +# tools download +dirs = "4.0.0" +reqwest = { version = "0.11", features = [ + "rustls-tls", + "stream", + "trust-dns", + "blocking", +] } +flate2 = "1.0.22" +tar = "0.4.38" +zip = "0.6.2" +tower = "0.4.12" +syn = { version = "2.0", features = ["full", "extra-traits"] } +lazy_static = "1.4.0" + +# plugin packages +mlua = { version = "0.8.1", features = [ + "lua54", + "vendored", + "async", + "send", + "macros", +], optional = true } +ctrlc = "3.2.3" +open = "4.1.0" +cargo-generate = "0.18" +toml_edit = "0.19.11" + +# bundling +tauri-bundler = { version = "=1.3.0", features = ["native-tls-vendored"] } +tauri-utils = "=1.4.*" + +dioxus-autofmt = { workspace = true } +dioxus-check = { workspace = true } +rsx-rosetta = { workspace = true } +dioxus-rsx = { workspace = true } +dioxus-html = { workspace = true, features = ["hot-reload-context"] } +dioxus-core = { workspace = true, features = ["serialize"] } +dioxus-hot-reload = { workspace = true } +interprocess-docfix = { version = "1.2.2" } + +[features] +default = [] +plugin = ["mlua"] + +[[bin]] +path = "src/main.rs" +name = "dx" + +[dev-dependencies] +tempfile = "3.3" + +[package.metadata.binstall] +pkg-url = "{ repo }/releases/download/v{ version }/dx-{ target }{ archive-suffix }" + +[package.metadata.binstall.overrides.x86_64-pc-windows-msvc] +pkg-fmt = "zip" 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/assets/dioxus.toml b/packages/cli-config/src/assets/dioxus.toml new file mode 100644 index 000000000..892a6cdf8 --- /dev/null +++ b/packages/cli-config/src/assets/dioxus.toml @@ -0,0 +1,74 @@ +[application] + +# dioxus project name +name = "{{project-name}}" + +# default platfrom +# you can also use `dx serve/build --platform XXX` to use other platform +# value: web | desktop +default_platform = "{{default-platform}}" + +# Web `build` & `serve` dist path +out_dir = "dist" + +# resource (static) file folder +asset_dir = "public" + +[web.app] + +# HTML title tag content +title = "Dioxus | An elegant GUI library for Rust" + +[web.watcher] + +index_on_404 = true + +watch_path = ["src", "examples"] + +# include `assets` in web platform +[web.resource] + +# CSS style file +style = [] + +# Javascript code file +script = [] + +[web.resource.dev] + +# Javascript code file +# serve: [dev-server] only +script = [] + +[application.plugins] + +available = true + +required = [] + +[bundler] +# Bundle identifier +identifier = "io.github.{{project-name}}" + +# Bundle publisher +publisher = "{{project-name}}" + +# Bundle icon +icon = ["icons/icon.png"] + +# Bundle resources +resources = ["public/*"] + +# Bundle copyright +copyright = "" + +# Bundle category +category = "Utility" + +# Bundle short description +short_description = "An amazing dioxus application." + +# Bundle long description +long_description = """ +An amazing dioxus application. +""" \ No newline at end of file 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..f7ab89124 --- /dev/null +++ b/packages/cli-config/src/config.rs @@ -0,0 +1,658 @@ +use clap::ValueEnum; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + fmt::{Display, Formatter}, + path::{Path, PathBuf}, +}; + +use crate::CargoError; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize, Debug)] +pub enum Platform { + #[clap(name = "web")] + #[serde(rename = "web")] + Web, + #[clap(name = "desktop")] + #[serde(rename = "desktop")] + Desktop, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DioxusConfig { + pub application: ApplicationConfig, + + pub web: WebConfig, + + #[serde(default)] + pub bundle: BundleConfig, + + #[serde(default = "default_plugin")] + pub plugin: toml::Value, +} + +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 {} + +impl DioxusConfig { + /// Load the dioxus config from a path + pub fn load(bin: Option) -> Result, CrateConfigError> { + let crate_dir = crate::cargo::crate_root(); + + let crate_dir = match crate_dir { + Ok(dir) => { + if let Some(bin) = bin { + dir.join(bin) + } else { + dir + } + } + Err(_) => return Ok(None), + }; + let crate_dir = crate_dir.as_path(); + + let Some(dioxus_conf_file) = acquire_dioxus_toml(crate_dir) else { + return Ok(None); + }; + + let dioxus_conf_file = dioxus_conf_file.as_path(); + let cfg = toml::from_str::(&std::fs::read_to_string(dioxus_conf_file)?) + .map_err(|err| { + let error_location = dioxus_conf_file + .strip_prefix(crate_dir) + .unwrap_or(dioxus_conf_file) + .display(); + CrateConfigError::LoadDioxusConfig(LoadDioxusConfigError { + location: error_location.to_string(), + error: err.to_string(), + }) + }) + .map(Some); + match cfg { + Ok(Some(mut cfg)) => { + let name = cfg.application.name.clone(); + if cfg.bundle.identifier.is_none() { + cfg.bundle.identifier = Some(format!("io.github.{name}")); + } + if cfg.bundle.publisher.is_none() { + cfg.bundle.publisher = Some(name); + } + Ok(Some(cfg)) + } + cfg => cfg, + } + } +} + +fn acquire_dioxus_toml(dir: &Path) -> Option { + // prefer uppercase + let uppercase_conf = dir.join("Dioxus.toml"); + if uppercase_conf.is_file() { + return Some(uppercase_conf); + } + + // lowercase is fine too + let lowercase_conf = dir.join("dioxus.toml"); + if lowercase_conf.is_file() { + return Some(lowercase_conf); + } + + None +} + +impl Default for DioxusConfig { + fn default() -> Self { + let name = "name"; + Self { + application: ApplicationConfig { + name: name.into(), + default_platform: Platform::Web, + out_dir: Some(PathBuf::from("dist")), + asset_dir: Some(PathBuf::from("public")), + + tools: None, + + sub_package: None, + }, + web: WebConfig { + app: WebAppConfig { + title: Some("dioxus | ⛺".into()), + base_path: None, + }, + proxy: Some(vec![]), + watcher: WebWatcherConfig { + watch_path: Some(vec![PathBuf::from("src"), PathBuf::from("examples")]), + reload_html: Some(false), + index_on_404: Some(true), + }, + resource: WebResourceConfig { + dev: WebDevResourceConfig { + style: Some(vec![]), + script: Some(vec![]), + }, + style: Some(vec![]), + script: Some(vec![]), + }, + https: WebHttpsConfig { + enabled: None, + mkcert: None, + key_path: None, + cert_path: None, + }, + }, + bundle: BundleConfig { + identifier: Some(format!("io.github.{name}")), + publisher: Some(name.into()), + ..Default::default() + }, + plugin: toml::Value::Table(toml::map::Map::new()), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ApplicationConfig { + pub name: String, + pub default_platform: Platform, + pub out_dir: Option, + pub asset_dir: Option, + + pub tools: Option>, + + pub sub_package: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebConfig { + pub app: WebAppConfig, + pub proxy: Option>, + pub watcher: WebWatcherConfig, + pub resource: WebResourceConfig, + #[serde(default)] + pub https: WebHttpsConfig, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebAppConfig { + pub title: Option, + pub base_path: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebProxyConfig { + pub backend: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebWatcherConfig { + pub watch_path: Option>, + pub reload_html: Option, + pub index_on_404: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebResourceConfig { + pub dev: WebDevResourceConfig, + pub style: Option>, + pub script: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebDevResourceConfig { + pub style: Option>, + pub script: Option>, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct WebHttpsConfig { + pub enabled: Option, + pub mkcert: Option, + pub key_path: Option, + pub cert_path: Option, +} + +#[derive(Debug, Clone)] +pub struct CrateConfig { + pub out_dir: PathBuf, + pub crate_dir: PathBuf, + pub workspace_dir: PathBuf, + pub target_dir: PathBuf, + pub asset_dir: PathBuf, + pub manifest: cargo_toml::Manifest, + pub executable: ExecutableType, + pub dioxus_config: DioxusConfig, + pub release: bool, + pub hot_reload: bool, + pub cross_origin_policy: bool, + pub verbose: bool, + pub custom_profile: Option, + pub features: Option>, +} + +#[derive(Debug)] +pub enum CrateConfigError { + Cargo(CargoError), + Io(std::io::Error), + 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) + } +} + +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), + Self::Toml(err) => write!(f, "{}", err), + Self::LoadDioxusConfig(err) => write!(f, "{}", err), + } + } +} + +impl std::error::Error for CrateConfigError {} + +#[derive(Debug, Clone)] +pub enum ExecutableType { + Binary(String), + Lib(String), + Example(String), +} + +impl CrateConfig { + pub fn new(bin: Option) -> Result { + let dioxus_config = DioxusConfig::load(bin.clone())?.unwrap_or_default(); + + let crate_root = crate::cargo::crate_root()?; + + let crate_dir = if let Some(package) = &dioxus_config.application.sub_package { + crate_root.join(package) + } else if let Some(bin) = bin { + crate_root.join(bin) + } else { + crate_root + }; + + let meta = crate::cargo::Metadata::get()?; + let workspace_dir = meta.workspace_root; + let target_dir = meta.target_directory; + + let out_dir = match dioxus_config.application.out_dir { + Some(ref v) => crate_dir.join(v), + None => crate_dir.join("dist"), + }; + + let cargo_def = &crate_dir.join("Cargo.toml"); + + let asset_dir = match dioxus_config.application.asset_dir { + Some(ref v) => crate_dir.join(v), + None => crate_dir.join("public"), + }; + + let manifest = cargo_toml::Manifest::from_path(cargo_def).unwrap(); + + let mut output_filename = String::from("dioxus_app"); + if let Some(package) = &manifest.package.as_ref() { + output_filename = match &package.default_run { + Some(default_run_target) => default_run_target.to_owned(), + None => manifest + .bin + .iter() + .find(|b| b.name == manifest.package.as_ref().map(|pkg| pkg.name.clone())) + .or(manifest + .bin + .iter() + .find(|b| b.path == Some("src/main.rs".to_owned()))) + .or(manifest.bin.first()) + .or(manifest.lib.as_ref()) + .and_then(|prod| prod.name.clone()) + .unwrap_or(String::from("dioxus_app")), + }; + } + + let executable = ExecutableType::Binary(output_filename); + + let release = false; + let hot_reload = false; + let verbose = false; + let custom_profile = None; + let features = None; + + Ok(Self { + out_dir, + crate_dir, + workspace_dir, + target_dir, + asset_dir, + manifest, + executable, + release, + dioxus_config, + hot_reload, + cross_origin_policy: false, + custom_profile, + features, + verbose, + }) + } + + pub fn as_example(&mut self, example_name: String) -> &mut Self { + self.executable = ExecutableType::Example(example_name); + self + } + + pub fn with_release(&mut self, release: bool) -> &mut Self { + self.release = release; + self + } + + pub fn with_hot_reload(&mut self, hot_reload: bool) -> &mut Self { + self.hot_reload = hot_reload; + self + } + + pub fn with_cross_origin_policy(&mut self, cross_origin_policy: bool) -> &mut Self { + self.cross_origin_policy = cross_origin_policy; + self + } + + pub fn with_verbose(&mut self, verbose: bool) -> &mut Self { + self.verbose = verbose; + self + } + + pub fn set_profile(&mut self, profile: String) -> &mut Self { + self.custom_profile = Some(profile); + self + } + + pub fn set_features(&mut self, features: Vec) -> &mut Self { + self.features = Some(features); + self + } +} + +#[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, +} + +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, +} + +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, +} + +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, +} + +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, +} + +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, +} + +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, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum NSISInstallerMode { + CurrentUser, + PerMachine, + Both, +} + +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 }, +} + +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-config/src/lib.rs b/packages/cli-config/src/lib.rs new file mode 100644 index 000000000..78531cf62 --- /dev/null +++ b/packages/cli-config/src/lib.rs @@ -0,0 +1,9 @@ +#![doc = include_str!("../README.md")] +#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")] +#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] + +mod config; +pub use config::*; + +mod cargo; +pub use cargo::*; diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index adb0239f2..4eb9db09f 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -14,6 +14,7 @@ clap = { version = "4.2", features = ["derive"] } thiserror = { workspace = true } wasm-bindgen-cli-support = "0.2" colored = "2.0.0" +dioxus-cli-config = { workspace = true } # features log = "0.4.14" diff --git a/packages/cli/src/builder.rs b/packages/cli/src/builder.rs index c85cdc484..c38514513 100644 --- a/packages/cli/src/builder.rs +++ b/packages/cli/src/builder.rs @@ -5,6 +5,7 @@ use crate::{ DioxusConfig, }; use cargo_metadata::{diagnostic::Diagnostic, Message}; +use dioxus_cli_config::crate_root; use indicatif::{ProgressBar, ProgressStyle}; use serde::Serialize; use std::{ @@ -429,7 +430,7 @@ fn prettier_build(cmd: subprocess::Exec) -> anyhow::Result> { } pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { - let crate_root = crate::cargo::crate_root().unwrap(); + let crate_root = crate_root().unwrap(); let custom_html_file = crate_root.join("index.html"); let mut html = if custom_html_file.is_file() { let mut buf = String::new(); diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index 7fe29f8ce..7ecc286ba 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -1,6 +1,6 @@ -use crate::cfg::Platform; #[cfg(feature = "plugin")] use crate::plugin::PluginManager; +use dioxus_cli_config::Platform; use super::*; diff --git a/packages/cli/src/cli/cfg.rs b/packages/cli/src/cli/cfg.rs index 2a441a1c8..53c64b6e2 100644 --- a/packages/cli/src/cli/cfg.rs +++ b/packages/cli/src/cli/cfg.rs @@ -1,5 +1,4 @@ -use clap::ValueEnum; -use serde::Serialize; +use dioxus_cli_config::Platform; use super::*; @@ -91,16 +90,6 @@ pub struct ConfigOptsServe { pub features: Option>, } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize, Debug)] -pub enum Platform { - #[clap(name = "web")] - #[serde(rename = "web")] - Web, - #[clap(name = "desktop")] - #[serde(rename = "desktop")] - Desktop, -} - /// Config options for the bundling system. #[derive(Clone, Debug, Default, Deserialize, Parser)] pub struct ConfigOptsBundle { diff --git a/packages/cli/src/cli/config.rs b/packages/cli/src/cli/config.rs index 45fcc2f23..ab29f95b2 100644 --- a/packages/cli/src/cli/config.rs +++ b/packages/cli/src/cli/config.rs @@ -1,3 +1,5 @@ +use dioxus_cli_config::crate_root; + use super::*; /// Dioxus config file controls @@ -26,7 +28,7 @@ pub enum Config { impl Config { pub fn config(self) -> Result<()> { - let crate_root = crate::cargo::crate_root()?; + let crate_root = crate_root()?; match self { Config::Init { name, diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index 433d611b7..af22a17ca 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -1,3 +1,5 @@ +use dioxus_cli_config::Platform; + use super::*; use std::{fs::create_dir_all, io::Write, path::PathBuf}; @@ -40,7 +42,7 @@ impl Serve { .unwrap_or(crate_config.dioxus_config.application.default_platform); match platform { - cfg::Platform::Web => { + Platform::Web => { // generate dev-index page Serve::regen_dev_page(&crate_config)?; @@ -48,7 +50,7 @@ impl Serve { server::web::startup(self.serve.port, crate_config.clone(), self.serve.open) .await?; } - cfg::Platform::Desktop => { + Platform::Desktop => { server::desktop::startup(crate_config.clone()).await?; } } diff --git a/packages/cli/src/config.rs b/packages/cli/src/config.rs index 3abf47f0c..842d3bad6 100644 --- a/packages/cli/src/config.rs +++ b/packages/cli/src/config.rs @@ -1,4 +1,5 @@ -use crate::{cfg::Platform, error::Result}; +use crate::error::Result; +use dioxus_cli_config::{crate_root, Metadata, Platform}; use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, @@ -24,7 +25,7 @@ fn default_plugin() -> toml::Value { impl DioxusConfig { pub fn load(bin: Option) -> crate::error::Result> { - let crate_dir = crate::cargo::crate_root(); + let crate_dir = crate_root(); let crate_dir = match crate_dir { Ok(dir) => { @@ -224,7 +225,7 @@ impl CrateConfig { pub fn new(bin: Option) -> Result { let dioxus_config = DioxusConfig::load(bin.clone())?.unwrap_or_default(); - let crate_root = crate::cargo::crate_root()?; + let crate_root = crate_root()?; let crate_dir = if let Some(package) = &dioxus_config.application.sub_package { crate_root.join(package) @@ -234,7 +235,7 @@ impl CrateConfig { crate_root }; - let meta = crate::cargo::Metadata::get()?; + let meta = Metadata::get()?; let workspace_dir = meta.workspace_root; let target_dir = meta.target_directory; diff --git a/packages/cli/src/error.rs b/packages/cli/src/error.rs index 84b9d4b71..e8d6c91e4 100644 --- a/packages/cli/src/error.rs +++ b/packages/cli/src/error.rs @@ -69,6 +69,20 @@ impl From for Error { } } +impl From for Error { + fn from(e: dioxus_cli_config::LoadDioxusConfigError) -> Self { + Self::RuntimeError(e.to_string()) + } +} + +impl From for Error { + fn from(e: dioxus_cli_config::CargoError) -> Self { + Self::CargoError(e.to_string()) + } +} + + + #[macro_export] macro_rules! custom_error { ($msg:literal $(,)?) => { diff --git a/packages/cli/src/lib.rs b/packages/cli/src/lib.rs index b27cf55a9..9a87a2dfa 100644 --- a/packages/cli/src/lib.rs +++ b/packages/cli/src/lib.rs @@ -10,9 +10,6 @@ pub mod tools; pub use builder::*; -pub mod cargo; -pub use cargo::*; - pub mod cli; pub use cli::*; diff --git a/packages/cli/src/server/output.rs b/packages/cli/src/server/output.rs index 71323dd29..a221e2f9b 100644 --- a/packages/cli/src/server/output.rs +++ b/packages/cli/src/server/output.rs @@ -1,6 +1,7 @@ use crate::server::Diagnostic; use crate::CrateConfig; use colored::Colorize; +use dioxus_cli_config::crate_root; use std::path::PathBuf; use std::process::Command; @@ -40,7 +41,7 @@ pub fn print_console_info( profile = config.custom_profile.as_ref().unwrap().to_string(); } let hot_reload = if config.hot_reload { "RSX" } else { "Normal" }; - let crate_root = crate::cargo::crate_root().unwrap(); + let crate_root = crate_root().unwrap(); let custom_html_file = if crate_root.join("index.html").is_file() { "Custom [index.html]" } else {