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: ResolvedVc<Box<dyn ModuleReference>>,
90) -> Result<Option<ReadRef<ReferencedAsset>>> {
91 let Some(r) = ResolvedVc::try_downcast::<Box<dyn ChunkableModuleReference>>(r) 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) = get_inherit_async_referenced_asset(*r).await?
178 else {
179 return Ok(false);
180 };
181 Ok(matches!(
182 &*referenced_asset,
183 ReferencedAsset::External(_, ExternalType::EcmaScriptModule)
184 ))
185 })
186 .try_join()
187 .await?
188 .iter()
189 .any(|&b| b),
190 ))
191 }
192
193 #[turbo_tasks::function]
195 pub fn module_options(
196 &self,
197 async_module_info: Option<Vc<AsyncModuleInfo>>,
198 ) -> Vc<OptionAsyncModuleOptions> {
199 if async_module_info.is_none() {
200 return Vc::cell(None);
201 }
202
203 Vc::cell(Some(AsyncModuleOptions {
204 has_top_level_await: self.has_top_level_await,
205 }))
206 }
207}
208
209impl AsyncModule {
210 pub async fn code_generation(
211 self: Vc<Self>,
212 async_module_info: Option<Vc<AsyncModuleInfo>>,
213 references: Vc<ModuleReferences>,
214 chunking_context: Vc<Box<dyn ChunkingContext>>,
215 ) -> Result<CodeGeneration> {
216 if let Some(async_module_info) = async_module_info {
217 let async_idents = self
218 .get_async_idents(async_module_info, references, chunking_context)
219 .await?;
220
221 if !async_idents.is_empty() {
222 let idents = async_idents
223 .iter()
224 .map(|(ident, ctxt)| Ident::new(ident.clone().into(), DUMMY_SP, **ctxt))
225 .collect::<Vec<_>>();
226
227 return Ok(CodeGeneration::hoisted_stmts([
228 CodeGenerationHoistedStmt::new(rcstr!("__turbopack_async_dependencies__"),
229 quote!(
230 "var __turbopack_async_dependencies__ = __turbopack_handle_async_dependencies__($deps);"
231 as Stmt,
232 deps: Expr = Expr::Array(ArrayLit {
233 span: DUMMY_SP,
234 elems: idents
235 .iter()
236 .map(|ident| { Some(Expr::Ident(ident.clone()).into()) })
237 .collect(),
238 })
239 )
240 ),
241 CodeGenerationHoistedStmt::new(rcstr!("__turbopack_async_dependencies__ await"),
242 quote!(
243 "($deps = __turbopack_async_dependencies__.then ? (await \
244 __turbopack_async_dependencies__)() : __turbopack_async_dependencies__);" as Stmt,
245 deps: AssignTarget = ArrayPat {
246 span: DUMMY_SP,
247 elems: idents
248 .into_iter()
249 .map(|ident| { Some(ident.into()) })
250 .collect(),
251 optional: false,
252 type_ann: None,
253 }.into(),
254 )),
255 ].to_vec()));
256 }
257 }
258
259 Ok(CodeGeneration::empty())
260 }
261}