Skip to main content

source_map_tauri/
discovery.rs

1use std::path::{Path, PathBuf};
2
3use anyhow::Result;
4use serde::Serialize;
5use walkdir::{DirEntry, WalkDir};
6
7use crate::config::{normalize_path, ResolvedConfig};
8
9#[derive(Debug, Clone, Default)]
10pub struct RepoDiscovery {
11    pub frontend_files: Vec<PathBuf>,
12    pub frontend_test_files: Vec<PathBuf>,
13    pub rust_files: Vec<PathBuf>,
14    pub rust_test_files: Vec<PathBuf>,
15    pub guest_js_files: Vec<PathBuf>,
16    pub plugin_rust_files: Vec<PathBuf>,
17    pub tauri_configs: Vec<PathBuf>,
18    pub capability_files: Vec<PathBuf>,
19    pub permission_files: Vec<PathBuf>,
20    pub package_json: Option<PathBuf>,
21    pub tsconfig: Option<PathBuf>,
22    pub vite_configs: Vec<PathBuf>,
23}
24
25#[derive(Debug, Clone, Serialize)]
26pub struct DoctorReport {
27    pub root: String,
28    pub repo: String,
29    pub src_tauri_exists: bool,
30    pub cargo_toml_exists: bool,
31    pub tauri_config_exists: bool,
32    pub package_json_exists: bool,
33    pub frontend_files_found: usize,
34    pub capability_files_found: usize,
35    pub permission_files_found: usize,
36    pub plugin_roots_found: usize,
37    pub sourcemap_support_hint: String,
38}
39
40fn has_segment(path: &str, segment: &str) -> bool {
41    path.starts_with(&format!("{segment}/")) || path.contains(&format!("/{segment}/"))
42}
43
44fn is_ignored_dir(entry: &DirEntry, config: &ResolvedConfig) -> bool {
45    if !entry.file_type().is_dir() {
46        return false;
47    }
48    let name = entry.file_name().to_string_lossy();
49    match name.as_ref() {
50        ".git" => true,
51        "node_modules" => !config.file.scan.include_node_modules,
52        "target" => !config.file.scan.include_target,
53        "dist" | "build" => !config.file.scan.include_dist,
54        "vendor" => !config.file.scan.include_vendor,
55        "coverage" | "logs" | "tmp" | "storage" => true,
56        _ => false,
57    }
58}
59
60pub fn discover(config: &ResolvedConfig) -> Result<RepoDiscovery> {
61    let mut discovery = RepoDiscovery::default();
62    let walker = WalkDir::new(&config.root)
63        .into_iter()
64        .filter_entry(|entry| !is_ignored_dir(entry, config));
65
66    for item in walker.flatten().filter(|entry| entry.file_type().is_file()) {
67        let path = item.path().to_path_buf();
68        let normalized = normalize_path(&config.root, &path);
69        let file_name = path
70            .file_name()
71            .and_then(|item| item.to_str())
72            .unwrap_or_default();
73
74        if normalized == "package.json" {
75            discovery.package_json = Some(path.clone());
76        }
77        if normalized == "tsconfig.json" {
78            discovery.tsconfig = Some(path.clone());
79        }
80        if file_name == "vite.config.ts" || file_name == "vite.config.js" {
81            discovery.vite_configs.push(path.clone());
82        }
83        if normalized.ends_with("src-tauri/tauri.conf.json")
84            || (normalized.contains("src-tauri/tauri.") && normalized.ends_with(".conf.json"))
85        {
86            discovery.tauri_configs.push(path.clone());
87        }
88        if has_segment(&normalized, "capabilities")
89            && (normalized.ends_with(".json") || normalized.ends_with(".toml"))
90        {
91            discovery.capability_files.push(path.clone());
92        }
93        if has_segment(&normalized, "permissions")
94            && (normalized.ends_with(".json") || normalized.ends_with(".toml"))
95        {
96            discovery.permission_files.push(path.clone());
97        }
98
99        let is_test = normalized.contains(".test.")
100            || normalized.contains(".spec.")
101            || normalized.contains("__tests__/");
102        if normalized.ends_with(".ts")
103            || normalized.ends_with(".tsx")
104            || normalized.ends_with(".js")
105            || normalized.ends_with(".jsx")
106        {
107            if has_segment(&normalized, "guest-js") || has_segment(&normalized, "dist-js") {
108                discovery.guest_js_files.push(path.clone());
109            } else if is_test {
110                discovery.frontend_test_files.push(path.clone());
111            } else if has_segment(&normalized, "src") {
112                discovery.frontend_files.push(path.clone());
113            }
114        }
115
116        if normalized.ends_with(".rs") {
117            if has_segment(&normalized, "plugins") && has_segment(&normalized, "src") {
118                discovery.plugin_rust_files.push(path.clone());
119            } else if normalized.contains("/tests/") {
120                discovery.rust_test_files.push(path.clone());
121            } else {
122                discovery.rust_files.push(path.clone());
123            }
124        }
125    }
126
127    Ok(discovery)
128}
129
130pub fn doctor(config: &ResolvedConfig) -> Result<DoctorReport> {
131    let discovery = discover(config)?;
132    let src_tauri = config.root.join("src-tauri");
133    let plugin_root_count = config
134        .file
135        .project
136        .plugin_roots
137        .iter()
138        .filter(|item| config.root.join(item).exists())
139        .count();
140
141    Ok(DoctorReport {
142        root: normalize_path(Path::new("."), &config.root),
143        repo: config.repo.clone(),
144        src_tauri_exists: src_tauri.exists(),
145        cargo_toml_exists: src_tauri.join("Cargo.toml").exists(),
146        tauri_config_exists: !discovery.tauri_configs.is_empty(),
147        package_json_exists: discovery.package_json.is_some(),
148        frontend_files_found: discovery.frontend_files.len(),
149        capability_files_found: discovery.capability_files.len(),
150        permission_files_found: discovery.permission_files.len(),
151        plugin_roots_found: plugin_root_count,
152        sourcemap_support_hint: if discovery.vite_configs.is_empty() {
153            "vite config not found".to_owned()
154        } else {
155            "vite config found; enable build.sourcemap for trace support".to_owned()
156        },
157    })
158}