next_core/next_server_component/
server_component_module.rs1use 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 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}