diff --git a/.changes/support-custom-repo-structure.md b/.changes/support-custom-repo-structure.md new file mode 100644 index 000000000..33ae9c3d2 --- /dev/null +++ b/.changes/support-custom-repo-structure.md @@ -0,0 +1,7 @@ +--- +"@tauri-apps/cli": patch:enhance +"tauri-cli": patch:enhance +--- + +Support custom project directory structure where the Tauri app folder is not a subfolder of the frontend project. +The frontend and Tauri app project paths can be set with the `TAURI_FRONTEND_PATH` and the `TAURI_APP_PATH` environment variables respectively. diff --git a/crates/tauri-cli/src/helpers/app_paths.rs b/crates/tauri-cli/src/helpers/app_paths.rs index 74db04236..8ece080ca 100644 --- a/crates/tauri-cli/src/helpers/app_paths.rs +++ b/crates/tauri-cli/src/helpers/app_paths.rs @@ -18,6 +18,11 @@ use tauri_utils::{ }; const TAURI_GITIGNORE: &[u8] = include_bytes!("../../tauri.gitignore"); +// path to the Tauri app (Rust crate) directory, usually /src-tauri +const ENV_TAURI_APP_PATH: &str = "TAURI_APP_PATH"; +// path to the frontend app directory +const ENV_TAURI_FRONTEND_PATH: &str = "TAURI_FRONTEND_PATH"; + static APP_DIR: OnceLock = OnceLock::new(); static TAURI_DIR: OnceLock = OnceLock::new(); @@ -68,29 +73,33 @@ fn lookup bool>(dir: &Path, checker: F) -> Option { None } +fn env_tauri_app_path() -> Option { + std::env::var(ENV_TAURI_APP_PATH) + .map(PathBuf::from) + .ok()? + .canonicalize() + .ok() +} + +fn env_tauri_frontend_path() -> Option { + std::env::var(ENV_TAURI_FRONTEND_PATH) + .map(PathBuf::from) + .ok()? + .canonicalize() + .ok() +} + pub fn resolve_tauri_dir() -> Option { - let Ok(cwd) = current_dir() else { - return None; - }; + let src_dir = env_tauri_frontend_path().or_else(|| current_dir().ok())?; - if cwd.join(ConfigFormat::Json.into_file_name()).exists() - || cwd.join(ConfigFormat::Json5.into_file_name()).exists() - || cwd.join(ConfigFormat::Toml.into_file_name()).exists() + if src_dir.join(ConfigFormat::Json.into_file_name()).exists() + || src_dir.join(ConfigFormat::Json5.into_file_name()).exists() + || src_dir.join(ConfigFormat::Toml.into_file_name()).exists() { - return Some(cwd); + return Some(src_dir); } - let src_tauri = cwd.join("src-tauri"); - if src_tauri.join(ConfigFormat::Json.into_file_name()).exists() - || src_tauri - .join(ConfigFormat::Json5.into_file_name()) - .exists() - || src_tauri.join(ConfigFormat::Toml.into_file_name()).exists() - { - return Some(src_tauri); - } - - lookup(&cwd, |path| { + lookup(&src_dir, |path| { folder_has_configuration_file(Target::Linux, path) || is_configuration_file(Target::Linux, path) }) .map(|p| { @@ -103,13 +112,15 @@ pub fn resolve_tauri_dir() -> Option { } pub fn resolve() { - TAURI_DIR.set(resolve_tauri_dir().unwrap_or_else(|| - panic!("Couldn't recognize the current folder as a Tauri project. It must contain a `{}`, `{}` or `{}` file in any subfolder.", + TAURI_DIR.set(resolve_tauri_dir().unwrap_or_else(|| { + let env_var_name = env_tauri_frontend_path().is_some().then(|| format!("`{ENV_TAURI_FRONTEND_PATH}`")); + panic!("Couldn't recognize the {} folder as a Tauri project. It must contain a `{}`, `{}` or `{}` file in any subfolder.", + env_var_name.as_deref().unwrap_or("current"), ConfigFormat::Json.into_file_name(), ConfigFormat::Json5.into_file_name(), ConfigFormat::Toml.into_file_name() ) - )).expect("tauri dir already resolved"); + })).expect("tauri dir already resolved"); APP_DIR .set(resolve_app_dir().unwrap_or_else(|| tauri_dir().parent().unwrap().to_path_buf())) .expect("app dir already resolved"); @@ -122,12 +133,13 @@ pub fn tauri_dir() -> &'static PathBuf { } pub fn resolve_app_dir() -> Option { - let cwd = current_dir().expect("failed to read cwd"); - if cwd.join("package.json").exists() { - return Some(cwd); + let app_dir = env_tauri_app_path().unwrap_or_else(|| current_dir().expect("failed to read cwd")); + + if app_dir.join("package.json").exists() { + return Some(app_dir); } - lookup(&cwd, |path| { + lookup(&app_dir, |path| { if let Some(file_name) = path.file_name() { file_name == OsStr::new("package.json") } else {