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