turbopack_core/chunk/
available_modules.rs

1use anyhow::Result;
2use bincode::{Decode, Encode};
3use turbo_tasks::{
4    FxIndexSet, NonLocalValue, ReadRef, ResolvedVc, TaskInput, TryJoinIterExt, ValueToString, Vc,
5    trace::TraceRawVcs,
6};
7use turbo_tasks_hash::Xxh3Hash64Hasher;
8
9use crate::{
10    chunk::ChunkableModule,
11    module::Module,
12    module_graph::module_batch::{ChunkableModuleOrBatch, IdentStrings, ModuleBatch},
13};
14
15#[derive(
16    Debug, Copy, Clone, Hash, PartialEq, Eq, TraceRawVcs, NonLocalValue, TaskInput, Encode, Decode,
17)]
18pub enum AvailableModuleItem {
19    Module(ResolvedVc<Box<dyn ChunkableModule>>),
20    Batch(ResolvedVc<ModuleBatch>),
21    AsyncLoader(ResolvedVc<Box<dyn ChunkableModule>>),
22}
23
24impl AvailableModuleItem {
25    pub async fn ident_strings(&self) -> Result<IdentStrings> {
26        Ok(match self {
27            AvailableModuleItem::Module(module) => {
28                IdentStrings::Single(module.ident().to_string().owned().await?)
29            }
30            AvailableModuleItem::Batch(batch) => {
31                IdentStrings::Multiple(batch.ident_strings().await?)
32            }
33            AvailableModuleItem::AsyncLoader(module) => IdentStrings::Single(
34                format!("async loader {}", module.ident().to_string().await?).into(),
35            ),
36        })
37    }
38}
39
40impl From<ChunkableModuleOrBatch> for AvailableModuleItem {
41    fn from(value: ChunkableModuleOrBatch) -> Self {
42        match value {
43            ChunkableModuleOrBatch::Module(module) => AvailableModuleItem::Module(module),
44            ChunkableModuleOrBatch::Batch(batch) => AvailableModuleItem::Batch(batch),
45            ChunkableModuleOrBatch::None(id) => {
46                panic!("Cannot create AvailableModuleItem from None({})", id)
47            }
48        }
49    }
50}
51
52#[turbo_tasks::value(transparent)]
53#[derive(Debug, Clone)]
54pub struct AvailableModulesSet(
55    #[bincode(with = "turbo_bincode::indexset")] FxIndexSet<AvailableModuleItem>,
56);
57
58/// Allows to gather information about which assets are already available.
59/// Adding more roots will form a linked list like structure to allow caching
60/// `include` queries.
61#[turbo_tasks::value]
62pub struct AvailableModules {
63    parent: Option<ResolvedVc<AvailableModules>>,
64    modules: ResolvedVc<AvailableModulesSet>,
65}
66
67#[turbo_tasks::value_impl]
68impl AvailableModules {
69    #[turbo_tasks::function]
70    pub fn new(modules: ResolvedVc<AvailableModulesSet>) -> Vc<Self> {
71        AvailableModules {
72            parent: None,
73            modules,
74        }
75        .cell()
76    }
77
78    #[turbo_tasks::function]
79    pub fn with_modules(
80        self: ResolvedVc<Self>,
81        modules: ResolvedVc<AvailableModulesSet>,
82    ) -> Result<Vc<Self>> {
83        Ok(AvailableModules {
84            parent: Some(self),
85            modules,
86        }
87        .cell())
88    }
89
90    #[turbo_tasks::function]
91    pub async fn hash(&self) -> Result<Vc<u64>> {
92        let mut hasher = Xxh3Hash64Hasher::new();
93        if let Some(parent) = self.parent {
94            hasher.write_value(parent.hash().await?);
95        } else {
96            hasher.write_value(0u64);
97        }
98        let item_idents = self
99            .modules
100            .await?
101            .iter()
102            .map(async |&module| module.ident_strings().await)
103            .try_join()
104            .await?;
105        for idents in item_idents {
106            match idents {
107                IdentStrings::Single(ident) => hasher.write_value(ident),
108                IdentStrings::Multiple(idents) => {
109                    for ident in idents {
110                        hasher.write_value(ident);
111                    }
112                }
113                IdentStrings::None => {}
114            }
115        }
116        Ok(Vc::cell(hasher.finish()))
117    }
118
119    #[turbo_tasks::function]
120    pub async fn get(&self, item: AvailableModuleItem) -> Result<Vc<bool>> {
121        if self.modules.await?.contains(&item) {
122            return Ok(Vc::cell(true));
123        };
124        if let Some(parent) = self.parent {
125            return Ok(parent.get(item));
126        }
127        Ok(Vc::cell(false))
128    }
129
130    #[turbo_tasks::function]
131    pub async fn snapshot(&self) -> Result<Vc<AvailableModulesSnapshot>> {
132        let modules = self.modules.await?;
133        let parent = if let Some(parent) = self.parent {
134            Some(parent.snapshot().await?)
135        } else {
136            None
137        };
138
139        Ok(AvailableModulesSnapshot { parent, modules }.cell())
140    }
141}
142
143#[turbo_tasks::value(serialization = "none")]
144#[derive(Debug, Clone)]
145pub struct AvailableModulesSnapshot {
146    parent: Option<ReadRef<AvailableModulesSnapshot>>,
147    modules: ReadRef<AvailableModulesSet>,
148}
149
150impl AvailableModulesSnapshot {
151    pub fn get(&self, item: AvailableModuleItem) -> bool {
152        self.modules.contains(&item) || self.parent.as_ref().is_some_and(|parent| parent.get(item))
153    }
154}