1use anyhow::Result;
2use next_taskless::{BUN_EXTERNALS, EDGE_NODE_EXTERNALS, NODE_EXTERNALS};
3use turbo_rcstr::rcstr;
4use turbo_tasks::{ResolvedVc, Vc};
5use turbo_tasks_fs::{FileSystem, FileSystemPath};
6use turbopack_core::resolve::{
7 AliasMap, AliasPattern, ExternalTraced, ExternalType, FindContextFileResult, find_context_file,
8 options::{
9 ConditionValue, ImportMap, ImportMapping, ResolutionConditions, ResolveInPackage,
10 ResolveIntoPackage, ResolveModules, ResolveOptions,
11 },
12};
13
14use crate::{
15 resolve_options_context::{ResolveOptionsContext, TsConfigHandling},
16 typescript::{apply_tsconfig_resolve_options, tsconfig, tsconfig_resolve_options},
17};
18
19#[turbo_tasks::function]
20async fn base_resolve_options(
21 fs: ResolvedVc<Box<dyn FileSystem>>,
22 options_context: Vc<ResolveOptionsContext>,
23) -> Result<Vc<ResolveOptions>> {
24 let opt = options_context.await?;
25 let emulating = opt.emulate_environment;
26 let root = fs.root().owned().await?;
27 let mut direct_mappings = AliasMap::new();
28 let node_externals = if let Some(environment) = emulating {
29 environment.node_externals().owned().await?
30 } else {
31 opt.enable_node_externals
32 };
33 let untraced_external_cell =
34 ImportMapping::External(None, ExternalType::CommonJs, ExternalTraced::Untraced)
35 .resolved_cell();
36
37 for req in BUN_EXTERNALS {
38 direct_mappings.insert(AliasPattern::exact(req), untraced_external_cell);
39 }
40
41 if node_externals || opt.enable_edge_node_externals {
42 if node_externals {
43 for req in NODE_EXTERNALS {
44 direct_mappings.insert(AliasPattern::exact(req), untraced_external_cell);
45 direct_mappings.insert(
46 AliasPattern::exact(format!("node:{req}")),
47 untraced_external_cell,
48 );
49 }
50 }
51
52 if opt.enable_edge_node_externals {
53 for req in EDGE_NODE_EXTERNALS {
54 direct_mappings.insert(
55 AliasPattern::exact(req),
56 ImportMapping::External(
57 Some(format!("node:{req}").into()),
58 ExternalType::CommonJs,
59 ExternalTraced::Untraced,
60 )
61 .resolved_cell(),
62 );
63 direct_mappings.insert(
64 AliasPattern::exact(format!("node:{req}")),
65 untraced_external_cell,
66 );
67 }
68 }
69 }
70
71 let mut import_map = ImportMap::new(direct_mappings);
72 if let Some(additional_import_map) = opt.import_map {
73 let additional_import_map = additional_import_map.await?;
74 import_map.extend_ref(&additional_import_map);
75 }
76 let import_map = import_map.resolved_cell();
77
78 let conditions = {
79 let mut conditions: ResolutionConditions = [
80 (rcstr!("import"), ConditionValue::Unknown),
81 (rcstr!("require"), ConditionValue::Unknown),
82 ]
83 .into_iter()
84 .collect();
85 if opt.browser {
86 conditions.insert(rcstr!("browser"), ConditionValue::Set);
87 }
88 if opt.module {
89 conditions.insert(rcstr!("module"), ConditionValue::Set);
90 }
91 if opt.module_sync != ConditionValue::Unset {
92 conditions.insert(rcstr!("module-sync"), opt.module_sync);
93 }
94 if let Some(environment) = emulating {
95 for condition in environment.resolve_conditions().await?.iter() {
96 conditions.insert(condition.clone(), ConditionValue::Set);
97 }
98 }
99 for condition in opt.custom_conditions.iter() {
100 conditions.insert(condition.clone(), ConditionValue::Set);
101 }
102 let dev = conditions.get("development").cloned();
104 let prod = conditions.get("production").cloned();
105 if prod.is_none() {
106 conditions.insert(
107 rcstr!("production"),
108 if matches!(dev, Some(ConditionValue::Set)) {
109 ConditionValue::Unset
110 } else {
111 ConditionValue::Unknown
112 },
113 );
114 }
115 if dev.is_none() {
116 conditions.insert(
117 rcstr!("development"),
118 if matches!(prod, Some(ConditionValue::Set)) {
119 ConditionValue::Unset
120 } else {
121 ConditionValue::Unknown
122 },
123 );
124 }
125 conditions
126 };
127
128 let extensions = if let Some(custom_extension) = &opt.custom_extensions {
129 custom_extension.clone()
130 } else if let Some(environment) = emulating {
131 environment.resolve_extensions().owned().await?
132 } else {
133 let mut ext = Vec::new();
134 if opt.enable_typescript && opt.enable_react {
135 ext.push(rcstr!(".tsx"));
136 }
137 if opt.enable_typescript {
138 ext.push(rcstr!(".ts"));
139 }
140 if opt.enable_react {
141 ext.push(rcstr!(".jsx"));
142 }
143 ext.push(rcstr!(".js"));
144 if opt.enable_mjs_extension {
145 ext.push(rcstr!(".mjs"));
146 }
147 if opt.enable_node_native_modules {
148 ext.push(rcstr!(".node"));
149 }
150 ext.push(rcstr!(".json"));
151 ext
152 };
153 Ok(ResolveOptions {
154 extensions,
155 modules: if let Some(environment) = emulating {
156 if *environment.resolve_node_modules().await? {
157 vec![ResolveModules::Nested(
158 root.clone(),
159 vec![rcstr!("node_modules")],
160 )]
161 } else {
162 Vec::new()
163 }
164 } else {
165 let mut mods = Vec::new();
166 if let Some(dir) = &opt.enable_node_modules {
167 mods.push(ResolveModules::Nested(
168 dir.clone(),
169 vec![rcstr!("node_modules")],
170 ));
171 }
172 mods
173 },
174 into_package: {
175 let mut resolve_into = vec![ResolveIntoPackage::ExportsField {
176 conditions: conditions.clone(),
177 unspecified_conditions: ConditionValue::Unset,
178 }];
179 if opt.browser {
180 resolve_into.push(ResolveIntoPackage::MainField {
181 field: rcstr!("browser"),
182 });
183 }
184 if opt.module {
185 resolve_into.push(ResolveIntoPackage::MainField {
186 field: rcstr!("module"),
187 });
188 }
189 resolve_into.push(ResolveIntoPackage::MainField {
190 field: rcstr!("main"),
191 });
192 resolve_into
193 },
194 in_package: {
195 let mut resolve_in = vec![ResolveInPackage::ImportsField {
196 conditions,
197 unspecified_conditions: ConditionValue::Unset,
198 }];
199 if opt.browser {
200 resolve_in.push(ResolveInPackage::AliasField(rcstr!("browser")));
201 }
202 resolve_in
203 },
204 default_files: vec![rcstr!("index")],
205 import_map: Some(import_map),
206 resolved_map: opt.resolved_map,
207 after_resolve_plugins: opt.after_resolve_plugins.clone(),
208 before_resolve_plugins: opt.before_resolve_plugins.clone(),
209 loose_errors: opt.loose_errors,
210 collect_affecting_sources: opt.collect_affecting_sources,
211 ..Default::default()
212 }
213 .cell())
214}
215
216#[turbo_tasks::function]
217pub async fn resolve_options(
218 resolve_path: FileSystemPath,
219 options_context: Vc<ResolveOptionsContext>,
220) -> Result<Vc<ResolveOptions>> {
221 let options_context_value = options_context.await?;
222 if !options_context_value.rules.is_empty() {
223 for (condition, new_options_context) in options_context_value.rules.iter() {
224 if condition.matches(&resolve_path) {
225 return Ok(resolve_options(resolve_path, **new_options_context));
226 }
227 }
228 }
229
230 let resolve_options = base_resolve_options(*resolve_path.fs, options_context);
231
232 let resolve_options = if options_context_value.enable_typescript {
233 let find_tsconfig = async || {
234 let tsconfig = find_context_file(
236 resolve_path.clone(),
237 tsconfig(),
238 options_context_value.collect_affecting_sources,
239 )
240 .await?;
241 anyhow::Ok::<Vc<ResolveOptions>>(match &*tsconfig {
242 FindContextFileResult::Found(path, _) => apply_tsconfig_resolve_options(
243 resolve_options,
244 tsconfig_resolve_options(path.clone()),
245 ),
246 FindContextFileResult::NotFound(_) => resolve_options,
247 })
248 };
249
250 match &options_context_value.tsconfig_path {
253 TsConfigHandling::Disabled => resolve_options,
254 TsConfigHandling::ContextFile => find_tsconfig().await?,
255 TsConfigHandling::Fixed(tsconfig_path) => {
256 let meta = tsconfig_path.metadata().await;
257 if meta.is_ok() {
258 apply_tsconfig_resolve_options(
260 resolve_options,
261 tsconfig_resolve_options(tsconfig_path.clone()),
262 )
263 } else {
264 find_tsconfig().await?
268 }
269 }
270 }
271 } else {
272 resolve_options
273 };
274
275 let resolve_options = options_context_value
278 .import_map
279 .map(|import_map| resolve_options.with_extended_import_map(*import_map))
280 .unwrap_or(resolve_options);
281 let resolve_options = options_context_value
283 .fallback_import_map
284 .map(|fallback_import_map| {
285 resolve_options.with_extended_fallback_import_map(*fallback_import_map)
286 })
287 .unwrap_or(resolve_options);
288
289 Ok(resolve_options)
290}