Skip to main content

turbopack_core/chunk/
available_modules.rs

1use anyhow::Result;
2use bincode::{Decode, Encode};
3use turbo_tasks::{
4    FxIndexSet, NonLocalValue, OperationVc, ReadRef, ResolvedVc, TaskInput, TryJoinIterExt,
5    ValueToString, Vc, trace::TraceRawVcs, turbofmt,
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) => {
34                IdentStrings::Single(turbofmt!("async loader {}", module.ident()).await?)
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: OperationVc<AvailableModulesSet>,
65}
66
67#[turbo_tasks::value_impl]
68impl AvailableModules {
69    #[turbo_tasks::function]
70    pub fn new(modules: OperationVc<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: OperationVc<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            .connect()
101            .await?
102            .iter()
103            .map(async |&module| module.ident_strings().await)
104            .try_join()
105            .await?;
106        for idents in item_idents {
107            match idents {
108                IdentStrings::Single(ident) => hasher.write_value(ident),
109                IdentStrings::Multiple(idents) => {
110                    for ident in &idents {
111                        hasher.write_value(ident);
112                    }
113                }
114                IdentStrings::None => {}
115            }
116        }
117        Ok(Vc::cell(hasher.finish()))
118    }
119
120    #[turbo_tasks::function]
121    pub async fn get(&self, item: AvailableModuleItem) -> Result<Vc<bool>> {
122        if self.modules.connect().await?.contains(&item) {
123            return Ok(Vc::cell(true));
124        };
125        if let Some(parent) = self.parent {
126            return Ok(parent.get(item));
127        }
128        Ok(Vc::cell(false))
129    }
130
131    #[turbo_tasks::function]
132    pub async fn snapshot(&self) -> Result<Vc<AvailableModulesSnapshot>> {
133        let modules = self.modules.connect().await?;
134        let parent = if let Some(parent) = self.parent {
135            Some(parent.snapshot().await?)
136        } else {
137            None
138        };
139
140        Ok(AvailableModulesSnapshot { parent, modules }.cell())
141    }
142}
143
144#[turbo_tasks::value(serialization = "skip")]
145#[derive(Debug, Clone)]
146pub struct AvailableModulesSnapshot {
147    parent: Option<ReadRef<AvailableModulesSnapshot>>,
148    modules: ReadRef<AvailableModulesSet>,
149}
150
151impl AvailableModulesSnapshot {
152    pub fn get(&self, item: AvailableModuleItem) -> bool {
153        self.modules.contains(&item) || self.parent.as_ref().is_some_and(|parent| parent.get(item))
154    }
155}