next_core/next_server_utility/
server_utility_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_utility_reference::NextServerUtilityModuleReference;
29
30#[turbo_tasks::value(shared)]
31pub struct NextServerUtilityModule {
32 pub module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
33}
34
35#[turbo_tasks::value_impl]
36impl NextServerUtilityModule {
37 #[turbo_tasks::function]
38 pub fn new(module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>) -> Vc<Self> {
39 NextServerUtilityModule { 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 NextServerUtilityModule {
50 #[turbo_tasks::function]
51 fn ident(&self) -> Vc<AssetIdent> {
52 self.module
53 .ident()
54 .with_modifier(rcstr!("Next.js server utility"))
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 NextServerUtilityModuleReference::new(Vc::upcast(*self.module))
66 .to_resolved()
67 .await?,
68 )]))
69 }
70
71 #[turbo_tasks::function]
72 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
73 ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
75 }
76}
77
78#[turbo_tasks::value_impl]
79impl Asset for NextServerUtilityModule {
80 #[turbo_tasks::function]
81 fn content(&self) -> Vc<AssetContent> {
82 AssetContent::File(
83 FileContent::Content("// This is a marker module for Next.js server utilities.".into())
84 .resolved_cell(),
85 )
86 .cell()
87 }
88}
89
90#[turbo_tasks::value_impl]
91impl ChunkableModule for NextServerUtilityModule {
92 #[turbo_tasks::function]
93 fn as_chunk_item(
94 self: ResolvedVc<Self>,
95 module_graph: ResolvedVc<ModuleGraph>,
96 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
97 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
98 Vc::upcast(
99 NextServerComponentChunkItem {
100 module_graph,
101 chunking_context,
102 inner: self,
103 }
104 .cell(),
105 )
106 }
107}
108
109#[turbo_tasks::value_impl]
110impl EcmascriptChunkPlaceable for NextServerUtilityModule {
111 #[turbo_tasks::function]
112 async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
113 let module_reference = ResolvedVc::upcast(
114 NextServerUtilityModuleReference::new(Vc::upcast(*self.module))
115 .to_resolved()
116 .await?,
117 );
118
119 let mut exports = BTreeMap::new();
120 let default = rcstr!("default");
121 exports.insert(
122 default.clone(),
123 EsmExport::ImportedBinding(module_reference, default, false),
124 );
125
126 Ok(EcmascriptExports::EsmExports(
127 EsmExports {
128 exports,
129 star_exports: vec![module_reference],
130 }
131 .resolved_cell(),
132 )
133 .cell())
134 }
135}
136
137#[turbo_tasks::value]
138struct NextServerComponentChunkItem {
139 module_graph: ResolvedVc<ModuleGraph>,
140 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
141 inner: ResolvedVc<NextServerUtilityModule>,
142}
143
144#[turbo_tasks::value_impl]
145impl OutputAssetsReference for NextServerComponentChunkItem {}
146
147#[turbo_tasks::value_impl]
148impl EcmascriptChunkItem for NextServerComponentChunkItem {
149 #[turbo_tasks::function]
150 async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
151 let inner = self.inner.await?;
152
153 let module_id = inner.module.chunk_item_id(*self.chunking_context).await?;
154 Ok(EcmascriptChunkItemContent {
155 inner_code: formatdoc!(
156 "{TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));",
157 StringifyJs(&module_id),
158 )
159 .into(),
160 ..Default::default()
161 }
162 .cell())
163 }
164}
165
166#[turbo_tasks::value_impl]
167impl ChunkItem for NextServerComponentChunkItem {
168 #[turbo_tasks::function]
169 fn asset_ident(&self) -> Vc<AssetIdent> {
170 self.inner.ident()
171 }
172
173 #[turbo_tasks::function]
174 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
175 *self.chunking_context
176 }
177
178 #[turbo_tasks::function]
179 fn ty(&self) -> Vc<Box<dyn ChunkType>> {
180 Vc::upcast(Vc::<EcmascriptChunkType>::default())
181 }
182
183 #[turbo_tasks::function]
184 fn module(&self) -> Vc<Box<dyn Module>> {
185 Vc::upcast(*self.inner)
186 }
187}