next_core/next_server_utility/
server_utility_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_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        // This just exports another import
74        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}