turbopack_resolve/
ecmascript.rs

1use anyhow::Result;
2use turbo_tasks::{ResolvedVc, Value, Vc};
3use turbopack_core::{
4    issue::IssueSource,
5    reference_type::{CommonJsReferenceSubType, EcmaScriptModulesReferenceSubType, ReferenceType},
6    resolve::{
7        ModuleResolveResult, ResolveResult, handle_resolve_error, handle_resolve_source_error,
8        options::{
9            ConditionValue, ResolutionConditions, ResolveInPackage, ResolveIntoPackage,
10            ResolveOptions,
11        },
12        origin::{ResolveOrigin, ResolveOriginExt},
13        parse::Request,
14        resolve,
15    },
16};
17/// Retrieves the [ResolutionConditions] of the "into" and "in" package resolution options, so that
18/// they can be manipulated together.
19///
20/// - "into" allows a package to control how it can be imported
21/// - "in" controls how this package imports others
22pub fn get_condition_maps(
23    options: &mut ResolveOptions,
24) -> impl Iterator<Item = &mut ResolutionConditions> {
25    options
26        .into_package
27        .iter_mut()
28        .filter_map(|item| {
29            if let ResolveIntoPackage::ExportsField { conditions, .. } = item {
30                Some(conditions)
31            } else {
32                None
33            }
34        })
35        .chain(options.in_package.iter_mut().filter_map(|item| {
36            if let ResolveInPackage::ImportsField { conditions, .. } = item {
37                Some(conditions)
38            } else {
39                None
40            }
41        }))
42}
43
44pub fn apply_esm_specific_options(
45    options: Vc<ResolveOptions>,
46    reference_type: Value<ReferenceType>,
47) -> Vc<ResolveOptions> {
48    apply_esm_specific_options_internal(
49        options,
50        matches!(
51            reference_type.into_value(),
52            ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::ImportWithType(_))
53        ),
54    )
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("import".into(), ConditionValue::Set);
67        conditions.insert("require".into(), 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("import".into(), ConditionValue::Unset);
84        conditions.insert("require".into(), 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: Value<EcmaScriptModulesReferenceSubType>,
93    is_optional: bool,
94    issue_source: Option<IssueSource>,
95) -> Result<Vc<ModuleResolveResult>> {
96    let ty = Value::new(ReferenceType::EcmaScriptModules(ty.into_value()));
97    let options = apply_esm_specific_options(origin.resolve_options(ty.clone()), 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 = Value::new(ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined));
112    let options = apply_cjs_specific_options(origin.resolve_options(ty.clone()))
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 = Value::new(ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined));
127    let options = apply_cjs_specific_options(origin.resolve_options(ty.clone()))
128        .resolve()
129        .await?;
130    let result = resolve(
131        origin.origin_path().parent().resolve().await?,
132        ty.clone(),
133        *request,
134        options,
135    );
136
137    handle_resolve_source_error(
138        result,
139        ty,
140        origin.origin_path(),
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: Value<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(),
165        request,
166        options,
167        is_optional,
168        issue_source,
169    )
170    .await
171}