diff --git a/.changes/migrate-plugins.md b/.changes/migrate-plugins.md new file mode 100644 index 00000000000..e4c63d3e1a9 --- /dev/null +++ b/.changes/migrate-plugins.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": patch:feat +"@tauri-apps/cli": patch:feat +--- + +The `migrate` command now automatically reads all JavaScript files and updates `@tauri-apps/api` import paths and install the missing plugins. diff --git a/tooling/cli/src/helpers/app_paths.rs b/tooling/cli/src/helpers/app_paths.rs index 77c5bee7acf..30af98f34bd 100644 --- a/tooling/cli/src/helpers/app_paths.rs +++ b/tooling/cli/src/helpers/app_paths.rs @@ -18,7 +18,7 @@ use tauri_utils::config::parse::{ const TAURI_GITIGNORE: &[u8] = include_bytes!("../../tauri.gitignore"); -fn lookup bool>(dir: &Path, checker: F) -> Option { +pub fn walk_builder(path: &Path) -> WalkBuilder { let mut default_gitignore = std::env::temp_dir(); default_gitignore.push(".gitignore"); if !default_gitignore.exists() { @@ -28,9 +28,14 @@ fn lookup bool>(dir: &Path, checker: F) -> Option { } } - let mut builder = WalkBuilder::new(dir); + let mut builder = WalkBuilder::new(path); builder.add_custom_ignore_filename(".taurignore"); let _ = builder.add_ignore(default_gitignore); + builder +} + +fn lookup bool>(dir: &Path, checker: F) -> Option { + let mut builder = walk_builder(dir); builder .require_git(false) .ignore(false) diff --git a/tooling/cli/src/helpers/npm.rs b/tooling/cli/src/helpers/npm.rs index f19d6b32b27..130f1b88a18 100644 --- a/tooling/cli/src/helpers/npm.rs +++ b/tooling/cli/src/helpers/npm.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use std::{fmt::Display, path::Path}; +use crate::{helpers::cross_command, Result}; +use std::{fmt::Display, path::Path, process::ExitStatus}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum PackageManager { @@ -65,4 +66,41 @@ impl PackageManager { found } + + pub fn install(&self, dependencies: &[String]) -> Result { + match self { + PackageManager::Yarn => { + let mut cmd = cross_command("yarn"); + cmd + .arg("add") + .args(dependencies) + .status() + .map_err(Into::into) + } + PackageManager::YarnBerry => { + let mut cmd = cross_command("yarn"); + cmd + .arg("add") + .args(dependencies) + .status() + .map_err(Into::into) + } + PackageManager::Npm => { + let mut cmd = cross_command("npm"); + cmd + .arg("install") + .args(dependencies) + .status() + .map_err(Into::into) + } + PackageManager::Pnpm => { + let mut cmd = cross_command("pnpm"); + cmd + .arg("install") + .args(dependencies) + .status() + .map_err(Into::into) + } + } + } } diff --git a/tooling/cli/src/migrate/frontend.rs b/tooling/cli/src/migrate/frontend.rs new file mode 100644 index 00000000000..2778d892ede --- /dev/null +++ b/tooling/cli/src/migrate/frontend.rs @@ -0,0 +1,93 @@ +// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use crate::{ + helpers::{app_paths::walk_builder, npm::PackageManager}, + Result, +}; + +use std::{ + fs::{read_to_string, write}, + path::Path, + process::Command, +}; + +const CORE_API_MODULES: &[&str] = &["event", "path", "tauri", "mocks"]; +const JS_EXTENSIONS: &[&str] = &["js", "jsx", "ts", "tsx", "mjs"]; + +pub fn migrate(app_dir: &Path, tauri_dir: &Path) -> Result<()> { + let mut new_npm_packages = Vec::new(); + let mut new_cargo_packages = Vec::new(); + + let pm = PackageManager::from_project(app_dir) + .into_iter() + .next() + .unwrap_or(PackageManager::Npm); + + let tauri_api_import_regex = regex::Regex::new(r"@tauri-apps/api/(\w+)").unwrap(); + + for entry in walk_builder(app_dir).build().flatten() { + if entry.file_type().map(|t| t.is_file()).unwrap_or_default() { + let path = entry.path(); + let ext = path.extension().unwrap_or_default(); + if JS_EXTENSIONS.iter().any(|e| e == &ext) { + let js_contents = read_to_string(path)?; + + let new_contents = + tauri_api_import_regex.replace_all(&js_contents, |cap: ®ex::Captures<'_>| { + let module = cap.get(1).unwrap().as_str(); + let original = cap.get(0).unwrap().as_str(); + + if CORE_API_MODULES.contains(&module) { + original.to_string() + } else { + let plugin = format!("@tauri-apps/plugin-{module}"); + log::info!( + "Replacing `{original}` with `{plugin}` on {}", + path.display() + ); + + new_npm_packages.push(plugin.clone()); + new_cargo_packages.push(format!( + "tauri-plugin-{}", + if module == "clipboard" { + "clipboard-manager" + } else { + module + } + )); + + plugin + } + }); + + if new_contents != js_contents { + write(path, new_contents.as_bytes())?; + } + } + } + } + + if !new_npm_packages.is_empty() { + log::info!( + "Installing NPM packages for plugins: {}", + new_npm_packages.join(", ") + ); + pm.install(&new_npm_packages)?; + } + + if !new_cargo_packages.is_empty() { + log::info!( + "Installing Cargo dependencies for plugins: {}", + new_cargo_packages.join(", ") + ); + Command::new("cargo") + .arg("add") + .args(new_cargo_packages) + .current_dir(tauri_dir) + .status()?; + } + + Ok(()) +} diff --git a/tooling/cli/src/migrate/mod.rs b/tooling/cli/src/migrate/mod.rs index e2f0a96bc0b..4d507fab6c9 100644 --- a/tooling/cli/src/migrate/mod.rs +++ b/tooling/cli/src/migrate/mod.rs @@ -2,16 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::{helpers::app_paths::tauri_dir, Result}; +use crate::{ + helpers::app_paths::{app_dir, tauri_dir}, + Result, +}; mod config; +mod frontend; mod manifest; pub fn command() -> Result<()> { let tauri_dir = tauri_dir(); + let app_dir = app_dir(); config::migrate(&tauri_dir)?; manifest::migrate(&tauri_dir)?; + frontend::migrate(app_dir, &tauri_dir)?; Ok(()) }