turbopack_ecmascript/manifest/
loader_item.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 ChunkData, ChunkItem, ChunkType, ChunkableModule, ChunkingContext, ChunksData,
10 ModuleChunkItemIdExt,
11 },
12 ident::AssetIdent,
13 module::Module,
14 module_graph::ModuleGraph,
15 output::OutputAssets,
16};
17
18use super::chunk_asset::ManifestAsyncModule;
19use crate::{
20 chunk::{
21 EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable,
22 EcmascriptChunkType, data::EcmascriptChunkData,
23 },
24 runtime_functions::{TURBOPACK_EXPORT_VALUE, TURBOPACK_LOAD, TURBOPACK_REQUIRE},
25 utils::{StringifyJs, StringifyModuleId},
26};
27
28fn modifier() -> RcStr {
29 rcstr!("loader")
30}
31
32#[turbo_tasks::value]
47pub struct ManifestLoaderChunkItem {
48 manifest: ResolvedVc<ManifestAsyncModule>,
49 module_graph: ResolvedVc<ModuleGraph>,
50 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
51}
52
53#[turbo_tasks::value_impl]
54impl ManifestLoaderChunkItem {
55 #[turbo_tasks::function]
56 pub fn new(
57 manifest: ResolvedVc<ManifestAsyncModule>,
58 module_graph: ResolvedVc<ModuleGraph>,
59 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
60 ) -> Vc<Self> {
61 Self::cell(ManifestLoaderChunkItem {
62 manifest,
63 module_graph,
64 chunking_context,
65 })
66 }
67
68 #[turbo_tasks::function]
69 pub async fn chunks_data(&self) -> Result<Vc<ChunksData>> {
70 let chunks = self.manifest.manifest_chunks();
71 Ok(ChunkData::from_assets(
72 self.chunking_context.output_root().await?.clone_value(),
73 chunks,
74 ))
75 }
76
77 #[turbo_tasks::function]
78 pub fn asset_ident_for(module: Vc<Box<dyn ChunkableModule>>) -> Vc<AssetIdent> {
79 module.ident().with_modifier(modifier())
80 }
81}
82
83#[turbo_tasks::value_impl]
84impl ChunkItem for ManifestLoaderChunkItem {
85 #[turbo_tasks::function]
86 fn asset_ident(&self) -> Vc<AssetIdent> {
87 self.manifest.module_ident().with_modifier(modifier())
88 }
89
90 #[turbo_tasks::function]
91 fn content_ident(&self) -> Vc<AssetIdent> {
92 self.manifest.content_ident().with_modifier(modifier())
93 }
94
95 #[turbo_tasks::function]
96 async fn references(self: Vc<Self>) -> Result<Vc<OutputAssets>> {
97 let this = self.await?;
98 let mut references = (*this.manifest.manifest_chunks().await?).clone();
99 for chunk_data in &*self.chunks_data().await? {
100 references.extend(chunk_data.references().await?);
101 }
102
103 Ok(Vc::cell(references))
104 }
105
106 #[turbo_tasks::function]
107 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
108 *ResolvedVc::upcast(self.chunking_context)
109 }
110
111 #[turbo_tasks::function]
112 async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
113 Ok(Vc::upcast(
114 Vc::<EcmascriptChunkType>::default().resolve().await?,
115 ))
116 }
117
118 #[turbo_tasks::function]
119 fn module(&self) -> Vc<Box<dyn Module>> {
120 *ResolvedVc::upcast(self.manifest)
121 }
122}
123
124#[turbo_tasks::value_impl]
125impl EcmascriptChunkItem for ManifestLoaderChunkItem {
126 #[turbo_tasks::function]
127 async fn content(self: Vc<Self>) -> Result<Vc<EcmascriptChunkItemContent>> {
128 let this = &*self.await?;
129 let mut code = Vec::new();
130
131 let manifest = this.manifest.await?;
132
133 let chunks_server_data = &*self.chunks_data().await?.iter().try_join().await?;
138
139 let item_id = &*this
142 .manifest
143 .chunk_item_id(*ResolvedVc::upcast(manifest.chunking_context))
144 .await?;
145
146 let placeable =
149 ResolvedVc::try_downcast::<Box<dyn EcmascriptChunkPlaceable>>(manifest.inner)
150 .ok_or_else(|| anyhow!("asset is not placeable in ecmascript chunk"))?;
151 let dynamic_id = &*placeable
152 .chunk_item_id(*ResolvedVc::upcast(manifest.chunking_context))
153 .await?;
154
155 writedoc!(
162 code,
163 r#"
164 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
165 return Promise.all({chunks_server_data}.map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{
166 return {TURBOPACK_REQUIRE}({item_id});
167 }}).then((chunks) => {{
168 return Promise.all(chunks.map((chunk) => {TURBOPACK_LOAD}(chunk)));
169 }}).then(() => {{
170 return parentImport({dynamic_id});
171 }});
172 }});
173 "#,
174 chunks_server_data = StringifyJs(
175 &chunks_server_data
176 .iter()
177 .map(|chunk_data| EcmascriptChunkData::new(chunk_data))
178 .collect::<Vec<_>>()
179 ),
180 item_id = StringifyModuleId(item_id),
181 dynamic_id = StringifyModuleId(dynamic_id),
182 )?;
183
184 Ok(EcmascriptChunkItemContent {
185 inner_code: code.into(),
186 ..Default::default()
187 }
188 .into())
189 }
190}