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