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};
35
36use rayon::prelude::*;
37use rustc_hash::FxHashMap;
38
39use fallow_types::discover::{DiscoveredFile, FileId};
40use fallow_types::extract::ModuleInfo;
41
42use dynamic_imports::{resolve_dynamic_imports, resolve_dynamic_patterns};
43use re_exports::resolve_re_exports;
44use require_imports::resolve_require_imports;
45use specifier::create_resolver;
46use static_imports::resolve_static_imports;
47use types::ResolveContext;
48use upgrades::apply_specifier_upgrades;
49
50#[must_use]
52pub fn resolve_all_imports(
53 modules: &[ModuleInfo],
54 files: &[DiscoveredFile],
55 workspaces: &[fallow_config::WorkspaceInfo],
56 active_plugins: &[String],
57 path_aliases: &[(String, String)],
58 root: &Path,
59) -> Vec<ResolvedModule> {
60 let canonical_ws_roots: Vec<PathBuf> = workspaces
65 .par_iter()
66 .map(|ws| dunce::canonicalize(&ws.root).unwrap_or_else(|_| ws.root.clone()))
67 .collect();
68 let workspace_roots: FxHashMap<&str, &Path> = workspaces
69 .iter()
70 .zip(canonical_ws_roots.iter())
71 .map(|(ws, canonical)| (ws.name.as_str(), canonical.as_path()))
72 .collect();
73
74 let root_is_canonical = dunce::canonicalize(root).is_ok_and(|c| c == root);
79
80 let canonical_paths: Vec<PathBuf> = if root_is_canonical {
83 Vec::new()
84 } else {
85 files
86 .par_iter()
87 .map(|f| dunce::canonicalize(&f.path).unwrap_or_else(|_| f.path.clone()))
88 .collect()
89 };
90
91 let path_to_id: FxHashMap<&Path, FileId> = if root_is_canonical {
94 files.iter().map(|f| (f.path.as_path(), f.id)).collect()
95 } else {
96 canonical_paths
97 .iter()
98 .enumerate()
99 .map(|(idx, canonical)| (canonical.as_path(), files[idx].id))
100 .collect()
101 };
102
103 let raw_path_to_id: FxHashMap<&Path, FileId> =
105 files.iter().map(|f| (f.path.as_path(), f.id)).collect();
106
107 let file_paths: Vec<&Path> = files.iter().map(|f| f.path.as_path()).collect();
109
110 let resolver = create_resolver(active_plugins);
112
113 let canonical_fallback = if root_is_canonical {
116 Some(types::CanonicalFallback::new(files))
117 } else {
118 None
119 };
120
121 let ctx = ResolveContext {
123 resolver: &resolver,
124 path_to_id: &path_to_id,
125 raw_path_to_id: &raw_path_to_id,
126 workspace_roots: &workspace_roots,
127 path_aliases,
128 root,
129 canonical_fallback: canonical_fallback.as_ref(),
130 };
131
132 let mut resolved: Vec<ResolvedModule> = modules
137 .par_iter()
138 .filter_map(|module| {
139 let Some(file_path) = file_paths.get(module.file_id.0 as usize) else {
140 tracing::warn!(
141 file_id = module.file_id.0,
142 "Skipping module with unknown file_id during resolution"
143 );
144 return None;
145 };
146
147 let mut all_imports = resolve_static_imports(&ctx, file_path, &module.imports);
148 all_imports.extend(resolve_require_imports(
149 &ctx,
150 file_path,
151 &module.require_calls,
152 ));
153
154 let from_dir = if canonical_paths.is_empty() {
155 file_path.parent().unwrap_or(file_path)
157 } else {
158 canonical_paths
159 .get(module.file_id.0 as usize)
160 .and_then(|p| p.parent())
161 .unwrap_or(file_path)
162 };
163
164 Some(ResolvedModule {
165 file_id: module.file_id,
166 path: file_path.to_path_buf(),
167 exports: module.exports.clone(),
168 re_exports: resolve_re_exports(&ctx, file_path, &module.re_exports),
169 resolved_imports: all_imports,
170 resolved_dynamic_imports: resolve_dynamic_imports(
171 &ctx,
172 file_path,
173 &module.dynamic_imports,
174 ),
175 resolved_dynamic_patterns: resolve_dynamic_patterns(
176 from_dir,
177 &module.dynamic_import_patterns,
178 &canonical_paths,
179 files,
180 ),
181 member_accesses: module.member_accesses.clone(),
182 whole_object_uses: module.whole_object_uses.clone(),
183 has_cjs_exports: module.has_cjs_exports,
184 unused_import_bindings: module.unused_import_bindings.iter().cloned().collect(),
185 })
186 })
187 .collect();
188
189 apply_specifier_upgrades(&mut resolved);
190
191 resolved
192}