turbopack_core/chunk/
data.rs

1use anyhow::Result;
2use turbo_tasks::{ReadRef, 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<ReadRef<ModuleId>>,
15    pub excluded: Vec<ReadRef<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) = Vc::try_resolve_sidecast::<Box<dyn OutputChunk>>(chunk).await?
75        else {
76            return Ok(Vc::cell(Some(
77                ChunkData {
78                    path,
79                    included: Vec::new(),
80                    excluded: Vec::new(),
81                    module_chunks: Vec::new(),
82                }
83                .resolved_cell(),
84            )));
85        };
86
87        let runtime_info = output_chunk.runtime_info().await?;
88
89        let OutputChunkRuntimeInfo {
90            included_ids,
91            excluded_ids,
92            module_chunks,
93            placeholder_for_future_extensions: _,
94        } = &*runtime_info;
95
96        let included = if let Some(included_ids) = included_ids {
97            included_ids.await?.iter().copied().try_join().await?
98        } else {
99            Vec::new()
100        };
101        let excluded = if let Some(excluded_ids) = excluded_ids {
102            excluded_ids.await?.iter().copied().try_join().await?
103        } else {
104            Vec::new()
105        };
106        let module_chunks = if let Some(module_chunks) = module_chunks {
107            module_chunks
108                .await?
109                .iter()
110                .copied()
111                .map(|chunk| {
112                    let output_root = output_root.clone();
113
114                    async move {
115                        let chunk_path = chunk.path().await?;
116                        Ok(output_root
117                            .get_path_to(&chunk_path)
118                            .map(|path| path.to_owned()))
119                    }
120                })
121                .try_flat_join()
122                .await?
123        } else {
124            Vec::new()
125        };
126
127        Ok(Vc::cell(Some(
128            ChunkData {
129                path,
130                included,
131                excluded,
132                module_chunks,
133            }
134            .resolved_cell(),
135        )))
136    }
137
138    #[turbo_tasks::function]
139    pub async fn from_assets(
140        output_root: FileSystemPath,
141        chunks: Vc<OutputAssets>,
142    ) -> Result<Vc<ChunksData>> {
143        Ok(Vc::cell(
144            chunks
145                .await?
146                .iter()
147                .map(|&chunk| ChunkData::from_asset(output_root.clone(), *chunk))
148                .try_join()
149                .await?
150                .into_iter()
151                .flat_map(|chunk| *chunk)
152                .collect(),
153        ))
154    }
155}