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::{FxIndexSet, NonLocalValue, ResolvedVc, TryFlatJoinIterExt, TryJoinIterExt, Vc};
10use turbopack_core::{
11 chunk::{AsyncModuleInfo, ChunkingContext, ChunkingType},
12 reference::{ModuleReference, ModuleReferences},
13 resolve::ExternalType,
14};
15
16use crate::{
17 ScopeHoistingContext,
18 code_gen::{CodeGeneration, CodeGenerationHoistedStmt},
19 references::esm::base::ReferencedAsset,
20 utils::AstSyntaxContext,
21};
22
23#[derive(PartialEq, Eq, Default, Debug, Clone, NonLocalValue, Encode, Decode)]
26pub struct AsyncModuleOptions {
27 pub has_top_level_await: bool,
28}
29
30#[turbo_tasks::value(transparent)]
32pub struct OptionAsyncModuleOptions(Option<AsyncModuleOptions>);
33
34#[turbo_tasks::value_impl]
35impl OptionAsyncModuleOptions {
36 #[turbo_tasks::function]
37 pub(crate) fn none() -> Vc<Self> {
38 Vc::cell(None)
39 }
40}
41
42#[turbo_tasks::value(shared)]
48pub struct AsyncModule {
49 pub has_top_level_await: bool,
50 pub import_externals: bool,
51}
52
53#[turbo_tasks::value(transparent)]
55pub struct OptionAsyncModule(Option<ResolvedVc<AsyncModule>>);
56
57#[turbo_tasks::value_impl]
58impl OptionAsyncModule {
59 #[turbo_tasks::function]
61 pub fn none() -> Vc<Self> {
62 Vc::cell(None)
63 }
64
65 #[turbo_tasks::function]
66 pub fn module_options(
67 &self,
68 async_module_info: Option<Vc<AsyncModuleInfo>>,
69 ) -> Vc<OptionAsyncModuleOptions> {
70 if let Some(async_module) = &self.0 {
71 return async_module.module_options(async_module_info);
72 }
73
74 OptionAsyncModuleOptions::none()
75 }
76}
77
78#[turbo_tasks::value(transparent)]
81struct AsyncModuleIdents(
82 #[bincode(with = "turbo_bincode::indexset")] FxIndexSet<(String, AstSyntaxContext)>,
83);
84
85async fn get_inherit_async_referenced_asset(
86 r: ResolvedVc<Box<dyn ModuleReference>>,
87) -> Result<Option<ReferencedAsset>> {
88 let trait_ref = r.into_trait_ref().await?;
89 let Some(ty) = &trait_ref.chunking_type() else {
90 return Ok(None);
91 };
92 if !matches!(
93 ty,
94 ChunkingType::Parallel {
95 inherit_async: true,
96 ..
97 }
98 ) {
99 return Ok(None);
100 };
101 let referenced_asset: ReferencedAsset =
102 ReferencedAsset::from_resolve_result(r.resolve_reference()).await?;
103 Ok(Some(referenced_asset))
104}
105
106#[turbo_tasks::value_impl]
107impl AsyncModule {
108 #[turbo_tasks::function]
109 async fn get_async_idents(
110 &self,
111 async_module_info: Vc<AsyncModuleInfo>,
112 references: Vc<ModuleReferences>,
113 chunking_context: Vc<Box<dyn ChunkingContext>>,
114 ) -> Result<Vc<AsyncModuleIdents>> {
115 let async_module_info = async_module_info.await?;
116
117 let reference_idents = references
118 .await?
119 .iter()
120 .map(async |r| {
121 let Some(referenced_asset) = get_inherit_async_referenced_asset(*r).await? else {
122 return Ok(None);
123 };
124 Ok(match &referenced_asset {
125 ReferencedAsset::External(_, ExternalType::EcmaScriptModule) => {
126 if self.import_externals {
127 referenced_asset
128 .get_ident(chunking_context, None, ScopeHoistingContext::None)
129 .await?
130 .map(|i| i.into_module_namespace_ident().unwrap())
131 .map(|(i, ctx)| (i, ctx.unwrap_or_default().into()))
132 } else {
133 None
134 }
135 }
136 ReferencedAsset::Some(placeable) => {
137 if async_module_info
138 .referenced_async_modules
139 .contains(&ResolvedVc::upcast(*placeable))
140 {
141 referenced_asset
142 .get_ident(chunking_context, None, ScopeHoistingContext::None)
143 .await?
144 .map(|i| i.into_module_namespace_ident().unwrap())
145 .map(|(i, ctx)| (i, ctx.unwrap_or_default().into()))
146 } else {
147 None
148 }
149 }
150 ReferencedAsset::External(..) => None,
151 ReferencedAsset::NonPlaceable(_)
152 | ReferencedAsset::None
153 | ReferencedAsset::Empty
154 | 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(async |r| {
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}