turbopack/transition/
mod.rs

1pub(crate) mod full_context_transition;
2
3use anyhow::Result;
4pub use full_context_transition::FullContextTransition;
5use rustc_hash::FxHashMap;
6use turbo_rcstr::RcStr;
7use turbo_tasks::{ResolvedVc, Value, ValueDefault, Vc};
8use turbopack_core::{
9    compile_time_info::CompileTimeInfo, context::ProcessResult, module::Module,
10    reference_type::ReferenceType, source::Source,
11};
12use turbopack_resolve::resolve_options_context::ResolveOptionsContext;
13
14use crate::{
15    ModuleAssetContext,
16    module_options::{ModuleOptionsContext, transition_rule::TransitionRule},
17};
18
19/// Some kind of operation that is executed during reference processing. e. g.
20/// you can transition to a different environment on a specific import
21/// (reference).
22#[turbo_tasks::value_trait]
23pub trait Transition {
24    /// Apply modifications/wrapping to the source asset
25    fn process_source(self: Vc<Self>, asset: Vc<Box<dyn Source>>) -> Vc<Box<dyn Source>> {
26        asset
27    }
28
29    /// Apply modifications to the compile-time information
30    fn process_compile_time_info(
31        self: Vc<Self>,
32        compile_time_info: Vc<CompileTimeInfo>,
33    ) -> Vc<CompileTimeInfo> {
34        compile_time_info
35    }
36
37    /// Apply modifications to the layer
38    fn process_layer(self: Vc<Self>, layer: Vc<RcStr>) -> Vc<RcStr> {
39        layer
40    }
41
42    /// Apply modifications/wrapping to the module options context
43    fn process_module_options_context(
44        self: Vc<Self>,
45        module_options_context: Vc<ModuleOptionsContext>,
46    ) -> Vc<ModuleOptionsContext> {
47        module_options_context
48    }
49
50    /// Apply modifications/wrapping to the resolve options context
51    fn process_resolve_options_context(
52        self: Vc<Self>,
53        resolve_options_context: Vc<ResolveOptionsContext>,
54    ) -> Vc<ResolveOptionsContext> {
55        resolve_options_context
56    }
57
58    /// Apply modifications/wrapping to the transition options
59    fn process_transition_options(
60        self: Vc<Self>,
61        transition_options: Vc<TransitionOptions>,
62    ) -> Vc<TransitionOptions> {
63        transition_options
64    }
65
66    /// Apply modifications/wrapping to the final asset
67    fn process_module(
68        self: Vc<Self>,
69        module: Vc<Box<dyn Module>>,
70        _context: Vc<ModuleAssetContext>,
71    ) -> Vc<Box<dyn Module>> {
72        module
73    }
74
75    /// Apply modifications to the context
76    async fn process_context(
77        self: Vc<Self>,
78        module_asset_context: Vc<ModuleAssetContext>,
79    ) -> Result<Vc<ModuleAssetContext>> {
80        let module_asset_context = module_asset_context.await?;
81        let compile_time_info =
82            self.process_compile_time_info(*module_asset_context.compile_time_info);
83        let module_options_context =
84            self.process_module_options_context(*module_asset_context.module_options_context);
85        let resolve_options_context =
86            self.process_resolve_options_context(*module_asset_context.resolve_options_context);
87        let layer = self.process_layer(*module_asset_context.layer);
88        let transition_options = self.process_transition_options(*module_asset_context.transitions);
89        let module_asset_context = ModuleAssetContext::new(
90            transition_options,
91            compile_time_info,
92            module_options_context,
93            resolve_options_context,
94            layer,
95        );
96        Ok(module_asset_context)
97    }
98
99    /// Apply modification on the processing of the asset
100    async fn process(
101        self: Vc<Self>,
102        asset: Vc<Box<dyn Source>>,
103        module_asset_context: Vc<ModuleAssetContext>,
104        reference_type: Value<ReferenceType>,
105    ) -> Result<Vc<ProcessResult>> {
106        let asset = self.process_source(asset);
107        let module_asset_context = self.process_context(module_asset_context);
108        let asset = asset.to_resolved().await?;
109
110        Ok(match &*module_asset_context
111            .process_default(asset, reference_type)
112            .await?
113            .await?
114        {
115            ProcessResult::Module(m) => ProcessResult::Module(
116                self.process_module(**m, module_asset_context)
117                    .to_resolved()
118                    .await?,
119            ),
120            ProcessResult::Unknown(source) => ProcessResult::Unknown(*source),
121            ProcessResult::Ignore => ProcessResult::Ignore,
122        }
123        .cell())
124    }
125}
126
127#[turbo_tasks::value(shared)]
128#[derive(Default)]
129pub struct TransitionOptions {
130    pub named_transitions: FxHashMap<RcStr, ResolvedVc<Box<dyn Transition>>>,
131    pub transition_rules: Vec<TransitionRule>,
132    pub placeholder_for_future_extensions: (),
133}
134
135#[turbo_tasks::value_impl]
136impl ValueDefault for TransitionOptions {
137    #[turbo_tasks::function]
138    fn value_default() -> Vc<Self> {
139        Self::default().cell()
140    }
141}
142
143impl TransitionOptions {
144    pub fn get_named(&self, name: RcStr) -> Option<ResolvedVc<Box<dyn Transition>>> {
145        self.named_transitions.get(&name).copied()
146    }
147
148    pub async fn get_by_rules(
149        &self,
150        source: ResolvedVc<Box<dyn Source>>,
151        reference_type: &ReferenceType,
152    ) -> Result<Option<ResolvedVc<Box<dyn Transition>>>> {
153        if self.transition_rules.is_empty() {
154            return Ok(None);
155        }
156        let path = &*source.ident().path().await?;
157        for rule in &self.transition_rules {
158            if rule.matches(source, path, reference_type).await? {
159                return Ok(Some(rule.transition()));
160            }
161        }
162        Ok(None)
163    }
164}