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 fn asset_ident_for(module: Vc<Box<dyn ChunkableModule>>) -> Vc<AssetIdent> {
72 module.ident().with_modifier(modifier())
73 }
74}
75
76#[turbo_tasks::value_impl]
77impl Module for ManifestLoaderModule {
78 #[turbo_tasks::function]
79 fn ident(&self) -> Vc<AssetIdent> {
80 self.manifest.module_ident().with_modifier(modifier())
81 }
82
83 #[turbo_tasks::function]
84 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
85 Vc::cell(None)
86 }
87
88 #[turbo_tasks::function]
89 fn references(&self) -> Vc<ModuleReferences> {
90 Vc::cell(vec![])
91 }
92
93 #[turbo_tasks::function]
94 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
95 ModuleSideEffects::SideEffectFree.cell()
96 }
97}
98
99#[turbo_tasks::value_impl]
100impl ChunkableModule for ManifestLoaderModule {
101 #[turbo_tasks::function]
102 fn as_chunk_item(
103 self: ResolvedVc<Self>,
104 module_graph: ResolvedVc<ModuleGraph>,
105 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
106 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
107 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
108 }
109}
110
111#[turbo_tasks::value_impl]
112impl EcmascriptChunkPlaceable for ManifestLoaderModule {
113 #[turbo_tasks::function]
114 fn get_exports(&self) -> Vc<EcmascriptExports> {
115 EcmascriptExports::Value.cell()
116 }
117
118 #[turbo_tasks::function]
119 async fn chunk_item_content(
120 self: Vc<Self>,
121 _chunking_context: Vc<Box<dyn ChunkingContext>>,
122 _module_graph: Vc<ModuleGraph>,
123 _async_module_info: Option<Vc<AsyncModuleInfo>>,
124 _estimated: bool,
125 ) -> Result<Vc<EcmascriptChunkItemContent>> {
126 let this = self.await?;
127 let mut code = Vec::new();
128
129 let manifest = this.manifest.await?;
130
131 let chunks_server_data = &*self.chunks_data().await?.iter().try_join().await?;
136
137 let item_id = this
140 .manifest
141 .chunk_item_id(*manifest.chunking_context)
142 .await?;
143
144 let placeable =
147 ResolvedVc::try_downcast::<Box<dyn EcmascriptChunkPlaceable>>(manifest.inner)
148 .ok_or_else(|| anyhow!("asset is not placeable in ecmascript chunk"))?;
149 let dynamic_id = placeable.chunk_item_id(*manifest.chunking_context).await?;
150
151 writedoc!(
158 code,
159 r#"
160 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
161 return Promise.all({chunks_server_data}.map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{
162 return {TURBOPACK_REQUIRE}({item_id});
163 }}).then((chunks) => {{
164 return Promise.all(chunks.map((chunk) => {TURBOPACK_LOAD}(chunk)));
165 }}).then(() => {{
166 return parentImport({dynamic_id});
167 }});
168 }});
169 "#,
170 chunks_server_data = StringifyJs(
171 &chunks_server_data
172 .iter()
173 .map(|chunk_data| EcmascriptChunkData::new(chunk_data))
174 .collect::<Vec<_>>()
175 ),
176 item_id = StringifyModuleId(&item_id),
177 dynamic_id = StringifyModuleId(&dynamic_id),
178 )?;
179
180 Ok(EcmascriptChunkItemContent {
181 inner_code: code.into(),
182 ..Default::default()
183 }
184 .cell())
185 }
186
187 #[turbo_tasks::function]
188 fn chunk_item_content_ident(
189 self: Vc<Self>,
190 _chunking_context: Vc<Box<dyn ChunkingContext>>,
191 _module_graph: Vc<ModuleGraph>,
192 ) -> Vc<AssetIdent> {
193 self.content_ident()
194 }
195
196 #[turbo_tasks::function]
197 fn chunk_item_output_assets(
198 &self,
199 _chunking_context: Vc<Box<dyn ChunkingContext>>,
200 _module_graph: Vc<ModuleGraph>,
201 ) -> Vc<OutputAssetsWithReferenced> {
202 self.manifest.manifest_chunk_group()
203 }
204}
205
206#[turbo_tasks::value_impl]
207impl ManifestLoaderModule {
208 #[turbo_tasks::function]
209 pub fn content_ident(&self) -> Vc<AssetIdent> {
210 self.manifest.content_ident().with_modifier(modifier())
211 }
212}