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,
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 let Some(environment) = emulating {
92 for condition in environment.resolve_conditions().await?.iter() {
93 conditions.insert(condition.clone(), ConditionValue::Set);
94 }
95 }
96 for condition in opt.custom_conditions.iter() {
97 conditions.insert(condition.clone(), ConditionValue::Set);
98 }
99 let dev = conditions.get("development").cloned();
101 let prod = conditions.get("production").cloned();
102 if prod.is_none() {
103 conditions.insert(
104 rcstr!("production"),
105 if matches!(dev, Some(ConditionValue::Set)) {
106 ConditionValue::Unset
107 } else {
108 ConditionValue::Unknown
109 },
110 );
111 }
112 if dev.is_none() {
113 conditions.insert(
114 rcstr!("development"),
115 if matches!(prod, Some(ConditionValue::Set)) {
116 ConditionValue::Unset
117 } else {
118 ConditionValue::Unknown
119 },
120 );
121 }
122 conditions
123 };
124
125 let extensions = if let Some(custom_extension) = &opt.custom_extensions {
126 custom_extension.clone()
127 } else if let Some(environment) = emulating {
128 environment.resolve_extensions().owned().await?
129 } else {
130 let mut ext = Vec::new();
131 if opt.enable_typescript && opt.enable_react {
132 ext.push(rcstr!(".tsx"));
133 }
134 if opt.enable_typescript {
135 ext.push(rcstr!(".ts"));
136 }
137 if opt.enable_react {
138 ext.push(rcstr!(".jsx"));
139 }
140 ext.push(rcstr!(".js"));
141 if opt.enable_mjs_extension {
142 ext.push(rcstr!(".mjs"));
143 }
144 if opt.enable_node_native_modules {
145 ext.push(rcstr!(".node"));
146 }
147 ext.push(rcstr!(".json"));
148 ext
149 };
150 Ok(ResolveOptions {
151 extensions,
152 modules: if let Some(environment) = emulating {
153 if *environment.resolve_node_modules().await? {
154 vec![ResolveModules::Nested(
155 root.clone(),
156 vec![rcstr!("node_modules")],
157 )]
158 } else {
159 Vec::new()
160 }
161 } else {
162 let mut mods = Vec::new();
163 if let Some(dir) = &opt.enable_node_modules {
164 mods.push(ResolveModules::Nested(
165 dir.clone(),
166 vec![rcstr!("node_modules")],
167 ));
168 }
169 mods
170 },
171 into_package: {
172 let mut resolve_into = vec![ResolveIntoPackage::ExportsField {
173 conditions: conditions.clone(),
174 unspecified_conditions: ConditionValue::Unset,
175 }];
176 if opt.browser {
177 resolve_into.push(ResolveIntoPackage::MainField {
178 field: rcstr!("browser"),
179 });
180 }
181 if opt.module {
182 resolve_into.push(ResolveIntoPackage::MainField {
183 field: rcstr!("module"),
184 });
185 }
186 resolve_into.push(ResolveIntoPackage::MainField {
187 field: rcstr!("main"),
188 });
189 resolve_into
190 },
191 in_package: {
192 let mut resolve_in = vec![ResolveInPackage::ImportsField {
193 conditions,
194 unspecified_conditions: ConditionValue::Unset,
195 }];
196 if opt.browser {
197 resolve_in.push(ResolveInPackage::AliasField(rcstr!("browser")));
198 }
199 resolve_in
200 },
201 default_files: vec![rcstr!("index")],
202 import_map: Some(import_map),
203 resolved_map: opt.resolved_map,
204 after_resolve_plugins: opt.after_resolve_plugins.clone(),
205 before_resolve_plugins: opt.before_resolve_plugins.clone(),
206 loose_errors: opt.loose_errors,
207 collect_affecting_sources: opt.collect_affecting_sources,
208 ..Default::default()
209 }
210 .cell())
211}
212
213#[turbo_tasks::function]
214pub async fn resolve_options(
215 resolve_path: FileSystemPath,
216 options_context: Vc<ResolveOptionsContext>,
217) -> Result<Vc<ResolveOptions>> {
218 let options_context_value = options_context.await?;
219 if !options_context_value.rules.is_empty() {
220 for (condition, new_options_context) in options_context_value.rules.iter() {
221 if condition.matches(&resolve_path) {
222 return Ok(resolve_options(resolve_path, **new_options_context));
223 }
224 }
225 }
226
227 let resolve_options = base_resolve_options(*resolve_path.fs, options_context);
228
229 let resolve_options = if options_context_value.enable_typescript {
230 let find_tsconfig = async || {
231 let tsconfig = find_context_file(
233 resolve_path.clone(),
234 tsconfig(),
235 options_context_value.collect_affecting_sources,
236 )
237 .await?;
238 anyhow::Ok::<Vc<ResolveOptions>>(match &*tsconfig {
239 FindContextFileResult::Found(path, _) => apply_tsconfig_resolve_options(
240 resolve_options,
241 tsconfig_resolve_options(path.clone()),
242 ),
243 FindContextFileResult::NotFound(_) => resolve_options,
244 })
245 };
246
247 if let Some(tsconfig_path) = &options_context_value.tsconfig_path {
250 let meta = tsconfig_path.metadata().await;
251 if meta.is_ok() {
252 apply_tsconfig_resolve_options(
254 resolve_options,
255 tsconfig_resolve_options(tsconfig_path.clone()),
256 )
257 } else {
258 find_tsconfig().await?
262 }
263 } else {
264 find_tsconfig().await?
265 }
266 } else {
267 resolve_options
268 };
269
270 let resolve_options = options_context_value
273 .import_map
274 .map(|import_map| resolve_options.with_extended_import_map(*import_map))
275 .unwrap_or(resolve_options);
276 let resolve_options = options_context_value
278 .fallback_import_map
279 .map(|fallback_import_map| {
280 resolve_options.with_extended_fallback_import_map(*fallback_import_map)
281 })
282 .unwrap_or(resolve_options);
283
284 Ok(resolve_options)
285}