浏览代码

feat(cli): automate API -> plugin migration (#7561)

Lucas Fernandes Nogueira 2 年之前
父节点
当前提交
8af2497496

+ 6 - 0
.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.

+ 7 - 2
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<F: Fn(&PathBuf) -> bool>(dir: &Path, checker: F) -> Option<PathBuf> {
+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<F: Fn(&PathBuf) -> bool>(dir: &Path, checker: F) -> Option<PathBuf> {
     }
   }
 
-  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<F: Fn(&PathBuf) -> bool>(dir: &Path, checker: F) -> Option<PathBuf> {
+  let mut builder = walk_builder(dir);
   builder
     .require_git(false)
     .ignore(false)

+ 39 - 1
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<ExitStatus> {
+    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)
+      }
+    }
+  }
 }

+ 93 - 0
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: &regex::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(())
+}

+ 7 - 1
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(())
 }