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 Some(ty) = &*r.chunking_type().await? else {
92 return Ok(None);
93 };
94 if !matches!(
95 ty,
96 ChunkingType::Parallel {
97 inherit_async: true,
98 ..
99 }
100 ) {
101 return Ok(None);
102 };
103 let referenced_asset: turbo_tasks::ReadRef<ReferencedAsset> =
104 ReferencedAsset::from_resolve_result(r.resolve_reference()).await?;
105 Ok(Some(referenced_asset))
106}
107
108#[turbo_tasks::value_impl]
109impl AsyncModule {
110 #[turbo_tasks::function]
111 async fn get_async_idents(
112 &self,
113 async_module_info: Vc<AsyncModuleInfo>,
114 references: Vc<ModuleReferences>,
115 chunking_context: Vc<Box<dyn ChunkingContext>>,
116 ) -> Result<Vc<AsyncModuleIdents>> {
117 let async_module_info = async_module_info.await?;
118
119 let reference_idents = references
120 .await?
121 .iter()
122 .map(|r| async {
123 let Some(referenced_asset) = get_inherit_async_referenced_asset(*r).await? else {
124 return Ok(None);
125 };
126 Ok(match &*referenced_asset {
127 ReferencedAsset::External(_, ExternalType::EcmaScriptModule) => {
128 if self.import_externals {
129 referenced_asset
130 .get_ident(chunking_context, None, ScopeHoistingContext::None)
131 .await?
132 .map(|i| i.into_module_namespace_ident().unwrap())
133 .map(|(i, ctx)| (i, ctx.unwrap_or_default().into()))
134 } else {
135 None
136 }
137 }
138 ReferencedAsset::Some(placeable) => {
139 if async_module_info
140 .referenced_async_modules
141 .contains(&ResolvedVc::upcast(*placeable))
142 {
143 referenced_asset
144 .get_ident(chunking_context, None, ScopeHoistingContext::None)
145 .await?
146 .map(|i| i.into_module_namespace_ident().unwrap())
147 .map(|(i, ctx)| (i, ctx.unwrap_or_default().into()))
148 } else {
149 None
150 }
151 }
152 ReferencedAsset::External(..) => None,
153 ReferencedAsset::None | ReferencedAsset::Unresolvable => None,
154 })
155 })
156 .try_flat_join()
157 .await?;
158
159 Ok(Vc::cell(FxIndexSet::from_iter(reference_idents)))
160 }
161
162 #[turbo_tasks::function]
163 pub(crate) async fn is_self_async(&self, references: Vc<ModuleReferences>) -> Result<Vc<bool>> {
164 if self.has_top_level_await {
165 return Ok(Vc::cell(true));
166 }
167
168 Ok(Vc::cell(
169 self.import_externals
170 && references
171 .await?
172 .iter()
173 .map(|r| async {
174 let Some(referenced_asset) = get_inherit_async_referenced_asset(*r).await?
175 else {
176 return Ok(false);
177 };
178 Ok(matches!(
179 &*referenced_asset,
180 ReferencedAsset::External(_, ExternalType::EcmaScriptModule)
181 ))
182 })
183 .try_join()
184 .await?
185 .iter()
186 .any(|&b| b),
187 ))
188 }
189
190 #[turbo_tasks::function]
192 pub fn module_options(
193 &self,
194 async_module_info: Option<Vc<AsyncModuleInfo>>,
195 ) -> Vc<OptionAsyncModuleOptions> {
196 if async_module_info.is_none() {
197 return Vc::cell(None);
198 }
199
200 Vc::cell(Some(AsyncModuleOptions {
201 has_top_level_await: self.has_top_level_await,
202 }))
203 }
204}
205
206impl AsyncModule {
207 pub async fn code_generation(
208 self: Vc<Self>,
209 async_module_info: Option<Vc<AsyncModuleInfo>>,
210 references: Vc<ModuleReferences>,
211 chunking_context: Vc<Box<dyn ChunkingContext>>,
212 ) -> Result<CodeGeneration> {
213 if let Some(async_module_info) = async_module_info {
214 let async_idents = self
215 .get_async_idents(async_module_info, references, chunking_context)
216 .await?;
217
218 if !async_idents.is_empty() {
219 let idents = async_idents
220 .iter()
221 .map(|(ident, ctxt)| Ident::new(ident.clone().into(), DUMMY_SP, **ctxt))
222 .collect::<Vec<_>>();
223
224 return Ok(CodeGeneration::hoisted_stmts([
225 CodeGenerationHoistedStmt::new(rcstr!("__turbopack_async_dependencies__"),
226 quote!(
227 "var __turbopack_async_dependencies__ = __turbopack_handle_async_dependencies__($deps);"
228 as Stmt,
229 deps: Expr = Expr::Array(ArrayLit {
230 span: DUMMY_SP,
231 elems: idents
232 .iter()
233 .map(|ident| { Some(Expr::Ident(ident.clone()).into()) })
234 .collect(),
235 })
236 )
237 ),
238 CodeGenerationHoistedStmt::new(rcstr!("__turbopack_async_dependencies__ await"),
239 quote!(
240 "($deps = __turbopack_async_dependencies__.then ? (await \
241 __turbopack_async_dependencies__)() : __turbopack_async_dependencies__);" as Stmt,
242 deps: AssignTarget = ArrayPat {
243 span: DUMMY_SP,
244 elems: idents
245 .into_iter()
246 .map(|ident| { Some(ident.into()) })
247 .collect(),
248 optional: false,
249 type_ann: None,
250 }.into(),
251 )),
252 ].to_vec()));
253 }
254 }
255
256 Ok(CodeGeneration::empty())
257 }
258}