Skip to main content

turbopack_core/chunk/
data.rs

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