Skip to main content

turbopack_wasm/
module_asset.rs

1use anyhow::{Result, bail};
2use turbo_rcstr::rcstr;
3use turbo_tasks::{ResolvedVc, Vc, fxindexmap};
4use turbo_tasks_fs::FileSystemPath;
5use turbopack_core::{
6    chunk::{AsyncModuleInfo, ChunkableModule, ChunkingContext},
7    context::AssetContext,
8    ident::AssetIdent,
9    module::{Module, ModuleSideEffects},
10    module_graph::ModuleGraph,
11    output::OutputAssetsWithReferenced,
12    reference::{ModuleReferences, SingleChunkableModuleReference},
13    reference_type::ReferenceType,
14    resolve::{ExportUsage, origin::ResolveOrigin},
15    source::{OptionSource, Source},
16};
17use turbopack_ecmascript::{
18    chunk::{
19        EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
20        ecmascript_chunk_item,
21    },
22    references::async_module::OptionAsyncModule,
23};
24
25use crate::{
26    loader::{compiling_loader_source, instantiating_loader_source},
27    output_asset::WebAssemblyAsset,
28    raw::RawWebAssemblyModuleAsset,
29    source::WebAssemblySource,
30};
31
32/// Creates a javascript loader which instantiates the WebAssembly source and
33/// re-exports its exports.
34#[turbo_tasks::value]
35#[derive(Clone)]
36pub struct WebAssemblyModuleAsset {
37    source: ResolvedVc<WebAssemblySource>,
38    asset_context: ResolvedVc<Box<dyn AssetContext>>,
39}
40
41#[turbo_tasks::value_impl]
42impl WebAssemblyModuleAsset {
43    #[turbo_tasks::function]
44    pub fn new(
45        source: ResolvedVc<WebAssemblySource>,
46        asset_context: ResolvedVc<Box<dyn AssetContext>>,
47    ) -> Vc<Self> {
48        Self::cell(WebAssemblyModuleAsset {
49            source,
50            asset_context,
51        })
52    }
53
54    #[turbo_tasks::function]
55    fn wasm_asset(&self, chunking_context: Vc<Box<dyn ChunkingContext>>) -> Vc<WebAssemblyAsset> {
56        WebAssemblyAsset::new(*self.source, chunking_context)
57    }
58
59    #[turbo_tasks::function]
60    async fn loader_as_module(&self) -> Result<Vc<Box<dyn Module>>> {
61        let query = &self.source.ident().await?.query;
62
63        let loader_source = if query == "?module" {
64            compiling_loader_source(*self.source)
65        } else {
66            instantiating_loader_source(*self.source)
67        };
68
69        let module = self.asset_context.process(
70            loader_source,
71            ReferenceType::Internal(ResolvedVc::cell(fxindexmap! {
72                rcstr!("WASM_PATH") => ResolvedVc::upcast(RawWebAssemblyModuleAsset::new(*self.source, *self.asset_context).to_resolved().await?),
73            })),
74        ).module();
75
76        Ok(module)
77    }
78    #[turbo_tasks::function]
79    async fn loader_as_resolve_origin(self: Vc<Self>) -> Result<Vc<Box<dyn ResolveOrigin>>> {
80        let module = self.loader_as_module();
81
82        let Some(esm_asset) =
83            ResolvedVc::try_sidecast::<Box<dyn ResolveOrigin>>(module.to_resolved().await?)
84        else {
85            bail!("WASM loader was not processed into an EcmascriptModuleAsset");
86        };
87
88        Ok(*esm_asset)
89    }
90
91    #[turbo_tasks::function]
92    async fn loader(self: Vc<Self>) -> Result<Vc<Box<dyn EcmascriptChunkPlaceable>>> {
93        let module = self.loader_as_module();
94
95        let Some(esm_asset) = ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(
96            module.to_resolved().await?,
97        ) else {
98            bail!("WASM loader was not processed into an EcmascriptModuleAsset");
99        };
100
101        Ok(*esm_asset)
102    }
103
104    #[turbo_tasks::function]
105    async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
106        Ok(Vc::cell(vec![ResolvedVc::upcast(
107            SingleChunkableModuleReference::new(
108                Vc::upcast(self.loader()),
109                rcstr!("wasm loader"),
110                ExportUsage::all(),
111            )
112            .to_resolved()
113            .await?,
114        )]))
115    }
116}
117
118#[turbo_tasks::value_impl]
119impl Module for WebAssemblyModuleAsset {
120    #[turbo_tasks::function]
121    async fn ident(&self) -> Result<Vc<AssetIdent>> {
122        Ok(self
123            .source
124            .ident()
125            .with_modifier(rcstr!("wasm module"))
126            .with_layer(self.asset_context.into_trait_ref().await?.layer()))
127    }
128
129    #[turbo_tasks::function]
130    fn source(&self) -> Vc<OptionSource> {
131        Vc::cell(Some(ResolvedVc::upcast(self.source)))
132    }
133
134    #[turbo_tasks::function]
135    fn references(self: Vc<Self>) -> Vc<ModuleReferences> {
136        self.loader().references()
137    }
138
139    #[turbo_tasks::function]
140    fn is_self_async(self: Vc<Self>) -> Vc<bool> {
141        Vc::cell(true)
142    }
143
144    #[turbo_tasks::function]
145    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
146        // Both versions of this module have a top level await that instantiates a wasm module
147        // wasm module instantiation can trigger arbitrary side effects from the native start
148        // function
149        ModuleSideEffects::SideEffectful.cell()
150    }
151}
152
153#[turbo_tasks::value_impl]
154impl ChunkableModule for WebAssemblyModuleAsset {
155    #[turbo_tasks::function]
156    fn as_chunk_item(
157        self: ResolvedVc<Self>,
158        module_graph: ResolvedVc<ModuleGraph>,
159        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
160    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
161        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
162    }
163}
164
165#[turbo_tasks::value_impl]
166impl EcmascriptChunkPlaceable for WebAssemblyModuleAsset {
167    #[turbo_tasks::function]
168    fn get_exports(self: Vc<Self>) -> Vc<EcmascriptExports> {
169        self.loader().get_exports()
170    }
171
172    #[turbo_tasks::function]
173    fn get_async_module(self: Vc<Self>) -> Vc<OptionAsyncModule> {
174        self.loader().get_async_module()
175    }
176
177    #[turbo_tasks::function]
178    async fn chunk_item_content(
179        self: Vc<Self>,
180        chunking_context: Vc<Box<dyn ChunkingContext>>,
181        module_graph: Vc<ModuleGraph>,
182        async_module_info: Option<Vc<AsyncModuleInfo>>,
183        estimated: bool,
184    ) -> Result<Vc<EcmascriptChunkItemContent>> {
185        // Delegate to the loader's chunk item content
186        Ok(self.loader().chunk_item_content(
187            chunking_context,
188            module_graph,
189            async_module_info,
190            estimated,
191        ))
192    }
193
194    #[turbo_tasks::function]
195    async fn chunk_item_output_assets(
196        self: Vc<Self>,
197        chunking_context: Vc<Box<dyn ChunkingContext>>,
198        _module_graph: Vc<ModuleGraph>,
199    ) -> Result<Vc<OutputAssetsWithReferenced>> {
200        let wasm_asset = self.wasm_asset(chunking_context).to_resolved().await?;
201        Ok(OutputAssetsWithReferenced::from_assets(Vc::cell(vec![
202            ResolvedVc::upcast(wasm_asset),
203        ])))
204    }
205}
206
207#[turbo_tasks::value_impl]
208impl ResolveOrigin for WebAssemblyModuleAsset {
209    #[turbo_tasks::function]
210    fn origin_path(&self) -> Vc<FileSystemPath> {
211        self.source.ident().path()
212    }
213
214    #[turbo_tasks::function]
215    fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
216        *self.asset_context
217    }
218}