next_core/next_dynamic/
dynamic_transition.rs

1use anyhow::{Result, bail};
2use turbo_rcstr::RcStr;
3use turbo_tasks::{ResolvedVc, Value, Vc};
4use turbopack::{ModuleAssetContext, transition::Transition};
5use turbopack_core::{
6    context::{AssetContext, ProcessResult},
7    reference_type::ReferenceType,
8    source::Source,
9};
10use turbopack_ecmascript::chunk::EcmascriptChunkPlaceable;
11
12use super::NextDynamicEntryModule;
13
14/// This transition is used to create the marker asset for a next/dynamic
15/// import. Optionally, it can also apply another transition (i.e. to the client context).
16///
17/// This will get picked up during module processing and will be used to
18/// create the dynamic entry, and the dynamic manifest entry.
19#[turbo_tasks::value]
20pub struct NextDynamicTransition {
21    client_transition: Option<ResolvedVc<Box<dyn Transition>>>,
22}
23
24#[turbo_tasks::value_impl]
25impl NextDynamicTransition {
26    /// Create a transition that only add a marker `NextDynamicEntryModule`.
27    #[turbo_tasks::function]
28    pub fn new_marker() -> Vc<Self> {
29        NextDynamicTransition {
30            client_transition: None,
31        }
32        .cell()
33    }
34
35    /// Create a transition that applies `client_transiton` and adds a marker
36    /// `NextDynamicEntryModule`.
37    #[turbo_tasks::function]
38    pub fn new_client(client_transition: ResolvedVc<Box<dyn Transition>>) -> Vc<Self> {
39        NextDynamicTransition {
40            client_transition: Some(client_transition),
41        }
42        .cell()
43    }
44}
45
46#[turbo_tasks::value_impl]
47impl Transition for NextDynamicTransition {
48    #[turbo_tasks::function]
49    fn process_layer(self: Vc<Self>, layer: Vc<RcStr>) -> Vc<RcStr> {
50        layer
51    }
52
53    #[turbo_tasks::function]
54    async fn process(
55        self: Vc<Self>,
56        source: Vc<Box<dyn Source>>,
57        module_asset_context: Vc<ModuleAssetContext>,
58        _reference_type: Value<ReferenceType>,
59    ) -> Result<Vc<ProcessResult>> {
60        let module_asset_context = self.process_context(module_asset_context);
61        let module = match self.await?.client_transition {
62            Some(client_transition) => client_transition.process(
63                source,
64                module_asset_context,
65                Value::new(ReferenceType::Undefined),
66            ),
67            None => module_asset_context.process(source, Value::new(ReferenceType::Undefined)),
68        };
69
70        Ok(match &*module.try_into_module().await? {
71            Some(client_module) => {
72                let Some(client_module) =
73                    ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(*client_module)
74                else {
75                    bail!("not an ecmascript client_module");
76                };
77
78                ProcessResult::Module(ResolvedVc::upcast(
79                    NextDynamicEntryModule::new(*client_module)
80                        .to_resolved()
81                        .await?,
82                ))
83            }
84            None => ProcessResult::Ignore,
85        }
86        .cell())
87    }
88}