fallow_graph/resolve/
mod.rs1mod dynamic_imports;
19pub(crate) mod fallbacks;
20mod path_info;
21mod re_exports;
22mod react_native;
23mod require_imports;
24mod specifier;
25mod static_imports;
26#[cfg(test)]
27mod tests;
28mod types;
29mod upgrades;
30
31pub use path_info::{extract_package_name, is_bare_specifier, is_path_alias};
32pub use types::{ResolveResult, ResolvedImport, ResolvedModule, ResolvedReExport};
33
34use std::path::{Path, PathBuf};
35use std::sync::Mutex;
36
37use rayon::prelude::*;
38use rustc_hash::{FxHashMap, FxHashSet};
39
40use fallow_types::discover::{DiscoveredFile, FileId};
41use fallow_types::extract::ModuleInfo;
42
43use dynamic_imports::{resolve_dynamic_imports, resolve_dynamic_patterns};
44use re_exports::resolve_re_exports;
45use require_imports::resolve_require_imports;
46use specifier::create_resolver;
47use static_imports::resolve_static_imports;
48use types::ResolveContext;
49use upgrades::apply_specifier_upgrades;
50
51#[must_use]
53pub fn resolve_all_imports(
54 modules: &[ModuleInfo],
55 files: &[DiscoveredFile],
56 workspaces: &[fallow_config::WorkspaceInfo],
57 active_plugins: &[String],
58 path_aliases: &[(String, String)],
59 root: &Path,
60) -> Vec<ResolvedModule> {
61 let canonical_ws_roots: Vec<PathBuf> = workspaces
66 .par_iter()
67 .map(|ws| dunce::canonicalize(&ws.root).unwrap_or_else(|_| ws.root.clone()))
68 .collect();
69 let workspace_roots: FxHashMap<&str, &Path> = workspaces
70 .iter()
71 .zip(canonical_ws_roots.iter())
72 .map(|(ws, canonical)| (ws.name.as_str(), canonical.as_path()))
73 .collect();
74
75 let root_is_canonical = dunce::canonicalize(root).is_ok_and(|c| c == root);
80
81 let canonical_paths: Vec<PathBuf> = if root_is_canonical {
84 Vec::new()
85 } else {
86 files
87 .par_iter()
88 .map(|f| dunce::canonicalize(&f.path).unwrap_or_else(|_| f.path.clone()))
89 .collect()
90 };
91
92 let path_to_id: FxHashMap<&Path, FileId> = if root_is_canonical {
95 files.iter().map(|f| (f.path.as_path(), f.id)).collect()
96 } else {
97 canonical_paths
98 .iter()
99 .enumerate()
100 .map(|(idx, canonical)| (canonical.as_path(), files[idx].id))
101 .collect()
102 };
103
104 let raw_path_to_id: FxHashMap<&Path, FileId> =
106 files.iter().map(|f| (f.path.as_path(), f.id)).collect();
107
108 let file_paths: Vec<&Path> = files.iter().map(|f| f.path.as_path()).collect();
110
111 let resolver = create_resolver(active_plugins);
113
114 let canonical_fallback = if root_is_canonical {
117 Some(types::CanonicalFallback::new(files))
118 } else {
119 None
120 };
121
122 let tsconfig_warned: Mutex<FxHashSet<String>> = Mutex::new(FxHashSet::default());
124
125 let ctx = ResolveContext {
127 resolver: &resolver,
128 path_to_id: &path_to_id,
129 raw_path_to_id: &raw_path_to_id,
130 workspace_roots: &workspace_roots,
131 path_aliases,
132 root,
133 canonical_fallback: canonical_fallback.as_ref(),
134 tsconfig_warned: &tsconfig_warned,
135 };
136
137 let mut resolved: Vec<ResolvedModule> = modules
142 .par_iter()
143 .filter_map(|module| {
144 let Some(file_path) = file_paths.get(module.file_id.0 as usize) else {
145 tracing::warn!(
146 file_id = module.file_id.0,
147 "Skipping module with unknown file_id during resolution"
148 );
149 return None;
150 };
151
152 let mut all_imports = resolve_static_imports(&ctx, file_path, &module.imports);
153 all_imports.extend(resolve_require_imports(
154 &ctx,
155 file_path,
156 &module.require_calls,
157 ));
158
159 let from_dir = if canonical_paths.is_empty() {
160 file_path.parent().unwrap_or(file_path)
162 } else {
163 canonical_paths
164 .get(module.file_id.0 as usize)
165 .and_then(|p| p.parent())
166 .unwrap_or(file_path)
167 };
168
169 Some(ResolvedModule {
170 file_id: module.file_id,
171 path: file_path.to_path_buf(),
172 exports: module.exports.clone(),
173 re_exports: resolve_re_exports(&ctx, file_path, &module.re_exports),
174 resolved_imports: all_imports,
175 resolved_dynamic_imports: resolve_dynamic_imports(
176 &ctx,
177 file_path,
178 &module.dynamic_imports,
179 ),
180 resolved_dynamic_patterns: resolve_dynamic_patterns(
181 from_dir,
182 &module.dynamic_import_patterns,
183 &canonical_paths,
184 files,
185 ),
186 member_accesses: module.member_accesses.clone(),
187 whole_object_uses: module.whole_object_uses.clone(),
188 has_cjs_exports: module.has_cjs_exports,
189 unused_import_bindings: module.unused_import_bindings.iter().cloned().collect(),
190 })
191 })
192 .collect();
193
194 apply_specifier_upgrades(&mut resolved);
195
196 resolved
197}