Skip to main content

turbopack_ecmascript/manifest/
chunk_asset.rs

1use anyhow::Result;
2use indoc::formatdoc;
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{ResolvedVc, TryJoinIterExt, Vc};
5use turbopack_core::{
6    chunk::{
7        AsyncModuleInfo, ChunkData, ChunkableModule, ChunkingContext, ChunkingContextExt,
8        ChunksData, availability_info::AvailabilityInfo,
9    },
10    ident::AssetIdent,
11    module::{Module, ModuleSideEffects},
12    module_graph::{
13        ModuleGraph, chunk_group_info::ChunkGroup, module_batch::ChunkableModuleOrBatch,
14    },
15    output::OutputAssetsWithReferenced,
16};
17
18use crate::{
19    chunk::{
20        EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
21        data::EcmascriptChunkData, ecmascript_chunk_item,
22    },
23    runtime_functions::TURBOPACK_EXPORT_VALUE,
24    utils::StringifyJs,
25};
26
27/// The manifest module is deferred until requested by the manifest loader
28/// item when the dynamic `import()` expression is reached.
29///
30/// Its responsibility is to generate a Promise that will resolve only after
31/// all the necessary chunks needed by the dynamic import are loaded by the client.
32///
33/// Splitting the dynamic import into a quickly generate-able manifest loader
34/// item and a slow-to-generate manifest chunk allows for faster incremental
35/// compilation. The traversal won't be performed until the dynamic import is
36/// actually reached, instead of eagerly as part of the chunk that the dynamic
37/// import appears in.
38#[turbo_tasks::value(shared)]
39pub struct ManifestAsyncModule {
40    pub inner: ResolvedVc<Box<dyn ChunkableModule>>,
41    pub module_graph: ResolvedVc<ModuleGraph>,
42    pub chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
43    pub availability_info: AvailabilityInfo,
44}
45
46#[turbo_tasks::value_impl]
47impl ManifestAsyncModule {
48    #[turbo_tasks::function]
49    pub fn new(
50        module: ResolvedVc<Box<dyn ChunkableModule>>,
51        module_graph: ResolvedVc<ModuleGraph>,
52        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
53        availability_info: AvailabilityInfo,
54    ) -> Vc<Self> {
55        Self::cell(ManifestAsyncModule {
56            inner: module,
57            module_graph,
58            chunking_context,
59            availability_info,
60        })
61    }
62
63    #[turbo_tasks::function]
64    pub(super) fn chunk_group(&self) -> Vc<OutputAssetsWithReferenced> {
65        self.chunking_context.chunk_group_assets(
66            self.inner.ident(),
67            ChunkGroup::Async(ResolvedVc::upcast(self.inner)),
68            *self.module_graph,
69            self.availability_info,
70        )
71    }
72
73    #[turbo_tasks::function]
74    pub async fn manifest_chunk_group(
75        self: ResolvedVc<Self>,
76    ) -> Result<Vc<OutputAssetsWithReferenced>> {
77        let this = self.await?;
78        if let Some(chunk_items) = this.availability_info.available_modules() {
79            let inner_module = ResolvedVc::upcast(this.inner);
80            let batches = this
81                .module_graph
82                .module_batches(this.chunking_context.batching_config())
83                .await?;
84            let module_or_batch = batches.get_entry(inner_module).await?;
85            if let Some(chunkable_module_or_batch) =
86                ChunkableModuleOrBatch::from_module_or_batch(module_or_batch)
87                && *chunk_items.get(chunkable_module_or_batch.into()).await?
88            {
89                return Ok(OutputAssetsWithReferenced {
90                    assets: ResolvedVc::cell(vec![]),
91                    referenced_assets: ResolvedVc::cell(vec![]),
92                    references: ResolvedVc::cell(vec![]),
93                }
94                .cell());
95            }
96        }
97        Ok(this.chunking_context.chunk_group_assets(
98            self.ident(),
99            ChunkGroup::Async(ResolvedVc::upcast(self)),
100            *this.module_graph,
101            this.availability_info,
102        ))
103    }
104
105    #[turbo_tasks::function]
106    pub fn module_ident(&self) -> Vc<AssetIdent> {
107        self.inner.ident()
108    }
109
110    #[turbo_tasks::function]
111    pub async fn content_ident(&self) -> Result<Vc<AssetIdent>> {
112        let ident = self.inner.ident();
113        Ok(
114            if let Some(available_modules) = self.availability_info.available_modules() {
115                ident
116                    .owned()
117                    .await?
118                    .with_modifier(available_modules.hash().await?.to_string().into())
119                    .into_vc()
120            } else {
121                ident
122            },
123        )
124    }
125
126    #[turbo_tasks::function]
127    async fn chunks_data(self: Vc<Self>) -> Result<Vc<ChunksData>> {
128        let this = self.await?;
129        Ok(ChunkData::from_assets(
130            this.chunking_context.output_root().owned().await?,
131            *self.chunk_group().await?.assets,
132        ))
133    }
134}
135
136fn manifest_chunk_reference_description() -> RcStr {
137    rcstr!("manifest chunk")
138}
139
140#[turbo_tasks::value_impl]
141impl Module for ManifestAsyncModule {
142    #[turbo_tasks::function]
143    async fn ident(&self) -> Result<Vc<AssetIdent>> {
144        Ok(self
145            .inner
146            .ident()
147            .owned()
148            .await?
149            .with_modifier(manifest_chunk_reference_description())
150            .into_vc())
151    }
152
153    #[turbo_tasks::function]
154    fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
155        Vc::cell(None)
156    }
157
158    #[turbo_tasks::function]
159    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
160        ModuleSideEffects::SideEffectFree.cell()
161    }
162}
163
164#[turbo_tasks::value_impl]
165impl ChunkableModule for ManifestAsyncModule {
166    #[turbo_tasks::function]
167    fn as_chunk_item(
168        self: ResolvedVc<Self>,
169        module_graph: ResolvedVc<ModuleGraph>,
170        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
171    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
172        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
173    }
174}
175
176#[turbo_tasks::value_impl]
177impl EcmascriptChunkPlaceable for ManifestAsyncModule {
178    #[turbo_tasks::function]
179    fn get_exports(&self) -> Vc<EcmascriptExports> {
180        EcmascriptExports::Value.cell()
181    }
182
183    #[turbo_tasks::function]
184    async fn chunk_item_content(
185        self: Vc<Self>,
186        _chunking_context: Vc<Box<dyn ChunkingContext>>,
187        _module_graph: Vc<ModuleGraph>,
188        _async_module_info: Option<Vc<AsyncModuleInfo>>,
189        _estimated: bool,
190    ) -> Result<Vc<EcmascriptChunkItemContent>> {
191        let chunks_data = self.chunks_data().await?;
192        let chunks_data = chunks_data.iter().try_join().await?;
193        let chunks_data: Vec<_> = chunks_data
194            .iter()
195            .map(|chunk_data| EcmascriptChunkData::new(chunk_data))
196            .collect();
197
198        let code = formatdoc! {
199            r#"
200                {TURBOPACK_EXPORT_VALUE}({:#});
201            "#,
202            StringifyJs(&chunks_data)
203        };
204
205        Ok(EcmascriptChunkItemContent {
206            inner_code: code.into(),
207            ..Default::default()
208        }
209        .cell())
210    }
211
212    #[turbo_tasks::function]
213    fn chunk_item_content_ident(
214        self: Vc<Self>,
215        _chunking_context: Vc<Box<dyn ChunkingContext>>,
216        _module_graph: Vc<ModuleGraph>,
217    ) -> Vc<AssetIdent> {
218        self.content_ident()
219    }
220
221    #[turbo_tasks::function]
222    fn chunk_item_output_assets(
223        self: Vc<Self>,
224        _chunking_context: Vc<Box<dyn ChunkingContext>>,
225        _module_graph: Vc<ModuleGraph>,
226    ) -> Vc<OutputAssetsWithReferenced> {
227        self.chunk_group()
228    }
229}