next_core/next_server_component/
server_component_module.rs

1use std::collections::BTreeMap;
2
3use anyhow::Result;
4use indoc::formatdoc;
5use turbo_rcstr::rcstr;
6use turbo_tasks::{ResolvedVc, Vc};
7use turbo_tasks_fs::{FileContent, FileSystemPath};
8use turbopack_core::{
9    asset::{Asset, AssetContent},
10    chunk::{ChunkItem, ChunkType, ChunkableModule, ChunkingContext, ModuleChunkItemIdExt},
11    ident::AssetIdent,
12    module::{Module, ModuleSideEffects},
13    module_graph::ModuleGraph,
14    output::OutputAssetsReference,
15    reference::ModuleReferences,
16    source::OptionSource,
17};
18use turbopack_ecmascript::{
19    chunk::{
20        EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable,
21        EcmascriptChunkType, EcmascriptExports,
22    },
23    references::esm::{EsmExport, EsmExports},
24    runtime_functions::{TURBOPACK_EXPORT_NAMESPACE, TURBOPACK_IMPORT},
25    utils::StringifyJs,
26};
27
28use super::server_component_reference::NextServerComponentModuleReference;
29
30#[turbo_tasks::value(shared)]
31pub struct NextServerComponentModule {
32    pub module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
33}
34
35#[turbo_tasks::value_impl]
36impl NextServerComponentModule {
37    #[turbo_tasks::function]
38    pub fn new(module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>) -> Vc<Self> {
39        NextServerComponentModule { module }.cell()
40    }
41
42    #[turbo_tasks::function]
43    pub fn server_path(&self) -> Vc<FileSystemPath> {
44        self.module.ident().path()
45    }
46}
47
48#[turbo_tasks::value_impl]
49impl Module for NextServerComponentModule {
50    #[turbo_tasks::function]
51    fn ident(&self) -> Vc<AssetIdent> {
52        self.module
53            .ident()
54            .with_modifier(rcstr!("Next.js Server Component"))
55    }
56
57    #[turbo_tasks::function]
58    fn source(&self) -> Vc<OptionSource> {
59        Vc::cell(None)
60    }
61
62    #[turbo_tasks::function]
63    async fn references(&self) -> Result<Vc<ModuleReferences>> {
64        Ok(Vc::cell(vec![ResolvedVc::upcast(
65            NextServerComponentModuleReference::new(Vc::upcast(*self.module))
66                .to_resolved()
67                .await?,
68        )]))
69    }
70    #[turbo_tasks::function]
71    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
72        // This just exports another import
73        ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
74    }
75}
76
77#[turbo_tasks::value_impl]
78impl Asset for NextServerComponentModule {
79    #[turbo_tasks::function]
80    fn content(&self) -> Vc<AssetContent> {
81        AssetContent::File(
82            FileContent::Content(
83                "// This is a marker module for Next.js server components.".into(),
84            )
85            .resolved_cell(),
86        )
87        .cell()
88    }
89}
90
91#[turbo_tasks::value_impl]
92impl ChunkableModule for NextServerComponentModule {
93    #[turbo_tasks::function]
94    fn as_chunk_item(
95        self: ResolvedVc<Self>,
96        module_graph: ResolvedVc<ModuleGraph>,
97        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
98    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
99        Vc::upcast(
100            NextServerComponentChunkItem {
101                module_graph,
102                chunking_context,
103                inner: self,
104            }
105            .cell(),
106        )
107    }
108}
109
110#[turbo_tasks::value_impl]
111impl EcmascriptChunkPlaceable for NextServerComponentModule {
112    #[turbo_tasks::function]
113    async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
114        let module_reference = ResolvedVc::upcast(
115            NextServerComponentModuleReference::new(Vc::upcast(*self.module))
116                .to_resolved()
117                .await?,
118        );
119
120        let mut exports = BTreeMap::new();
121        let default = rcstr!("default");
122        exports.insert(
123            default.clone(),
124            EsmExport::ImportedBinding(module_reference, default, false),
125        );
126
127        Ok(EcmascriptExports::EsmExports(
128            EsmExports {
129                exports,
130                star_exports: vec![module_reference],
131            }
132            .resolved_cell(),
133        )
134        .cell())
135    }
136}
137
138#[turbo_tasks::value]
139struct NextServerComponentChunkItem {
140    module_graph: ResolvedVc<ModuleGraph>,
141    chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
142    inner: ResolvedVc<NextServerComponentModule>,
143}
144
145#[turbo_tasks::value_impl]
146impl OutputAssetsReference for NextServerComponentChunkItem {}
147
148#[turbo_tasks::value_impl]
149impl EcmascriptChunkItem for NextServerComponentChunkItem {
150    #[turbo_tasks::function]
151    async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
152        let inner = self.inner.await?;
153
154        let module_id = inner.module.chunk_item_id(*self.chunking_context).await?;
155        Ok(EcmascriptChunkItemContent {
156            inner_code: formatdoc!(
157                r#"
158                    {TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));
159                "#,
160                StringifyJs(&module_id),
161            )
162            .into(),
163            ..Default::default()
164        }
165        .cell())
166    }
167}
168
169#[turbo_tasks::value_impl]
170impl ChunkItem for NextServerComponentChunkItem {
171    #[turbo_tasks::function]
172    fn asset_ident(&self) -> Vc<AssetIdent> {
173        self.inner.ident()
174    }
175
176    #[turbo_tasks::function]
177    fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
178        *self.chunking_context
179    }
180
181    #[turbo_tasks::function]
182    fn ty(&self) -> Vc<Box<dyn ChunkType>> {
183        Vc::upcast(Vc::<EcmascriptChunkType>::default())
184    }
185
186    #[turbo_tasks::function]
187    fn module(&self) -> Vc<Box<dyn Module>> {
188        Vc::upcast(*self.inner)
189    }
190}