turbopack_core/chunk/
data.rs

1use anyhow::Result;
2use turbo_rcstr::RcStr;
3use turbo_tasks::{ReadRef, ResolvedVc, TryJoinIterExt, ValueToString, Vc};
4use turbo_tasks_fs::FileSystemPath;
5use turbo_tasks_hash::Xxh3Hash64Hasher;
6
7use crate::{
8    chunk::{ModuleId, OutputChunk, OutputChunkRuntimeInfo},
9    output::{OutputAsset, OutputAssets},
10};
11
12#[turbo_tasks::value]
13pub struct ChunkData {
14    pub path: String,
15    pub included: Vec<ReadRef<ModuleId>>,
16    pub excluded: Vec<ReadRef<ModuleId>>,
17    pub module_chunks: Vec<String>,
18    pub references: ResolvedVc<OutputAssets>,
19}
20
21#[turbo_tasks::value(transparent)]
22pub struct ChunkDataOption(Option<ResolvedVc<ChunkData>>);
23
24// NOTE(alexkirsz) Our convention for naming vector types is to add an "s" to
25// the end of the type name, but in this case it would be both gramatically
26// incorrect and clash with the variable names everywhere.
27// TODO(WEB-101) Should fix this.
28#[turbo_tasks::value(transparent)]
29pub struct ChunksData(Vec<ResolvedVc<ChunkData>>);
30
31#[turbo_tasks::value_impl]
32impl ChunksData {
33    #[turbo_tasks::function]
34    pub async fn hash(&self) -> Result<Vc<u64>> {
35        let mut hasher = Xxh3Hash64Hasher::new();
36        for chunk in self.0.iter() {
37            hasher.write_value(chunk.await?.path.as_str());
38        }
39        Ok(Vc::cell(hasher.finish()))
40    }
41}
42
43#[turbo_tasks::function]
44fn module_chunk_reference_description() -> Vc<RcStr> {
45    Vc::cell("module chunk".into())
46}
47
48#[turbo_tasks::value_impl]
49impl ChunkData {
50    #[turbo_tasks::function]
51    pub async fn hash(&self) -> Result<Vc<u64>> {
52        let mut hasher = Xxh3Hash64Hasher::new();
53        hasher.write_value(self.path.as_str());
54        for module in &self.included {
55            hasher.write_value(module);
56        }
57        for module in &self.excluded {
58            hasher.write_value(module);
59        }
60        for module_chunk in &self.module_chunks {
61            hasher.write_value(module_chunk.as_str());
62        }
63        for reference in self.references.await?.iter() {
64            hasher.write_value(reference.path().to_string().await?);
65        }
66
67        Ok(Vc::cell(hasher.finish()))
68    }
69
70    #[turbo_tasks::function]
71    pub async fn from_asset(
72        output_root: Vc<FileSystemPath>,
73        chunk: Vc<Box<dyn OutputAsset>>,
74    ) -> Result<Vc<ChunkDataOption>> {
75        let output_root = output_root.await?;
76        let path = chunk.path().await?;
77        // The "path" in this case is the chunk's path, not the chunk item's path.
78        // The difference is a chunk is a file served by the dev server, and an
79        // item is one of several that are contained in that chunk file.
80        let Some(path) = output_root.get_path_to(&path) else {
81            return Ok(Vc::cell(None));
82        };
83        let path = path.to_string();
84
85        let Some(output_chunk) = Vc::try_resolve_sidecast::<Box<dyn OutputChunk>>(chunk).await?
86        else {
87            return Ok(Vc::cell(Some(
88                ChunkData {
89                    path,
90                    included: Vec::new(),
91                    excluded: Vec::new(),
92                    module_chunks: Vec::new(),
93                    references: OutputAssets::empty().to_resolved().await?,
94                }
95                .resolved_cell(),
96            )));
97        };
98
99        let runtime_info = output_chunk.runtime_info().await?;
100
101        let OutputChunkRuntimeInfo {
102            included_ids,
103            excluded_ids,
104            module_chunks,
105            placeholder_for_future_extensions: _,
106        } = &*runtime_info;
107
108        let included = if let Some(included_ids) = included_ids {
109            included_ids.await?.iter().copied().try_join().await?
110        } else {
111            Vec::new()
112        };
113        let excluded = if let Some(excluded_ids) = excluded_ids {
114            excluded_ids.await?.iter().copied().try_join().await?
115        } else {
116            Vec::new()
117        };
118        let (module_chunks, module_chunks_references) = if let Some(module_chunks) = module_chunks {
119            module_chunks
120                .await?
121                .iter()
122                .copied()
123                .map(|chunk| {
124                    let output_root = output_root.clone();
125
126                    async move {
127                        let chunk_path = chunk.path().await?;
128                        Ok(output_root
129                            .get_path_to(&chunk_path)
130                            .map(|path| (path.to_owned(), chunk)))
131                    }
132                })
133                .try_join()
134                .await?
135                .into_iter()
136                .flatten()
137                .unzip()
138        } else {
139            (Vec::new(), Vec::new())
140        };
141
142        Ok(Vc::cell(Some(
143            ChunkData {
144                path,
145                included,
146                excluded,
147                module_chunks,
148                references: ResolvedVc::cell(module_chunks_references),
149            }
150            .resolved_cell(),
151        )))
152    }
153
154    #[turbo_tasks::function]
155    pub async fn from_assets(
156        output_root: Vc<FileSystemPath>,
157        chunks: Vc<OutputAssets>,
158    ) -> Result<Vc<ChunksData>> {
159        Ok(Vc::cell(
160            chunks
161                .await?
162                .iter()
163                .map(|&chunk| ChunkData::from_asset(output_root, *chunk))
164                .try_join()
165                .await?
166                .into_iter()
167                .flat_map(|chunk| *chunk)
168                .collect(),
169        ))
170    }
171
172    /// Returns [`OutputAsset`]s that this chunk data references.
173    #[turbo_tasks::function]
174    pub fn references(&self) -> Vc<OutputAssets> {
175        *self.references
176    }
177}