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, OptionModule},
14 module_graph::ModuleGraph,
15 output::OutputAssets,
16 reference::{ModuleReferences, SingleChunkableModuleReference},
17 reference_type::ReferenceType,
18 resolve::{ExportUsage, origin::ResolveOrigin, parse::Request},
19 source::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 references(self: Vc<Self>) -> Vc<ModuleReferences> {
135 self.loader().references()
136 }
137
138 #[turbo_tasks::function]
139 fn is_self_async(self: Vc<Self>) -> Vc<bool> {
140 Vc::cell(true)
141 }
142}
143
144#[turbo_tasks::value_impl]
145impl Asset for WebAssemblyModuleAsset {
146 #[turbo_tasks::function]
147 fn content(&self) -> Vc<AssetContent> {
148 self.source.content()
149 }
150}
151
152#[turbo_tasks::value_impl]
153impl ChunkableModule for WebAssemblyModuleAsset {
154 #[turbo_tasks::function]
155 fn as_chunk_item(
156 self: ResolvedVc<Self>,
157 module_graph: ResolvedVc<ModuleGraph>,
158 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
159 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
160 Vc::upcast(
161 ModuleChunkItem {
162 module: self,
163 module_graph,
164 chunking_context,
165 }
166 .cell(),
167 )
168 }
169}
170
171#[turbo_tasks::value_impl]
172impl EcmascriptChunkPlaceable for WebAssemblyModuleAsset {
173 #[turbo_tasks::function]
174 fn get_exports(self: Vc<Self>) -> Vc<EcmascriptExports> {
175 self.loader().get_exports()
176 }
177
178 #[turbo_tasks::function]
179 fn get_async_module(self: Vc<Self>) -> Vc<OptionAsyncModule> {
180 self.loader().get_async_module()
181 }
182}
183
184#[turbo_tasks::value_impl]
185impl ResolveOrigin for WebAssemblyModuleAsset {
186 #[turbo_tasks::function]
187 fn origin_path(&self) -> Vc<FileSystemPath> {
188 self.source.ident().path()
189 }
190
191 #[turbo_tasks::function]
192 fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
193 *self.asset_context
194 }
195
196 #[turbo_tasks::function]
197 fn get_inner_asset(self: Vc<Self>, request: Vc<Request>) -> Vc<OptionModule> {
198 self.loader_as_resolve_origin().get_inner_asset(request)
199 }
200}
201
202#[turbo_tasks::value]
203struct ModuleChunkItem {
204 module: ResolvedVc<WebAssemblyModuleAsset>,
205 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
206 module_graph: ResolvedVc<ModuleGraph>,
207}
208
209#[turbo_tasks::value_impl]
210impl ChunkItem for ModuleChunkItem {
211 #[turbo_tasks::function]
212 fn asset_ident(&self) -> Vc<AssetIdent> {
213 self.module.ident()
214 }
215
216 #[turbo_tasks::function]
217 async fn references(&self) -> Result<Vc<OutputAssets>> {
218 let loader_references = self.module.loader().references().await?;
219 references_to_output_assets(&*loader_references).await
220 }
221
222 #[turbo_tasks::function]
223 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
224 Vc::upcast(*self.chunking_context)
225 }
226
227 #[turbo_tasks::function]
228 async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
229 Ok(Vc::upcast(
230 Vc::<EcmascriptChunkType>::default().resolve().await?,
231 ))
232 }
233
234 #[turbo_tasks::function]
235 fn module(&self) -> Vc<Box<dyn Module>> {
236 Vc::upcast(*self.module)
237 }
238}
239
240#[turbo_tasks::value_impl]
241impl EcmascriptChunkItem for ModuleChunkItem {
242 #[turbo_tasks::function]
243 fn content(self: Vc<Self>) -> Vc<EcmascriptChunkItemContent> {
244 panic!("content() should not be called");
245 }
246
247 #[turbo_tasks::function]
248 async fn content_with_async_module_info(
249 &self,
250 async_module_info: Option<Vc<AsyncModuleInfo>>,
251 ) -> Result<Vc<EcmascriptChunkItemContent>> {
252 let loader_asset = self.module.loader();
253 let item =
254 loader_asset.as_chunk_item(*self.module_graph, Vc::upcast(*self.chunking_context));
255
256 let ecmascript_item = Vc::try_resolve_downcast::<Box<dyn EcmascriptChunkItem>>(item)
257 .await?
258 .context("EcmascriptModuleAsset must implement EcmascriptChunkItem")?;
259
260 let mut chunk_item_content = ecmascript_item
261 .content_with_async_module_info(async_module_info)
262 .owned()
263 .await?;
264
265 chunk_item_content.options.wasm = true;
266
267 Ok(chunk_item_content.cell())
268 }
269}