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,
10 module_graph::ModuleGraph,
11 output::{OutputAsset, OutputAssets},
12 source::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#[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
64#[turbo_tasks::value_impl]
65impl Asset for RawWebAssemblyModuleAsset {
66 #[turbo_tasks::function]
67 fn content(&self) -> Vc<AssetContent> {
68 self.source.content()
69 }
70}
71
72#[turbo_tasks::value_impl]
73impl ChunkableModule for RawWebAssemblyModuleAsset {
74 #[turbo_tasks::function]
75 async fn as_chunk_item(
76 self: ResolvedVc<Self>,
77 _module_graph: Vc<ModuleGraph>,
78 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
79 ) -> Result<Vc<Box<dyn turbopack_core::chunk::ChunkItem>>> {
80 Ok(Vc::upcast(
81 RawModuleChunkItem {
82 module: self,
83 chunking_context,
84 wasm_asset: self.wasm_asset(*chunking_context).to_resolved().await?,
85 }
86 .cell(),
87 ))
88 }
89}
90
91#[turbo_tasks::value_impl]
92impl EcmascriptChunkPlaceable for RawWebAssemblyModuleAsset {
93 #[turbo_tasks::function]
94 fn get_exports(self: Vc<Self>) -> Vc<EcmascriptExports> {
95 EcmascriptExports::Value.cell()
96 }
97}
98
99#[turbo_tasks::value]
100struct RawModuleChunkItem {
101 module: ResolvedVc<RawWebAssemblyModuleAsset>,
102 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
103 wasm_asset: ResolvedVc<WebAssemblyAsset>,
104}
105
106#[turbo_tasks::value_impl]
107impl ChunkItem for RawModuleChunkItem {
108 #[turbo_tasks::function]
109 fn asset_ident(&self) -> Vc<AssetIdent> {
110 self.module.ident()
111 }
112
113 #[turbo_tasks::function]
114 fn references(&self) -> Result<Vc<OutputAssets>> {
115 Ok(Vc::cell(vec![ResolvedVc::upcast(self.wasm_asset)]))
116 }
117
118 #[turbo_tasks::function]
119 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
120 Vc::upcast(*self.chunking_context)
121 }
122
123 #[turbo_tasks::function]
124 async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
125 Ok(Vc::upcast(
126 Vc::<EcmascriptChunkType>::default().resolve().await?,
127 ))
128 }
129
130 #[turbo_tasks::function]
131 fn module(&self) -> Vc<Box<dyn Module>> {
132 Vc::upcast(*self.module)
133 }
134}
135
136#[turbo_tasks::value_impl]
137impl EcmascriptChunkItem for RawModuleChunkItem {
138 #[turbo_tasks::function]
139 async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
140 let path = self.wasm_asset.path().await?;
141 let output_root = self.chunking_context.output_root().await?;
142
143 let Some(path) = output_root.get_path_to(&path) else {
144 bail!("WASM asset ident is not relative to output root");
145 };
146
147 Ok(EcmascriptChunkItemContent {
148 inner_code: format!(
149 "{TURBOPACK_EXPORT_VALUE}({path});",
150 path = StringifyJs(path)
151 )
152 .into(),
153 ..Default::default()
154 }
155 .into())
156 }
157}