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