Skip to main content

turbopack_resolve/
ecmascript.rs

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