turbopack_ecmascript/async_chunk/
chunk_item.rs

1use anyhow::Result;
2use indoc::formatdoc;
3use turbo_tasks::{ResolvedVc, TryJoinIterExt, Vc};
4use turbopack_core::{
5    chunk::{
6        ChunkData, ChunkItem, ChunkType, ChunkingContext, ChunkingContextExt, ChunksData,
7        ModuleChunkItemIdExt,
8    },
9    ident::AssetIdent,
10    module::Module,
11    module_graph::{
12        ModuleGraph, chunk_group_info::ChunkGroup, module_batch::ChunkableModuleOrBatch,
13    },
14    output::OutputAssets,
15};
16
17use crate::{
18    async_chunk::module::AsyncLoaderModule,
19    chunk::{
20        EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable,
21        EcmascriptChunkType, data::EcmascriptChunkData,
22    },
23    runtime_functions::{TURBOPACK_EXPORT_VALUE, TURBOPACK_LOAD},
24    utils::{StringifyJs, StringifyModuleId},
25};
26
27#[turbo_tasks::value(shared)]
28pub struct AsyncLoaderChunkItem {
29    pub module: ResolvedVc<AsyncLoaderModule>,
30    pub module_graph: ResolvedVc<ModuleGraph>,
31    pub chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
32}
33
34#[turbo_tasks::value_impl]
35impl AsyncLoaderChunkItem {
36    #[turbo_tasks::function]
37    pub(super) async fn chunks(&self) -> Result<Vc<OutputAssets>> {
38        let module = self.module.await?;
39        if let Some(chunk_items) = module.availability_info.available_modules() {
40            let inner_module = ResolvedVc::upcast(module.inner);
41            let batches = self
42                .module_graph
43                .module_batches(self.chunking_context.batching_config())
44                .await?;
45            let module_or_batch = batches.get_entry(inner_module).await?;
46            if let Some(chunkable_module_or_batch) =
47                ChunkableModuleOrBatch::from_module_or_batch(module_or_batch)
48                && *chunk_items.get(chunkable_module_or_batch).await?
49            {
50                return Ok(Vc::cell(vec![]));
51            }
52        }
53        Ok(self.chunking_context.chunk_group_assets(
54            module.inner.ident(),
55            ChunkGroup::Async(ResolvedVc::upcast(module.inner)),
56            *self.module_graph,
57            module.availability_info,
58        ))
59    }
60
61    #[turbo_tasks::function]
62    async fn chunks_data(self: Vc<Self>) -> Result<Vc<ChunksData>> {
63        let this = self.await?;
64        Ok(ChunkData::from_assets(
65            this.chunking_context.output_root().owned().await?,
66            self.chunks(),
67        ))
68    }
69}
70
71#[turbo_tasks::value_impl]
72impl EcmascriptChunkItem for AsyncLoaderChunkItem {
73    #[turbo_tasks::function]
74    async fn content(self: Vc<Self>) -> Result<Vc<EcmascriptChunkItemContent>> {
75        let this = self.await?;
76        let module = this.module.await?;
77
78        let id = if let Some(placeable) =
79            Vc::try_resolve_downcast::<Box<dyn EcmascriptChunkPlaceable>>(*module.inner).await?
80        {
81            Some(
82                placeable
83                    .chunk_item_id(*ResolvedVc::upcast(this.chunking_context))
84                    .await?,
85            )
86        } else {
87            None
88        };
89        let id = id.as_deref();
90
91        let chunks_data = self.chunks_data().await?;
92        let chunks_data = chunks_data.iter().try_join().await?;
93        let chunks_data: Vec<_> = chunks_data
94            .iter()
95            .map(|chunk_data| EcmascriptChunkData::new(chunk_data))
96            .collect();
97
98        let code = match (id, chunks_data.is_empty()) {
99            (Some(id), true) => {
100                formatdoc! {
101                    r#"
102                        {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
103                            return Promise.resolve().then(() => {{
104                                return parentImport({id});
105                            }});
106                        }});
107                    "#,
108                    id = StringifyModuleId(id),
109                }
110            }
111            (Some(id), false) => {
112                formatdoc! {
113                    r#"
114                        {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
115                            return Promise.all({chunks:#}.map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{
116                                return parentImport({id});
117                            }});
118                        }});
119                    "#,
120                    chunks = StringifyJs(&chunks_data),
121                    id = StringifyModuleId(id),
122                }
123            }
124            (None, true) => {
125                formatdoc! {
126                    r#"
127                        {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
128                            return Promise.resolve();
129                        }});
130                    "#,
131                }
132            }
133            (None, false) => {
134                formatdoc! {
135                    r#"
136                        {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
137                            return Promise.all({chunks:#}.map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{}});
138                        }});
139                    "#,
140                    chunks = StringifyJs(&chunks_data),
141                }
142            }
143        };
144
145        Ok(EcmascriptChunkItemContent {
146            inner_code: code.into(),
147            ..Default::default()
148        }
149        .into())
150    }
151}
152
153#[turbo_tasks::value_impl]
154impl ChunkItem for AsyncLoaderChunkItem {
155    #[turbo_tasks::function]
156    fn asset_ident(&self) -> Vc<AssetIdent> {
157        self.module.ident()
158    }
159
160    #[turbo_tasks::function]
161    async fn content_ident(self: Vc<Self>) -> Result<Vc<AssetIdent>> {
162        let ident = self.module().ident();
163
164        Ok(ident.with_modifier(self.chunks_data().hash().await?.to_string().into()))
165    }
166
167    #[turbo_tasks::function]
168    fn references(self: Vc<Self>) -> Vc<OutputAssets> {
169        self.chunks()
170    }
171
172    #[turbo_tasks::function]
173    fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
174        *ResolvedVc::upcast(self.chunking_context)
175    }
176
177    #[turbo_tasks::function]
178    async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
179        Ok(Vc::upcast(
180            Vc::<EcmascriptChunkType>::default().resolve().await?,
181        ))
182    }
183
184    #[turbo_tasks::function]
185    fn module(&self) -> Vc<Box<dyn Module>> {
186        *ResolvedVc::upcast(self.module)
187    }
188}