Skip to main content

turbopack_core/chunk/
available_modules.rs

1use anyhow::Result;
2use bincode::{Decode, Encode};
3use turbo_tasks::{
4    FxIndexSet, OperationVc, ReadRef, ResolvedVc, TryJoinIterExt, ValueToString, Vc,
5    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#[turbo_tasks::task_input]
16#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, TraceRawVcs, Encode, Decode)]
17pub enum AvailableModuleItem {
18    Module(ResolvedVc<Box<dyn ChunkableModule>>),
19    Batch(ResolvedVc<ModuleBatch>),
20    AsyncLoader(ResolvedVc<Box<dyn ChunkableModule>>),
21}
22
23impl AvailableModuleItem {
24    pub async fn ident_strings(&self) -> Result<IdentStrings> {
25        Ok(match self {
26            AvailableModuleItem::Module(module) => {
27                IdentStrings::Single(module.ident().to_string().owned().await?)
28            }
29            AvailableModuleItem::Batch(batch) => {
30                IdentStrings::Multiple(batch.ident_strings().await?)
31            }
32            AvailableModuleItem::AsyncLoader(module) => {
33                IdentStrings::Single(turbofmt!("async loader {}", module.ident()).await?)
34            }
35        })
36    }
37}
38
39impl From<ChunkableModuleOrBatch> for AvailableModuleItem {
40    fn from(value: ChunkableModuleOrBatch) -> Self {
41        match value {
42            ChunkableModuleOrBatch::Module(module) => AvailableModuleItem::Module(module),
43            ChunkableModuleOrBatch::Batch(batch) => AvailableModuleItem::Batch(batch),
44            ChunkableModuleOrBatch::None(id) => {
45                panic!("Cannot create AvailableModuleItem from None({})", id)
46            }
47        }
48    }
49}
50
51#[turbo_tasks::value(transparent)]
52#[derive(Debug, Clone)]
53pub struct AvailableModulesSet(
54    #[bincode(with = "turbo_bincode::indexset")] FxIndexSet<AvailableModuleItem>,
55);
56
57/// Allows to gather information about which assets are already available.
58/// Adding more roots will form a linked list like structure to allow caching
59/// `include` queries.
60#[turbo_tasks::value]
61pub struct AvailableModules {
62    parent: Option<ResolvedVc<AvailableModules>>,
63    modules: OperationVc<AvailableModulesSet>,
64}
65
66#[turbo_tasks::value_impl]
67impl AvailableModules {
68    #[turbo_tasks::function]
69    pub fn new(modules: OperationVc<AvailableModulesSet>) -> Vc<Self> {
70        AvailableModules {
71            parent: None,
72            modules,
73        }
74        .cell()
75    }
76
77    #[turbo_tasks::function]
78    pub fn with_modules(
79        self: ResolvedVc<Self>,
80        modules: OperationVc<AvailableModulesSet>,
81    ) -> Result<Vc<Self>> {
82        Ok(AvailableModules {
83            parent: Some(self),
84            modules,
85        }
86        .cell())
87    }
88
89    #[turbo_tasks::function]
90    pub async fn hash(&self) -> Result<Vc<u64>> {
91        let mut hasher = Xxh3Hash64Hasher::new();
92        if let Some(parent) = self.parent {
93            hasher.write_value(parent.hash().await?);
94        } else {
95            hasher.write_value(0u64);
96        }
97        let item_idents = self
98            .modules
99            .connect()
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.connect().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.connect().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 = "skip")]
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}