Skip to main content

next_core/next_server_component/
server_component_module.rs

1use anyhow::Result;
2use indoc::formatdoc;
3use turbo_rcstr::rcstr;
4use turbo_tasks::{ResolvedVc, Vc};
5use turbo_tasks_fs::FileSystemPath;
6use turbopack_core::{
7    chunk::{AsyncModuleInfo, ChunkableModule, ChunkingContext, ModuleChunkItemIdExt},
8    ident::AssetIdent,
9    module::{Module, ModuleSideEffects},
10    module_graph::ModuleGraph,
11    reference::{ModuleReference, ModuleReferences, SingleChunkableModuleReference},
12    resolve::ExportUsage,
13    source::OptionSource,
14};
15use turbopack_ecmascript::{
16    chunk::{
17        EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
18        ecmascript_chunk_item,
19    },
20    references::esm::EsmExports,
21    runtime_functions::{TURBOPACK_EXPORT_NAMESPACE, TURBOPACK_IMPORT},
22    utils::StringifyJs,
23};
24
25#[turbo_tasks::value(shared)]
26pub struct NextServerComponentModule {
27    pub module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
28    /// The original source path before any transformations (e.g., page.mdx before it becomes
29    /// page.mdx.tsx). This is used to generate consistent manifest keys that match what the
30    /// LoaderTree stores.
31    source_path: FileSystemPath,
32}
33
34#[turbo_tasks::value_impl]
35impl NextServerComponentModule {
36    #[turbo_tasks::function]
37    pub fn new(
38        module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
39        source_path: FileSystemPath,
40    ) -> Vc<Self> {
41        NextServerComponentModule {
42            module,
43            source_path,
44        }
45        .cell()
46    }
47
48    /// Returns the original source path (before transformations like MDX -> MDX.tsx).
49    /// Use this for manifest key generation to match the LoaderTree paths.
50    #[turbo_tasks::function]
51    pub fn source_path(&self) -> Vc<FileSystemPath> {
52        self.source_path.clone().cell()
53    }
54
55    /// Returns the transformed module path (e.g., page.mdx.tsx for MDX files).
56    /// This is the path of the actual compiled module.
57    #[turbo_tasks::function]
58    pub fn server_path(&self) -> Vc<FileSystemPath> {
59        self.module.ident().path()
60    }
61}
62
63impl NextServerComponentModule {
64    fn module_reference(&self) -> Vc<Box<dyn ModuleReference>> {
65        Vc::upcast(SingleChunkableModuleReference::new(
66            Vc::upcast(*self.module),
67            rcstr!("Next.js Server Component"),
68            ExportUsage::all(),
69        ))
70    }
71}
72
73#[turbo_tasks::value_impl]
74impl Module for NextServerComponentModule {
75    #[turbo_tasks::function]
76    fn ident(&self) -> Vc<AssetIdent> {
77        self.module
78            .ident()
79            .with_modifier(rcstr!("Next.js Server Component"))
80    }
81
82    #[turbo_tasks::function]
83    fn source(&self) -> Vc<OptionSource> {
84        Vc::cell(None)
85    }
86
87    #[turbo_tasks::function]
88    async fn references(&self) -> Result<Vc<ModuleReferences>> {
89        Ok(Vc::cell(vec![self.module_reference().to_resolved().await?]))
90    }
91
92    #[turbo_tasks::function]
93    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
94        // This just exports another import
95        ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
96    }
97}
98
99#[turbo_tasks::value_impl]
100impl ChunkableModule for NextServerComponentModule {
101    #[turbo_tasks::function]
102    fn as_chunk_item(
103        self: ResolvedVc<Self>,
104        module_graph: ResolvedVc<ModuleGraph>,
105        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
106    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
107        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
108    }
109}
110
111#[turbo_tasks::value_impl]
112impl EcmascriptChunkPlaceable for NextServerComponentModule {
113    #[turbo_tasks::function]
114    fn get_exports(&self) -> Vc<EcmascriptExports> {
115        let module_reference = self.module_reference();
116        EsmExports::reexport_including_default(module_reference)
117    }
118
119    #[turbo_tasks::function]
120    async fn chunk_item_content(
121        &self,
122        chunking_context: Vc<Box<dyn ChunkingContext>>,
123        _module_graph: Vc<ModuleGraph>,
124        _async_module_info: Option<Vc<AsyncModuleInfo>>,
125        _estimated: bool,
126    ) -> Result<Vc<EcmascriptChunkItemContent>> {
127        let module_id = self.module.chunk_item_id(chunking_context).await?;
128        Ok(EcmascriptChunkItemContent {
129            inner_code: formatdoc!(
130                r#"
131                    {TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));
132                "#,
133                StringifyJs(&module_id),
134            )
135            .into(),
136            ..Default::default()
137        }
138        .cell())
139    }
140}