turbopack_ecmascript/async_chunk/
chunk_item.rs

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