turbopack_wasm/
module_asset.rs1use anyhow::{Result, bail};
2use turbo_rcstr::rcstr;
3use turbo_tasks::{ResolvedVc, Vc, fxindexmap};
4use turbo_tasks_fs::{FileSystem, FileSystemPath};
5use turbopack_core::{
6 chunk::{AsyncModuleInfo, ChunkableModule, ChunkingContext},
7 context::AssetContext,
8 environment::ChunkLoading,
9 file_source::FileSource,
10 ident::AssetIdent,
11 module::{Module, ModuleSideEffects},
12 module_graph::ModuleGraph,
13 output::OutputAssetsWithReferenced,
14 reference::{ModuleReferences, SingleChunkableModuleReference},
15 reference_type::ReferenceType,
16 resolve::{ExportUsage, origin::ResolveOrigin},
17 source::{OptionSource, Source},
18};
19use turbopack_ecmascript::{
20 chunk::{
21 EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
22 ecmascript_chunk_item,
23 },
24 references::async_module::OptionAsyncModule,
25};
26
27use crate::{
28 embed,
29 loader::{compiling_loader_source, instantiating_loader_source},
30 output_asset::WebAssemblyAsset,
31 raw::RawWebAssemblyModuleAsset,
32 source::WebAssemblySource,
33};
34
35#[turbo_tasks::value]
38#[derive(Clone)]
39pub struct WebAssemblyModuleAsset {
40 source: ResolvedVc<WebAssemblySource>,
41 asset_context: ResolvedVc<Box<dyn AssetContext>>,
42 origin_path: FileSystemPath,
44}
45
46#[turbo_tasks::value_impl]
47impl WebAssemblyModuleAsset {
48 #[turbo_tasks::function]
49 pub async fn new(
50 source: ResolvedVc<WebAssemblySource>,
51 asset_context: ResolvedVc<Box<dyn AssetContext>>,
52 ) -> Result<Vc<Self>> {
53 Ok(Self::cell(WebAssemblyModuleAsset {
54 origin_path: source.ident().await?.path.clone(),
55 source,
56 asset_context,
57 }))
58 }
59
60 #[turbo_tasks::function]
61 fn wasm_asset(&self, chunking_context: Vc<Box<dyn ChunkingContext>>) -> Vc<WebAssemblyAsset> {
62 WebAssemblyAsset::new(*self.source, chunking_context)
63 }
64
65 #[turbo_tasks::function]
66 async fn loader_as_module(&self) -> Result<Vc<Box<dyn Module>>> {
67 let query = &self.source.ident().await?.query;
68
69 let chunk_loading = self
70 .asset_context
71 .compile_time_info()
72 .environment()
73 .chunk_loading()
74 .await?;
75 let is_edge = matches!(*chunk_loading, ChunkLoading::Edge);
76
77 let loader_source = if query == "?module" {
78 compiling_loader_source(*self.source, is_edge)
79 } else {
80 instantiating_loader_source(*self.source, is_edge)
81 };
82
83 let helper_path = match *chunk_loading {
84 ChunkLoading::Edge => rcstr!("edge/loadWasm.ts"),
85 ChunkLoading::NodeJs => rcstr!("node/loadWasm.ts"),
86 ChunkLoading::Dom => rcstr!("browser/loadWasm.ts"),
87 };
88
89 let helper = self
90 .asset_context
91 .process(
92 Vc::upcast(FileSource::new(
93 embed::embed_fs().root().await?.join(&helper_path)?,
94 )),
95 ReferenceType::Runtime,
101 )
102 .module()
103 .to_resolved()
104 .await?;
105
106 let module = self.asset_context.process(
107 loader_source,
108 ReferenceType::Internal(ResolvedVc::cell(fxindexmap! {
109 rcstr!("WASM_PATH") => ResolvedVc::upcast(RawWebAssemblyModuleAsset::new(*self.source, *self.asset_context).to_resolved().await?),
110 rcstr!("WASM_HELPER") => helper,
111 })),
112 ).module();
113
114 Ok(module)
115 }
116 #[turbo_tasks::function]
117 async fn loader_as_resolve_origin(self: Vc<Self>) -> Result<Vc<Box<dyn ResolveOrigin>>> {
118 let module = self.loader_as_module();
119
120 let Some(esm_asset) =
121 ResolvedVc::try_sidecast::<Box<dyn ResolveOrigin>>(module.to_resolved().await?)
122 else {
123 bail!("WASM loader was not processed into an EcmascriptModuleAsset");
124 };
125
126 Ok(*esm_asset)
127 }
128
129 #[turbo_tasks::function]
130 async fn loader(self: Vc<Self>) -> Result<Vc<Box<dyn EcmascriptChunkPlaceable>>> {
131 let module = self.loader_as_module();
132
133 let Some(esm_asset) = ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(
134 module.to_resolved().await?,
135 ) else {
136 bail!("WASM loader was not processed into an EcmascriptModuleAsset");
137 };
138
139 Ok(*esm_asset)
140 }
141
142 #[turbo_tasks::function]
143 async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
144 Ok(Vc::cell(vec![ResolvedVc::upcast(
145 SingleChunkableModuleReference::new(
146 Vc::upcast(self.loader()),
147 rcstr!("wasm loader"),
148 ExportUsage::all(),
149 )
150 .to_resolved()
151 .await?,
152 )]))
153 }
154}
155
156#[turbo_tasks::value_impl]
157impl Module for WebAssemblyModuleAsset {
158 #[turbo_tasks::function]
159 async fn ident(&self) -> Result<Vc<AssetIdent>> {
160 Ok(self
161 .source
162 .ident()
163 .owned()
164 .await?
165 .with_modifier(rcstr!("wasm module"))
166 .with_layer(self.asset_context.into_trait_ref().await?.layer())
167 .into_vc())
168 }
169
170 #[turbo_tasks::function]
171 fn source(&self) -> Vc<OptionSource> {
172 Vc::cell(Some(ResolvedVc::upcast(self.source)))
173 }
174
175 #[turbo_tasks::function]
176 fn references(self: Vc<Self>) -> Vc<ModuleReferences> {
177 self.loader().references()
178 }
179
180 #[turbo_tasks::function]
181 fn is_self_async(self: Vc<Self>) -> Vc<bool> {
182 Vc::cell(true)
183 }
184
185 #[turbo_tasks::function]
186 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
187 ModuleSideEffects::SideEffectful.cell()
191 }
192}
193
194#[turbo_tasks::value_impl]
195impl ChunkableModule for WebAssemblyModuleAsset {
196 #[turbo_tasks::function]
197 fn as_chunk_item(
198 self: ResolvedVc<Self>,
199 module_graph: ResolvedVc<ModuleGraph>,
200 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
201 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
202 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
203 }
204}
205
206#[turbo_tasks::value_impl]
207impl EcmascriptChunkPlaceable for WebAssemblyModuleAsset {
208 #[turbo_tasks::function]
209 fn get_exports(self: Vc<Self>) -> Vc<EcmascriptExports> {
210 self.loader().get_exports()
211 }
212
213 #[turbo_tasks::function]
214 fn get_async_module(self: Vc<Self>) -> Vc<OptionAsyncModule> {
215 self.loader().get_async_module()
216 }
217
218 #[turbo_tasks::function]
219 async fn chunk_item_content(
220 self: Vc<Self>,
221 chunking_context: Vc<Box<dyn ChunkingContext>>,
222 module_graph: Vc<ModuleGraph>,
223 async_module_info: Option<Vc<AsyncModuleInfo>>,
224 estimated: bool,
225 ) -> Result<Vc<EcmascriptChunkItemContent>> {
226 Ok(self.loader().chunk_item_content(
228 chunking_context,
229 module_graph,
230 async_module_info,
231 estimated,
232 ))
233 }
234
235 #[turbo_tasks::function]
236 async fn chunk_item_output_assets(
237 self: Vc<Self>,
238 chunking_context: Vc<Box<dyn ChunkingContext>>,
239 _module_graph: Vc<ModuleGraph>,
240 ) -> Result<Vc<OutputAssetsWithReferenced>> {
241 let wasm_asset = self.wasm_asset(chunking_context).to_resolved().await?;
242 Ok(OutputAssetsWithReferenced::from_assets(Vc::cell(vec![
243 ResolvedVc::upcast(wasm_asset),
244 ])))
245 }
246}
247
248#[turbo_tasks::value_impl]
249impl ResolveOrigin for WebAssemblyModuleAsset {
250 fn origin_path(&self) -> FileSystemPath {
251 self.origin_path.clone()
252 }
253
254 fn asset_context(&self) -> ResolvedVc<Box<dyn AssetContext>> {
255 self.asset_context
256 }
257}