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