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