Skip to main content

next_core/next_server_component/
server_component_transition.rs

1use anyhow::{Result, bail};
2use turbo_tasks::{ResolvedVc, Vc};
3use turbopack::{ModuleAssetContext, transition::Transition};
4use turbopack_core::{
5    context::{AssetContext, ProcessResult},
6    reference_type::ReferenceType,
7    source::Source,
8};
9use turbopack_ecmascript::chunk::EcmascriptChunkPlaceable;
10
11use super::server_component_module::NextServerComponentModule;
12
13/// This transition wraps a module into a marker
14/// [`Vc<NextServerComponentModule>`].
15///
16/// When walking the module graph to build the client reference manifest, this
17/// is used to determine under which server component CSS client references are
18/// required. Ultimately, this tells Next.js what CSS to inject into the page.
19#[turbo_tasks::value(shared)]
20pub struct NextServerComponentTransition {}
21
22#[turbo_tasks::value_impl]
23impl NextServerComponentTransition {
24    /// Creates a new [`Vc<NextServerComponentTransition>`].
25    #[turbo_tasks::function]
26    pub fn new() -> Vc<Self> {
27        NextServerComponentTransition {}.cell()
28    }
29}
30
31#[turbo_tasks::value_impl]
32impl Transition for NextServerComponentTransition {
33    /// Override process to capture the original source path before transformation.
34    /// This is important for MDX files where page.mdx becomes page.mdx.tsx after
35    /// transformation, but we need the original path for manifest key generation.
36    #[turbo_tasks::function]
37    async fn process(
38        self: Vc<Self>,
39        source: Vc<Box<dyn Source>>,
40        module_asset_context: Vc<ModuleAssetContext>,
41        reference_type: ReferenceType,
42    ) -> Result<Vc<ProcessResult>> {
43        // Capture the original source path before any transformation
44        let source_path = source.ident().path().owned().await?;
45
46        let source = self.process_source(source);
47        let module_asset_context = self.process_context(module_asset_context);
48
49        Ok(
50            match &*module_asset_context.process(source, reference_type).await? {
51                ProcessResult::Module(module) => {
52                    let Some(module) =
53                        ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(*module)
54                    else {
55                        bail!("not an ecmascript module");
56                    };
57
58                    // Create the server component module with the original source path
59                    let server_component = NextServerComponentModule::new(*module, source_path);
60
61                    ProcessResult::Module(ResolvedVc::upcast(server_component.to_resolved().await?))
62                        .cell()
63                }
64                ProcessResult::Unknown(source) => ProcessResult::Unknown(*source).cell(),
65                ProcessResult::Ignore => ProcessResult::Ignore.cell(),
66            },
67        )
68    }
69}