diff --git a/.changes/cli-plugins-migrate.md b/.changes/cli-plugins-migrate.md new file mode 100644 index 000000000..be41341fa --- /dev/null +++ b/.changes/cli-plugins-migrate.md @@ -0,0 +1,6 @@ +--- +'tauri-cli': 'patch:enhance' +'@tauri-apps/cli': 'patch:enhance' +--- + +Add plugins to `Cargo.toml` when using `tauri migrate` diff --git a/tooling/cli/src/add.rs b/tooling/cli/src/add.rs index 83c96afa4..5092a85e9 100644 --- a/tooling/cli/src/add.rs +++ b/tooling/cli/src/add.rs @@ -22,16 +22,16 @@ use std::{collections::HashMap, process::Command}; #[clap(about = "Add a tauri plugin to the project")] pub struct Options { /// The plugin to add. - plugin: String, + pub plugin: String, /// Git tag to use. #[clap(short, long)] - tag: Option, + pub tag: Option, /// Git rev to use. #[clap(short, long)] - rev: Option, + pub rev: Option, /// Git branch to use. #[clap(short, long)] - branch: Option, + pub branch: Option, } pub fn command(options: Options) -> Result<()> { diff --git a/tooling/cli/src/migrate/config.rs b/tooling/cli/src/migrate/config.rs index 2f2960917..256eaa9b0 100644 --- a/tooling/cli/src/migrate/config.rs +++ b/tooling/cli/src/migrate/config.rs @@ -14,24 +14,12 @@ use tauri_utils::{ }; use std::{ + collections::HashSet, fs::{create_dir_all, write}, path::Path, }; -macro_rules! move_allowlist_object { - ($plugins: ident, $value: expr, $plugin: literal, $field: literal) => {{ - if $value != Default::default() { - $plugins - .entry($plugin) - .or_insert_with(|| Value::Object(Default::default())) - .as_object_mut() - .unwrap() - .insert($field.into(), serde_json::to_value($value.clone())?); - } - }}; -} - -pub fn migrate(tauri_dir: &Path) -> Result<()> { +pub fn migrate(tauri_dir: &Path) -> Result { if let Ok((mut config, config_path)) = tauri_utils_v1::config::parse::parse_value(tauri_dir.join("tauri.conf.json")) { @@ -50,7 +38,7 @@ pub fn migrate(tauri_dir: &Path) -> Result<()> { .into_iter() .map(|p| PermissionEntry::PermissionRef(p.to_string().try_into().unwrap())) .collect(); - permissions.extend(migrated.permissions); + permissions.extend(migrated.permissions.clone()); let capabilities_path = config_path.parent().unwrap().join("capabilities"); create_dir_all(&capabilities_path)?; @@ -73,18 +61,23 @@ pub fn migrate(tauri_dir: &Path) -> Result<()> { ], })?, )?; + + return Ok(migrated); } - Ok(()) + Ok(Default::default()) } -struct MigratedConfig { - permissions: Vec, +#[derive(Default)] +pub struct MigratedConfig { + pub permissions: Vec, + pub plugins: HashSet, } fn migrate_config(config: &mut Value) -> Result { let mut migrated = MigratedConfig { permissions: Vec::new(), + plugins: HashSet::new(), }; if let Some(config) = config.as_object_mut() { @@ -101,8 +94,9 @@ fn migrate_config(config: &mut Value) -> Result { if let Some(tauri_config) = config.get_mut("tauri").and_then(|c| c.as_object_mut()) { // allowlist if let Some(allowlist) = tauri_config.remove("allowlist") { - let allowlist = process_allowlist(tauri_config, &mut plugins, allowlist)?; + let allowlist = process_allowlist(tauri_config, allowlist)?; let permissions = allowlist_to_permissions(allowlist); + migrated.plugins = plugins_from_permissions(&permissions); migrated.permissions = permissions; } @@ -178,8 +172,11 @@ fn process_build(config: &mut Map) { if let Some(dist_dir) = build_config.remove("distDir") { build_config.insert("frontendDist".into(), dist_dir); } - if let Some(dist_dir) = build_config.remove("devPath") { - build_config.insert("devUrl".into(), dist_dir); + if let Some(dev_path) = build_config.remove("devPath") { + let is_url = url::Url::parse(dev_path.as_str().unwrap_or_default()).is_ok(); + if is_url { + build_config.insert("devUrl".into(), dev_path); + } } if let Some(with_global_tauri) = build_config.remove("withGlobalTauri") { config @@ -282,13 +279,10 @@ fn process_security(security: &mut Map) -> Result<()> { fn process_allowlist( tauri_config: &mut Map, - plugins: &mut Map, allowlist: Value, ) -> Result { let allowlist: tauri_utils_v1::config::AllowlistConfig = serde_json::from_value(allowlist)?; - move_allowlist_object!(plugins, allowlist.shell.open, "shell", "open"); - if allowlist.protocol.asset_scope != Default::default() { let security = tauri_config .entry("security") @@ -524,6 +518,34 @@ fn process_updater( Ok(()) } +const KNOWN_PLUGINS: &[&str] = &[ + "fs", + "shell", + "dialog", + "http", + "notification", + "global-shortcut", + "os", + "process", + "clipboard-manager", +]; + +fn plugins_from_permissions(permissions: &Vec) -> HashSet { + let mut plugins = HashSet::new(); + + for permission in permissions { + let permission = permission.identifier().get(); + for plugin in KNOWN_PLUGINS { + if permission.starts_with(plugin) { + plugins.insert(plugin.to_string()); + break; + } + } + } + + plugins +} + #[cfg(test)] mod test { fn migrate(original: &serde_json::Value) -> serde_json::Value { @@ -846,4 +868,22 @@ mod test { "connect-src migration failed" ); } + + #[test] + fn migrate_invalid_url_dev_path() { + let original = serde_json::json!({ + "build": { + "devPath": "../src", + "distDir": "../src" + } + }); + + let migrated = migrate(&original); + + assert!(migrated["build"].get("devUrl").is_none()); + assert_eq!( + migrated["build"]["distDir"], + original["build"]["frontendDist"] + ); + } } diff --git a/tooling/cli/src/migrate/manifest.rs b/tooling/cli/src/migrate/manifest.rs index 8d7bc3526..f2071f4dd 100644 --- a/tooling/cli/src/migrate/manifest.rs +++ b/tooling/cli/src/migrate/manifest.rs @@ -11,7 +11,7 @@ use toml_edit::{Document, Entry, Item, Table, TableLike, Value}; use std::{fs::File, io::Write, path::Path}; -const CRATE_TYPES: &[&str] = &["staticlib", "cdylib", "rlib"]; +const CRATE_TYPES: [&str; 3] = ["lib", "staticlib", "cdylib"]; pub fn migrate(tauri_dir: &Path) -> Result<()> { let manifest_path = tauri_dir.join("Cargo.toml"); @@ -57,34 +57,34 @@ fn migrate_manifest(manifest: &mut Document) -> Result<()> { migrate_dependency(build_dependencies, "tauri-build", &version, &[]); - let lib = manifest + if let Some(lib) = manifest .as_table_mut() - .entry("lib") - .or_insert(Item::Table(Table::new())) - .as_table_mut() - .expect("manifest lib isn't a table"); - match lib.entry("crate-type") { - Entry::Occupied(mut e) => { - if let Item::Value(Value::Array(types)) = e.get_mut() { - let mut crate_types_to_add = CRATE_TYPES.to_vec(); - for t in types.iter() { - // type is already in the manifest, skip adding it - if let Some(i) = crate_types_to_add - .iter() - .position(|ty| Some(ty) == t.as_str().as_ref()) - { - crate_types_to_add.remove(i); + .get_mut("lib") + .and_then(|l| l.as_table_mut()) + { + match lib.entry("crate-type") { + Entry::Occupied(mut e) => { + if let Item::Value(Value::Array(types)) = e.get_mut() { + let mut crate_types_to_add = CRATE_TYPES.to_vec(); + for t in types.iter() { + // type is already in the manifest, skip adding it + if let Some(i) = crate_types_to_add + .iter() + .position(|ty| Some(ty) == t.as_str().as_ref()) + { + crate_types_to_add.remove(i); + } + } + for t in crate_types_to_add { + types.push(t); } } - for t in crate_types_to_add { - types.push(t); - } } - } - Entry::Vacant(e) => { - let mut arr = toml_edit::Array::new(); - arr.extend(CRATE_TYPES.to_vec()); - e.insert(Item::Value(arr.into())); + Entry::Vacant(e) => { + let mut arr = toml_edit::Array::new(); + arr.extend(CRATE_TYPES.to_vec()); + e.insert(Item::Value(arr.into())); + } } } @@ -265,35 +265,6 @@ mod tests { } } - fn migrate_lib(toml: &str) { - let mut manifest = toml.parse::().expect("invalid toml"); - super::migrate_manifest(&mut manifest).expect("failed to migrate manifest"); - - let lib = manifest - .as_table() - .get("lib") - .expect("missing manifest lib") - .as_table() - .expect("manifest lib isn't a table"); - - let crate_types = lib - .get("crate-type") - .expect("missing lib crate-type") - .as_array() - .expect("crate-type must be an array"); - let mut not_added_crate_types = super::CRATE_TYPES.to_vec(); - for t in crate_types { - let t = t.as_str().expect("crate-type must be a string"); - if let Some(i) = not_added_crate_types.iter().position(|ty| ty == &t) { - not_added_crate_types.remove(i); - } - } - assert!( - not_added_crate_types.is_empty(), - "missing crate-type: {not_added_crate_types:?}" - ); - } - #[test] fn migrate_table() { migrate_deps(|features| { @@ -332,22 +303,32 @@ mod tests { }) } - #[test] - fn migrate_missing_lib() { - migrate_lib("[dependencies]"); - } - - #[test] - fn migrate_missing_crate_types() { - migrate_lib("[lib]"); - } - #[test] fn migrate_add_crate_types() { - migrate_lib( - r#" + let toml = r#" [lib] - crate-type = ["something"]"#, - ); + crate-type = ["something"]"#; + + let mut manifest = toml.parse::().expect("invalid toml"); + super::migrate_manifest(&mut manifest).expect("failed to migrate manifest"); + + if let Some(crate_types) = manifest + .as_table() + .get("lib") + .and_then(|l| l.get("crate-type")) + .and_then(|c| c.as_array()) + { + let mut not_added_crate_types = super::CRATE_TYPES.to_vec(); + for t in crate_types { + let t = t.as_str().expect("crate-type must be a string"); + if let Some(i) = not_added_crate_types.iter().position(|ty| ty == &t) { + not_added_crate_types.remove(i); + } + } + assert!( + not_added_crate_types.is_empty(), + "missing crate-type: {not_added_crate_types:?}" + ); + } } } diff --git a/tooling/cli/src/migrate/mod.rs b/tooling/cli/src/migrate/mod.rs index 4d507fab6..6d762a1c1 100644 --- a/tooling/cli/src/migrate/mod.rs +++ b/tooling/cli/src/migrate/mod.rs @@ -15,9 +15,19 @@ pub fn command() -> Result<()> { let tauri_dir = tauri_dir(); let app_dir = app_dir(); - config::migrate(&tauri_dir)?; + let migrated = config::migrate(&tauri_dir)?; manifest::migrate(&tauri_dir)?; frontend::migrate(app_dir, &tauri_dir)?; + // Add plugins + for plugin in migrated.plugins { + crate::add::command(crate::add::Options { + plugin, + branch: None, + tag: None, + rev: None, + })? + } + Ok(()) }