next_core/next_server_component/
server_component_module.rs

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