turbopack_wasm/
raw.rs

1use anyhow::{Result, bail};
2use turbo_rcstr::rcstr;
3use turbo_tasks::{IntoTraitRef, ResolvedVc, Vc};
4use turbopack_core::{
5    asset::{Asset, AssetContent},
6    chunk::{ChunkItem, ChunkType, ChunkableModule, ChunkingContext},
7    context::AssetContext,
8    ident::AssetIdent,
9    module::{Module, ModuleSideEffects},
10    module_graph::ModuleGraph,
11    output::{OutputAsset, OutputAssetsReference, OutputAssetsWithReferenced},
12    source::{OptionSource, Source},
13};
14use turbopack_ecmascript::{
15    chunk::{
16        EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable,
17        EcmascriptChunkType, EcmascriptExports,
18    },
19    runtime_functions::TURBOPACK_EXPORT_VALUE,
20    utils::StringifyJs,
21};
22
23use crate::{output_asset::WebAssemblyAsset, source::WebAssemblySource};
24
25/// Exports the relative path to the WebAssembly file without loading it.
26#[turbo_tasks::value]
27#[derive(Clone)]
28pub struct RawWebAssemblyModuleAsset {
29    source: ResolvedVc<WebAssemblySource>,
30    asset_context: ResolvedVc<Box<dyn AssetContext>>,
31}
32
33#[turbo_tasks::value_impl]
34impl RawWebAssemblyModuleAsset {
35    #[turbo_tasks::function]
36    pub fn new(
37        source: ResolvedVc<WebAssemblySource>,
38        asset_context: ResolvedVc<Box<dyn AssetContext>>,
39    ) -> Vc<Self> {
40        Self::cell(RawWebAssemblyModuleAsset {
41            source,
42            asset_context,
43        })
44    }
45
46    #[turbo_tasks::function]
47    fn wasm_asset(&self, chunking_context: Vc<Box<dyn ChunkingContext>>) -> Vc<WebAssemblyAsset> {
48        WebAssemblyAsset::new(*self.source, chunking_context)
49    }
50}
51
52#[turbo_tasks::value_impl]
53impl Module for RawWebAssemblyModuleAsset {
54    #[turbo_tasks::function]
55    async fn ident(&self) -> Result<Vc<AssetIdent>> {
56        Ok(self
57            .source
58            .ident()
59            .with_modifier(rcstr!("wasm raw"))
60            .with_layer(self.asset_context.into_trait_ref().await?.layer()))
61    }
62
63    #[turbo_tasks::function]
64    fn source(&self) -> Vc<OptionSource> {
65        Vc::cell(Some(ResolvedVc::upcast(self.source)))
66    }
67
68    #[turbo_tasks::function]
69    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
70        // this just exports a path
71        ModuleSideEffects::SideEffectFree.cell()
72    }
73}
74
75#[turbo_tasks::value_impl]
76impl Asset for RawWebAssemblyModuleAsset {
77    #[turbo_tasks::function]
78    fn content(&self) -> Vc<AssetContent> {
79        self.source.content()
80    }
81}
82
83#[turbo_tasks::value_impl]
84impl ChunkableModule for RawWebAssemblyModuleAsset {
85    #[turbo_tasks::function]
86    async fn as_chunk_item(
87        self: ResolvedVc<Self>,
88        _module_graph: Vc<ModuleGraph>,
89        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
90    ) -> Result<Vc<Box<dyn turbopack_core::chunk::ChunkItem>>> {
91        Ok(Vc::upcast(
92            RawModuleChunkItem {
93                module: self,
94                chunking_context,
95                wasm_asset: self.wasm_asset(*chunking_context).to_resolved().await?,
96            }
97            .cell(),
98        ))
99    }
100}
101
102#[turbo_tasks::value_impl]
103impl EcmascriptChunkPlaceable for RawWebAssemblyModuleAsset {
104    #[turbo_tasks::function]
105    fn get_exports(self: Vc<Self>) -> Vc<EcmascriptExports> {
106        EcmascriptExports::Value.cell()
107    }
108}
109
110#[turbo_tasks::value]
111struct RawModuleChunkItem {
112    module: ResolvedVc<RawWebAssemblyModuleAsset>,
113    chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
114    wasm_asset: ResolvedVc<WebAssemblyAsset>,
115}
116
117#[turbo_tasks::value_impl]
118impl OutputAssetsReference for RawModuleChunkItem {
119    #[turbo_tasks::function]
120    fn references(&self) -> Vc<OutputAssetsWithReferenced> {
121        OutputAssetsWithReferenced::from_assets(Vc::cell(vec![ResolvedVc::upcast(self.wasm_asset)]))
122    }
123}
124
125#[turbo_tasks::value_impl]
126impl ChunkItem for RawModuleChunkItem {
127    #[turbo_tasks::function]
128    fn asset_ident(&self) -> Vc<AssetIdent> {
129        self.module.ident()
130    }
131
132    #[turbo_tasks::function]
133    fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
134        *self.chunking_context
135    }
136
137    #[turbo_tasks::function]
138    async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
139        Ok(Vc::upcast(
140            Vc::<EcmascriptChunkType>::default().resolve().await?,
141        ))
142    }
143
144    #[turbo_tasks::function]
145    fn module(&self) -> Vc<Box<dyn Module>> {
146        Vc::upcast(*self.module)
147    }
148}
149
150#[turbo_tasks::value_impl]
151impl EcmascriptChunkItem for RawModuleChunkItem {
152    #[turbo_tasks::function]
153    async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
154        let path = self.wasm_asset.path().await?;
155        let output_root = self.chunking_context.output_root().await?;
156
157        let Some(path) = output_root.get_path_to(&path) else {
158            bail!("WASM asset ident is not relative to output root");
159        };
160
161        Ok(EcmascriptChunkItemContent {
162            inner_code: format!(
163                "{TURBOPACK_EXPORT_VALUE}({path});",
164                path = StringifyJs(path)
165            )
166            .into(),
167            ..Default::default()
168        }
169        .cell())
170    }
171}