turbopack_core/chunk/
data.rs

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