Skip to main content

turbopack_resolve/
ecmascript.rs

1use anyhow::Result;
2use turbo_rcstr::rcstr;
3use turbo_tasks::{ResolvedVc, Vc};
4use turbopack_core::{
5    issue::IssueSource,
6    reference_type::{CommonJsReferenceSubType, EcmaScriptModulesReferenceSubType, ReferenceType},
7    resolve::{
8        ModuleResolveResult, ResolveErrorMode, ResolveResult,
9        error::{handle_resolve_error, handle_resolve_source_error},
10        options::{
11            ConditionValue, ResolutionConditions, ResolveInPackage, ResolveIntoPackage,
12            ResolveOptions,
13        },
14        origin::{ResolveOrigin, ResolveOriginExt},
15        parse::Request,
16        resolve,
17    },
18};
19/// Retrieves the [ResolutionConditions] of the "into" and "in" package resolution options, so that
20/// they can be manipulated together.
21///
22/// - "into" allows a package to control how it can be imported
23/// - "in" controls how this package imports others
24pub fn get_condition_maps(
25    options: &mut ResolveOptions,
26) -> impl Iterator<Item = &mut ResolutionConditions> {
27    options
28        .into_package
29        .iter_mut()
30        .filter_map(|item| {
31            if let ResolveIntoPackage::ExportsField { conditions, .. } = item {
32                Some(conditions)
33            } else {
34                None
35            }
36        })
37        .chain(options.in_package.iter_mut().filter_map(|item| {
38            if let ResolveInPackage::ImportsField { conditions, .. } = item {
39                Some(conditions)
40            } else {
41                None
42            }
43        }))
44}
45
46pub fn apply_esm_specific_options(
47    options: Vc<ResolveOptions>,
48    reference_type: &ReferenceType,
49) -> Vc<ResolveOptions> {
50    let clear_extensions = matches!(
51        reference_type,
52        ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::ImportWithType(_))
53    );
54
55    apply_esm_specific_options_internal(options, clear_extensions)
56}
57
58#[turbo_tasks::function]
59async fn apply_esm_specific_options_internal(
60    options: Vc<ResolveOptions>,
61    clear_extensions: bool,
62) -> Result<Vc<ResolveOptions>> {
63    let mut options: ResolveOptions = options.owned().await?;
64    // TODO set fully_specified when in strict ESM mode
65    // options.fully_specified = true;
66    for conditions in get_condition_maps(&mut options) {
67        conditions.insert(rcstr!("import"), ConditionValue::Set);
68        conditions.insert(rcstr!("require"), ConditionValue::Unset);
69        // Don't set "module-sync" to ConditionValue::Set here. When tracing, the Node.js runtime
70        // version might not support it yet, so we still want the "import"/"require"/"default"
71        // result anyway.
72    }
73
74    if clear_extensions {
75        options.extensions.clear();
76    }
77
78    options.parse_data_uris = true;
79
80    Ok(options.cell())
81}
82
83#[turbo_tasks::function]
84pub async fn apply_cjs_specific_options(options: Vc<ResolveOptions>) -> Result<Vc<ResolveOptions>> {
85    let mut options: ResolveOptions = options.owned().await?;
86    for conditions in get_condition_maps(&mut options) {
87        conditions.insert(rcstr!("import"), ConditionValue::Unset);
88        conditions.insert(rcstr!("require"), ConditionValue::Set);
89        // Don't set "module-sync" to ConditionValue::Set here. When tracing, the Node.js runtime
90        // version might not support it yet, so we still want the "import"/"require"/"default"
91        // result anyway.
92    }
93    Ok(options.cell())
94}
95
96pub async fn esm_resolve(
97    origin: Vc<Box<dyn ResolveOrigin>>,
98    request: Vc<Request>,
99    ty: EcmaScriptModulesReferenceSubType,
100    error_mode: ResolveErrorMode,
101    issue_source: Option<IssueSource>,
102) -> Result<Vc<ModuleResolveResult>> {
103    let ty = ReferenceType::EcmaScriptModules(ty);
104    let options = *apply_esm_specific_options(origin.resolve_options(), &ty)
105        .to_resolved()
106        .await?;
107    specific_resolve(origin, request, options, ty, error_mode, issue_source).await
108}
109
110#[turbo_tasks::function]
111pub async fn cjs_resolve(
112    origin: Vc<Box<dyn ResolveOrigin>>,
113    request: Vc<Request>,
114    ty: CommonJsReferenceSubType,
115    issue_source: Option<IssueSource>,
116    error_mode: ResolveErrorMode,
117) -> Result<Vc<ModuleResolveResult>> {
118    let ty = ReferenceType::CommonJs(ty);
119    let options = *apply_cjs_specific_options(origin.resolve_options())
120        .to_resolved()
121        .await?;
122    specific_resolve(origin, request, options, ty, error_mode, issue_source).await
123}
124
125#[turbo_tasks::function]
126pub async fn cjs_resolve_source(
127    origin: ResolvedVc<Box<dyn ResolveOrigin>>,
128    request: ResolvedVc<Request>,
129    ty: CommonJsReferenceSubType,
130    issue_source: Option<IssueSource>,
131    error_mode: ResolveErrorMode,
132) -> Result<Vc<ResolveResult>> {
133    let ty = ReferenceType::CommonJs(ty);
134    let options = *apply_cjs_specific_options(origin.resolve_options())
135        .to_resolved()
136        .await?;
137    let result = resolve(
138        origin.origin_path().await?.parent(),
139        ty.clone(),
140        *request,
141        options,
142    );
143
144    handle_resolve_source_error(
145        result,
146        ty,
147        *origin,
148        *request,
149        options,
150        error_mode,
151        issue_source,
152    )
153    .await
154}
155
156async fn specific_resolve(
157    origin: Vc<Box<dyn ResolveOrigin>>,
158    request: Vc<Request>,
159    options: Vc<ResolveOptions>,
160    reference_type: ReferenceType,
161    error_mode: ResolveErrorMode,
162    issue_source: Option<IssueSource>,
163) -> Result<Vc<ModuleResolveResult>> {
164    let result = origin
165        .resolve_asset(request, options, reference_type.clone())
166        .await?;
167
168    handle_resolve_error(
169        result,
170        reference_type,
171        origin,
172        request,
173        options,
174        error_mode,
175        issue_source,
176    )
177    .await
178}