turbopack_ecmascript/manifest/
loader_module.rs1use std::io::Write as _;
2
3use anyhow::{Result, anyhow};
4use indoc::writedoc;
5use turbo_rcstr::{RcStr, rcstr};
6use turbo_tasks::{ResolvedVc, TryJoinIterExt, Vc};
7use turbopack_core::{
8 chunk::{
9 AsyncModuleInfo, ChunkData, ChunkableModule, ChunkingContext, ChunksData,
10 ModuleChunkItemIdExt,
11 },
12 ident::AssetIdent,
13 module::{Module, ModuleSideEffects},
14 module_graph::ModuleGraph,
15 output::OutputAssetsWithReferenced,
16 reference::ModuleReferences,
17};
18
19use super::chunk_asset::ManifestAsyncModule;
20use crate::{
21 chunk::{
22 EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
23 data::EcmascriptChunkData, ecmascript_chunk_item,
24 },
25 runtime_functions::{TURBOPACK_EXPORT_VALUE, TURBOPACK_LOAD, TURBOPACK_REQUIRE},
26 utils::{StringifyJs, StringifyModuleId},
27};
28
29fn modifier() -> RcStr {
30 rcstr!("loader")
31}
32
33#[turbo_tasks::value]
48pub struct ManifestLoaderModule {
49 pub manifest: ResolvedVc<ManifestAsyncModule>,
50}
51
52#[turbo_tasks::value_impl]
53impl ManifestLoaderModule {
54 #[turbo_tasks::function]
55 pub fn new(manifest: ResolvedVc<ManifestAsyncModule>) -> Vc<Self> {
56 Self::cell(ManifestLoaderModule { manifest })
57 }
58
59 #[turbo_tasks::function]
60 pub async fn chunks_data(self: Vc<Self>) -> Result<Vc<ChunksData>> {
61 let this = self.await?;
62 let manifest = this.manifest.await?;
63 let chunks = this.manifest.manifest_chunk_group().await?.assets;
64 Ok(ChunkData::from_assets(
65 manifest.chunking_context.output_root().owned().await?,
66 *chunks,
67 ))
68 }
69
70 #[turbo_tasks::function]
71 pub async fn asset_ident_for(module: Vc<Box<dyn ChunkableModule>>) -> Result<Vc<AssetIdent>> {
72 Ok(module
73 .ident()
74 .owned()
75 .await?
76 .with_modifier(modifier())
77 .into_vc())
78 }
79}
80
81#[turbo_tasks::value_impl]
82impl Module for ManifestLoaderModule {
83 #[turbo_tasks::function]
84 async fn ident(&self) -> Result<Vc<AssetIdent>> {
85 Ok(self
86 .manifest
87 .module_ident()
88 .owned()
89 .await?
90 .with_modifier(modifier())
91 .into_vc())
92 }
93
94 #[turbo_tasks::function]
95 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
96 Vc::cell(None)
97 }
98
99 #[turbo_tasks::function]
100 fn references(&self) -> Vc<ModuleReferences> {
101 Vc::cell(vec![])
102 }
103
104 #[turbo_tasks::function]
105 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
106 ModuleSideEffects::SideEffectFree.cell()
107 }
108}
109
110#[turbo_tasks::value_impl]
111impl ChunkableModule for ManifestLoaderModule {
112 #[turbo_tasks::function]
113 fn as_chunk_item(
114 self: ResolvedVc<Self>,
115 module_graph: ResolvedVc<ModuleGraph>,
116 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
117 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
118 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
119 }
120}
121
122#[turbo_tasks::value_impl]
123impl EcmascriptChunkPlaceable for ManifestLoaderModule {
124 #[turbo_tasks::function]
125 fn get_exports(&self) -> Vc<EcmascriptExports> {
126 EcmascriptExports::Value.cell()
127 }
128
129 #[turbo_tasks::function]
130 async fn chunk_item_content(
131 self: Vc<Self>,
132 _chunking_context: Vc<Box<dyn ChunkingContext>>,
133 _module_graph: Vc<ModuleGraph>,
134 _async_module_info: Option<Vc<AsyncModuleInfo>>,
135 _estimated: bool,
136 ) -> Result<Vc<EcmascriptChunkItemContent>> {
137 let this = self.await?;
138 let mut code = Vec::new();
139
140 let manifest = this.manifest.await?;
141
142 let chunks_server_data = &*self.chunks_data().await?.iter().try_join().await?;
147
148 let item_id = this
151 .manifest
152 .chunk_item_id(*manifest.chunking_context)
153 .await?;
154
155 let placeable =
158 ResolvedVc::try_downcast::<Box<dyn EcmascriptChunkPlaceable>>(manifest.inner)
159 .ok_or_else(|| anyhow!("asset is not placeable in ecmascript chunk"))?;
160 let dynamic_id = placeable.chunk_item_id(*manifest.chunking_context).await?;
161
162 writedoc!(
169 code,
170 r#"
171 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
172 return Promise.all({chunks_server_data}.map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{
173 return {TURBOPACK_REQUIRE}({item_id});
174 }}).then((chunks) => {{
175 return Promise.all(chunks.map((chunk) => {TURBOPACK_LOAD}(chunk)));
176 }}).then(() => {{
177 return parentImport({dynamic_id});
178 }});
179 }});
180 "#,
181 chunks_server_data = StringifyJs(
182 &chunks_server_data
183 .iter()
184 .map(|chunk_data| EcmascriptChunkData::new(chunk_data))
185 .collect::<Vec<_>>()
186 ),
187 item_id = StringifyModuleId(&item_id),
188 dynamic_id = StringifyModuleId(&dynamic_id),
189 )?;
190
191 Ok(EcmascriptChunkItemContent {
192 inner_code: code.into(),
193 ..Default::default()
194 }
195 .cell())
196 }
197
198 #[turbo_tasks::function]
199 fn chunk_item_content_ident(
200 self: Vc<Self>,
201 _chunking_context: Vc<Box<dyn ChunkingContext>>,
202 _module_graph: Vc<ModuleGraph>,
203 ) -> Vc<AssetIdent> {
204 self.content_ident()
205 }
206
207 #[turbo_tasks::function]
208 fn chunk_item_output_assets(
209 &self,
210 _chunking_context: Vc<Box<dyn ChunkingContext>>,
211 _module_graph: Vc<ModuleGraph>,
212 ) -> Vc<OutputAssetsWithReferenced> {
213 self.manifest.manifest_chunk_group()
214 }
215}
216
217#[turbo_tasks::value_impl]
218impl ManifestLoaderModule {
219 #[turbo_tasks::function]
220 pub async fn content_ident(&self) -> Result<Vc<AssetIdent>> {
221 Ok(self
222 .manifest
223 .content_ident()
224 .owned()
225 .await?
226 .with_modifier(modifier())
227 .into_vc())
228 }
229}