turbopack_ecmascript/chunk_list/
version.rs1use anyhow::Result;
2use turbo_rcstr::RcStr;
3use turbo_tasks::{FxIndexMap, ResolvedVc, TraitRef, TryJoinIterExt, Vc};
4use turbo_tasks_hash::{Xxh3Hash64Hasher, encode_base64};
5use turbopack_core::version::{
6 MergeableVersionedContent, Version, VersionedContent, VersionedContentMerger,
7};
8
9type VersionTraitRef = TraitRef<Box<dyn Version>>;
10
11#[turbo_tasks::value(serialization = "skip", shared, eq = "manual")]
19#[derive(Debug)]
20pub struct ChunkListVersion {
21 pub id: RcStr,
22 pub by_path: FxIndexMap<String, VersionTraitRef>,
24 pub by_merger: FxIndexMap<ResolvedVc<Box<dyn VersionedContentMerger>>, VersionTraitRef>,
29}
30
31impl PartialEq for ChunkListVersion {
34 fn eq(&self, other: &Self) -> bool {
35 self.id == other.id
36 }
37}
38
39impl Eq for ChunkListVersion {}
40
41#[turbo_tasks::value_impl]
42impl Version for ChunkListVersion {
43 #[turbo_tasks::function]
44 async fn id(&self) -> Result<Vc<RcStr>> {
45 Ok(Vc::cell(self.id.clone()))
46 }
47}
48
49async fn chunk_list_version_id(
50 by_path: &FxIndexMap<String, VersionTraitRef>,
51 by_merger: &FxIndexMap<ResolvedVc<Box<dyn VersionedContentMerger>>, VersionTraitRef>,
52) -> Result<RcStr> {
53 let by_path = {
54 let mut by_path = by_path
55 .iter()
56 .map(|(path, version)| (path, TraitRef::cell(version.clone())))
57 .map(async |(path, version)| {
58 let id = version.id().owned().await?;
59 Ok((path, id))
60 })
61 .try_join()
62 .await?;
63 by_path.sort();
64 by_path
65 };
66 let by_merger = {
67 let mut by_merger = by_merger
68 .iter()
69 .map(|(_merger, version)| TraitRef::cell(version.clone()).id().owned())
70 .try_join()
71 .await?;
72 by_merger.sort();
73 by_merger
74 };
75 let mut hasher = Xxh3Hash64Hasher::new();
76 hasher.write_value(by_path.len());
77 for (path, id) in by_path {
78 hasher.write_value(path);
79 hasher.write_value(id);
80 }
81 hasher.write_value(by_merger.len());
82 for id in by_merger {
83 hasher.write_value(id);
84 }
85 let hash = hasher.finish();
86 let hash = encode_base64(hash);
87 Ok(hash.into())
88}
89
90pub async fn compute_chunk_list_version(
99 chunks_contents: &FxIndexMap<String, ResolvedVc<Box<dyn VersionedContent>>>,
100) -> Result<Vc<ChunkListVersion>> {
101 let mut by_merger = FxIndexMap::<_, Vec<_>>::default();
102 let mut by_path = FxIndexMap::<_, _>::default();
103
104 for (chunk_path, chunk_content) in chunks_contents {
105 if let Some(mergeable) =
106 ResolvedVc::try_sidecast::<Box<dyn MergeableVersionedContent>>(*chunk_content)
107 {
108 let merger = mergeable.get_merger().to_resolved().await?;
109 by_merger.entry(merger).or_default().push(*chunk_content);
110 } else {
111 by_path.insert(
112 chunk_path.clone(),
113 chunk_content.version().into_trait_ref().await?,
114 );
115 }
116 }
117
118 let by_merger = by_merger
119 .into_iter()
120 .map(|(merger, contents)| (merger, Vc::cell(contents)))
121 .map(async |(merger, contents)| {
122 Ok((
123 merger,
124 merger.merge(contents).version().into_trait_ref().await?,
125 ))
126 })
127 .try_join()
128 .await?
129 .into_iter()
130 .collect();
131
132 let id = chunk_list_version_id(&by_path, &by_merger).await?;
133 Ok(ChunkListVersion {
134 id,
135 by_path,
136 by_merger,
137 }
138 .cell())
139}