turbopack_ecmascript/references/
async_module.rs1use anyhow::Result;
2use bincode::{Decode, Encode};
3use swc_core::{
4 common::DUMMY_SP,
5 ecma::ast::{ArrayLit, ArrayPat, Expr, Ident},
6 quote,
7};
8use turbo_rcstr::rcstr;
9use turbo_tasks::{
10 FxIndexSet, NonLocalValue, ReadRef, ResolvedVc, TryFlatJoinIterExt, TryJoinIterExt, Vc,
11 trace::TraceRawVcs,
12};
13use turbopack_core::{
14 chunk::{AsyncModuleInfo, ChunkingContext, ChunkingType},
15 reference::{ModuleReference, ModuleReferences},
16 resolve::ExternalType,
17};
18
19use crate::{
20 ScopeHoistingContext,
21 code_gen::{CodeGeneration, CodeGenerationHoistedStmt},
22 references::esm::base::ReferencedAsset,
23 utils::AstSyntaxContext,
24};
25
26#[derive(PartialEq, Eq, Default, Debug, Clone, TraceRawVcs, NonLocalValue, Encode, Decode)]
29pub struct AsyncModuleOptions {
30 pub has_top_level_await: bool,
31}
32
33#[turbo_tasks::value(transparent)]
35pub struct OptionAsyncModuleOptions(Option<AsyncModuleOptions>);
36
37#[turbo_tasks::value_impl]
38impl OptionAsyncModuleOptions {
39 #[turbo_tasks::function]
40 pub(crate) fn none() -> Vc<Self> {
41 Vc::cell(None)
42 }
43}
44
45#[turbo_tasks::value(shared)]
51pub struct AsyncModule {
52 pub has_top_level_await: bool,
53 pub import_externals: bool,
54}
55
56#[turbo_tasks::value(transparent)]
58pub struct OptionAsyncModule(Option<ResolvedVc<AsyncModule>>);
59
60#[turbo_tasks::value_impl]
61impl OptionAsyncModule {
62 #[turbo_tasks::function]
64 pub fn none() -> Vc<Self> {
65 Vc::cell(None)
66 }
67
68 #[turbo_tasks::function]
69 pub fn module_options(
70 &self,
71 async_module_info: Option<Vc<AsyncModuleInfo>>,
72 ) -> Vc<OptionAsyncModuleOptions> {
73 if let Some(async_module) = &self.0 {
74 return async_module.module_options(async_module_info);
75 }
76
77 OptionAsyncModuleOptions::none()
78 }
79}
80
81#[turbo_tasks::value(transparent)]
84struct AsyncModuleIdents(
85 #[bincode(with = "turbo_bincode::indexset")] FxIndexSet<(String, AstSyntaxContext)>,
86);
87
88async fn get_inherit_async_referenced_asset(
89 r: ResolvedVc<Box<dyn ModuleReference>>,
90) -> Result<Option<ReadRef<ReferencedAsset>>> {
91 let trait_ref = r.into_trait_ref().await?;
92 let Some(ty) = &trait_ref.chunking_type() else {
93 return Ok(None);
94 };
95 if !matches!(
96 ty,
97 ChunkingType::Parallel {
98 inherit_async: true,
99 ..
100 }
101 ) {
102 return Ok(None);
103 };
104 let referenced_asset: turbo_tasks::ReadRef<ReferencedAsset> =
105 ReferencedAsset::from_resolve_result(r.resolve_reference()).await?;
106 Ok(Some(referenced_asset))
107}
108
109#[turbo_tasks::value_impl]
110impl AsyncModule {
111 #[turbo_tasks::function]
112 async fn get_async_idents(
113 &self,
114 async_module_info: Vc<AsyncModuleInfo>,
115 references: Vc<ModuleReferences>,
116 chunking_context: Vc<Box<dyn ChunkingContext>>,
117 ) -> Result<Vc<AsyncModuleIdents>> {
118 let async_module_info = async_module_info.await?;
119
120 let reference_idents = references
121 .await?
122 .iter()
123 .map(|r| async {
124 let Some(referenced_asset) = get_inherit_async_referenced_asset(*r).await? else {
125 return Ok(None);
126 };
127 Ok(match &*referenced_asset {
128 ReferencedAsset::External(_, ExternalType::EcmaScriptModule) => {
129 if self.import_externals {
130 referenced_asset
131 .get_ident(chunking_context, None, ScopeHoistingContext::None)
132 .await?
133 .map(|i| i.into_module_namespace_ident().unwrap())
134 .map(|(i, ctx)| (i, ctx.unwrap_or_default().into()))
135 } else {
136 None
137 }
138 }
139 ReferencedAsset::Some(placeable) => {
140 if async_module_info
141 .referenced_async_modules
142 .contains(&ResolvedVc::upcast(*placeable))
143 {
144 referenced_asset
145 .get_ident(chunking_context, None, ScopeHoistingContext::None)
146 .await?
147 .map(|i| i.into_module_namespace_ident().unwrap())
148 .map(|(i, ctx)| (i, ctx.unwrap_or_default().into()))
149 } else {
150 None
151 }
152 }
153 ReferencedAsset::External(..) => None,
154 ReferencedAsset::None | ReferencedAsset::Unresolvable => None,
155 })
156 })
157 .try_flat_join()
158 .await?;
159
160 Ok(Vc::cell(FxIndexSet::from_iter(reference_idents)))
161 }
162
163 #[turbo_tasks::function]
164 pub(crate) async fn is_self_async(&self, references: Vc<ModuleReferences>) -> Result<Vc<bool>> {
165 if self.has_top_level_await {
166 return Ok(Vc::cell(true));
167 }
168
169 Ok(Vc::cell(
170 self.import_externals
171 && references
172 .await?
173 .iter()
174 .map(|r| async {
175 let Some(referenced_asset) = get_inherit_async_referenced_asset(*r).await?
176 else {
177 return Ok(false);
178 };
179 Ok(matches!(
180 &*referenced_asset,
181 ReferencedAsset::External(_, ExternalType::EcmaScriptModule)
182 ))
183 })
184 .try_join()
185 .await?
186 .iter()
187 .any(|&b| b),
188 ))
189 }
190
191 #[turbo_tasks::function]
193 pub fn module_options(
194 &self,
195 async_module_info: Option<Vc<AsyncModuleInfo>>,
196 ) -> Vc<OptionAsyncModuleOptions> {
197 if async_module_info.is_none() {
198 return Vc::cell(None);
199 }
200
201 Vc::cell(Some(AsyncModuleOptions {
202 has_top_level_await: self.has_top_level_await,
203 }))
204 }
205}
206
207impl AsyncModule {
208 pub async fn code_generation(
209 self: Vc<Self>,
210 async_module_info: Option<Vc<AsyncModuleInfo>>,
211 references: Vc<ModuleReferences>,
212 chunking_context: Vc<Box<dyn ChunkingContext>>,
213 ) -> Result<CodeGeneration> {
214 if let Some(async_module_info) = async_module_info {
215 let async_idents = self
216 .get_async_idents(async_module_info, references, chunking_context)
217 .await?;
218
219 if !async_idents.is_empty() {
220 let idents = async_idents
221 .iter()
222 .map(|(ident, ctxt)| Ident::new(ident.clone().into(), DUMMY_SP, **ctxt))
223 .collect::<Vec<_>>();
224
225 return Ok(CodeGeneration::hoisted_stmts([
226 CodeGenerationHoistedStmt::new(rcstr!("__turbopack_async_dependencies__"),
227 quote!(
228 "var __turbopack_async_dependencies__ = __turbopack_handle_async_dependencies__($deps);"
229 as Stmt,
230 deps: Expr = Expr::Array(ArrayLit {
231 span: DUMMY_SP,
232 elems: idents
233 .iter()
234 .map(|ident| { Some(Expr::Ident(ident.clone()).into()) })
235 .collect(),
236 })
237 )
238 ),
239 CodeGenerationHoistedStmt::new(rcstr!("__turbopack_async_dependencies__ await"),
240 quote!(
241 "($deps = __turbopack_async_dependencies__.then ? (await \
242 __turbopack_async_dependencies__)() : __turbopack_async_dependencies__);" as Stmt,
243 deps: AssignTarget = ArrayPat {
244 span: DUMMY_SP,
245 elems: idents
246 .into_iter()
247 .map(|ident| { Some(ident.into()) })
248 .collect(),
249 optional: false,
250 type_ann: None,
251 }.into(),
252 )),
253 ].to_vec()));
254 }
255 }
256
257 Ok(CodeGeneration::empty())
258 }
259}