turbopack_wasm/
module_asset.rs1use anyhow::{Context, Result, bail};
2use turbo_rcstr::rcstr;
3use turbo_tasks::{IntoTraitRef, ResolvedVc, Vc, fxindexmap};
4use turbo_tasks_fs::FileSystemPath;
5use turbopack_core::{
6 asset::{Asset, AssetContent},
7 chunk::{
8 AsyncModuleInfo, ChunkItem, ChunkType, ChunkableModule, ChunkingContext,
9 chunk_group::references_to_output_assets,
10 },
11 context::AssetContext,
12 ident::AssetIdent,
13 module::{Module, ModuleSideEffects, OptionModule},
14 module_graph::ModuleGraph,
15 output::{OutputAssetsReference, OutputAssetsWithReferenced},
16 reference::{ModuleReferences, SingleChunkableModuleReference},
17 reference_type::ReferenceType,
18 resolve::{ExportUsage, origin::ResolveOrigin, parse::Request},
19 source::{OptionSource, Source},
20};
21use turbopack_ecmascript::{
22 chunk::{
23 EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable,
24 EcmascriptChunkType, EcmascriptExports,
25 },
26 references::async_module::OptionAsyncModule,
27};
28
29use crate::{
30 loader::{compiling_loader_source, instantiating_loader_source},
31 output_asset::WebAssemblyAsset,
32 raw::RawWebAssemblyModuleAsset,
33 source::WebAssemblySource,
34};
35
36#[turbo_tasks::value]
39#[derive(Clone)]
40pub struct WebAssemblyModuleAsset {
41 source: ResolvedVc<WebAssemblySource>,
42 asset_context: ResolvedVc<Box<dyn AssetContext>>,
43}
44
45#[turbo_tasks::value_impl]
46impl WebAssemblyModuleAsset {
47 #[turbo_tasks::function]
48 pub fn new(
49 source: ResolvedVc<WebAssemblySource>,
50 asset_context: ResolvedVc<Box<dyn AssetContext>>,
51 ) -> Vc<Self> {
52 Self::cell(WebAssemblyModuleAsset {
53 source,
54 asset_context,
55 })
56 }
57
58 #[turbo_tasks::function]
59 fn wasm_asset(&self, chunking_context: Vc<Box<dyn ChunkingContext>>) -> Vc<WebAssemblyAsset> {
60 WebAssemblyAsset::new(*self.source, chunking_context)
61 }
62
63 #[turbo_tasks::function]
64 async fn loader_as_module(self: Vc<Self>) -> Result<Vc<Box<dyn Module>>> {
65 let this = self.await?;
66 let query = &this.source.ident().await?.query;
67
68 let loader_source = if query == "?module" {
69 compiling_loader_source(*this.source)
70 } else {
71 instantiating_loader_source(*this.source)
72 };
73
74 let module = this.asset_context.process(
75 loader_source,
76 ReferenceType::Internal(ResolvedVc::cell(fxindexmap! {
77 rcstr!("WASM_PATH") => ResolvedVc::upcast(RawWebAssemblyModuleAsset::new(*this.source, *this.asset_context).to_resolved().await?),
78 })),
79 ).module();
80
81 Ok(module)
82 }
83 #[turbo_tasks::function]
84 async fn loader_as_resolve_origin(self: Vc<Self>) -> Result<Vc<Box<dyn ResolveOrigin>>> {
85 let module = self.loader_as_module();
86
87 let Some(esm_asset) = Vc::try_resolve_sidecast::<Box<dyn ResolveOrigin>>(module).await?
88 else {
89 bail!("WASM loader was not processed into an EcmascriptModuleAsset");
90 };
91
92 Ok(esm_asset)
93 }
94
95 #[turbo_tasks::function]
96 async fn loader(self: Vc<Self>) -> Result<Vc<Box<dyn EcmascriptChunkPlaceable>>> {
97 let module = self.loader_as_module();
98
99 let Some(esm_asset) =
100 Vc::try_resolve_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(module).await?
101 else {
102 bail!("WASM loader was not processed into an EcmascriptModuleAsset");
103 };
104
105 Ok(esm_asset)
106 }
107
108 #[turbo_tasks::function]
109 async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
110 Ok(Vc::cell(vec![ResolvedVc::upcast(
111 SingleChunkableModuleReference::new(
112 Vc::upcast(self.loader()),
113 rcstr!("wasm loader"),
114 ExportUsage::all(),
115 )
116 .to_resolved()
117 .await?,
118 )]))
119 }
120}
121
122#[turbo_tasks::value_impl]
123impl Module for WebAssemblyModuleAsset {
124 #[turbo_tasks::function]
125 async fn ident(&self) -> Result<Vc<AssetIdent>> {
126 Ok(self
127 .source
128 .ident()
129 .with_modifier(rcstr!("wasm module"))
130 .with_layer(self.asset_context.into_trait_ref().await?.layer()))
131 }
132
133 #[turbo_tasks::function]
134 fn source(&self) -> Vc<OptionSource> {
135 Vc::cell(Some(ResolvedVc::upcast(self.source)))
136 }
137
138 #[turbo_tasks::function]
139 fn references(self: Vc<Self>) -> Vc<ModuleReferences> {
140 self.loader().references()
141 }
142
143 #[turbo_tasks::function]
144 fn is_self_async(self: Vc<Self>) -> Vc<bool> {
145 Vc::cell(true)
146 }
147
148 #[turbo_tasks::function]
149 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
150 ModuleSideEffects::SideEffectful.cell()
154 }
155}
156
157#[turbo_tasks::value_impl]
158impl Asset for WebAssemblyModuleAsset {
159 #[turbo_tasks::function]
160 fn content(&self) -> Vc<AssetContent> {
161 self.source.content()
162 }
163}
164
165#[turbo_tasks::value_impl]
166impl ChunkableModule for WebAssemblyModuleAsset {
167 #[turbo_tasks::function]
168 fn as_chunk_item(
169 self: ResolvedVc<Self>,
170 module_graph: ResolvedVc<ModuleGraph>,
171 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
172 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
173 Vc::upcast(
174 ModuleChunkItem {
175 module: self,
176 module_graph,
177 chunking_context,
178 }
179 .cell(),
180 )
181 }
182}
183
184#[turbo_tasks::value_impl]
185impl EcmascriptChunkPlaceable for WebAssemblyModuleAsset {
186 #[turbo_tasks::function]
187 fn get_exports(self: Vc<Self>) -> Vc<EcmascriptExports> {
188 self.loader().get_exports()
189 }
190
191 #[turbo_tasks::function]
192 fn get_async_module(self: Vc<Self>) -> Vc<OptionAsyncModule> {
193 self.loader().get_async_module()
194 }
195}
196
197#[turbo_tasks::value_impl]
198impl ResolveOrigin for WebAssemblyModuleAsset {
199 #[turbo_tasks::function]
200 fn origin_path(&self) -> Vc<FileSystemPath> {
201 self.source.ident().path()
202 }
203
204 #[turbo_tasks::function]
205 fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
206 *self.asset_context
207 }
208
209 #[turbo_tasks::function]
210 fn get_inner_asset(self: Vc<Self>, request: Vc<Request>) -> Vc<OptionModule> {
211 self.loader_as_resolve_origin().get_inner_asset(request)
212 }
213}
214
215#[turbo_tasks::value]
216struct ModuleChunkItem {
217 module: ResolvedVc<WebAssemblyModuleAsset>,
218 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
219 module_graph: ResolvedVc<ModuleGraph>,
220}
221
222#[turbo_tasks::value_impl]
223impl OutputAssetsReference for ModuleChunkItem {
224 #[turbo_tasks::function]
225 async fn references(&self) -> Result<Vc<OutputAssetsWithReferenced>> {
226 let loader_references = self.module.loader().references().await?;
227 references_to_output_assets(&*loader_references).await
228 }
229}
230
231#[turbo_tasks::value_impl]
232impl ChunkItem for ModuleChunkItem {
233 #[turbo_tasks::function]
234 fn asset_ident(&self) -> Vc<AssetIdent> {
235 self.module.ident()
236 }
237
238 #[turbo_tasks::function]
239 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
240 *self.chunking_context
241 }
242
243 #[turbo_tasks::function]
244 async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
245 Ok(Vc::upcast(
246 Vc::<EcmascriptChunkType>::default().resolve().await?,
247 ))
248 }
249
250 #[turbo_tasks::function]
251 fn module(&self) -> Vc<Box<dyn Module>> {
252 Vc::upcast(*self.module)
253 }
254}
255
256#[turbo_tasks::value_impl]
257impl EcmascriptChunkItem for ModuleChunkItem {
258 #[turbo_tasks::function]
259 fn content(self: Vc<Self>) -> Vc<EcmascriptChunkItemContent> {
260 panic!("content() should not be called");
261 }
262
263 #[turbo_tasks::function]
264 async fn content_with_async_module_info(
265 &self,
266 async_module_info: Option<Vc<AsyncModuleInfo>>,
267 _estimated: bool,
268 ) -> Result<Vc<EcmascriptChunkItemContent>> {
269 let loader_asset = self.module.loader();
270 let item = loader_asset.as_chunk_item(*self.module_graph, *self.chunking_context);
271
272 let ecmascript_item = Vc::try_resolve_downcast::<Box<dyn EcmascriptChunkItem>>(item)
273 .await?
274 .context("EcmascriptModuleAsset must implement EcmascriptChunkItem")?;
275
276 let chunk_item_content = ecmascript_item
277 .content_with_async_module_info(async_module_info, false)
278 .owned()
279 .await?;
280
281 Ok(chunk_item_content.cell())
282 }
283}