next_core/next_server_utility/
server_utility_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_utility_reference::NextServerUtilityModuleReference;
28
29#[turbo_tasks::value(shared)]
30pub struct NextServerUtilityModule {
31    pub module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
32}
33
34#[turbo_tasks::value_impl]
35impl NextServerUtilityModule {
36    #[turbo_tasks::function]
37    pub fn new(module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>) -> Vc<Self> {
38        NextServerUtilityModule { 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 NextServerUtilityModule {
49    #[turbo_tasks::function]
50    fn ident(&self) -> Vc<AssetIdent> {
51        self.module
52            .ident()
53            .with_modifier(rcstr!("Next.js server utility"))
54    }
55
56    #[turbo_tasks::function]
57    async fn references(&self) -> Result<Vc<ModuleReferences>> {
58        Ok(Vc::cell(vec![ResolvedVc::upcast(
59            NextServerUtilityModuleReference::new(Vc::upcast(*self.module))
60                .to_resolved()
61                .await?,
62        )]))
63    }
64}
65
66#[turbo_tasks::value_impl]
67impl Asset for NextServerUtilityModule {
68    #[turbo_tasks::function]
69    fn content(&self) -> Result<Vc<AssetContent>> {
70        bail!("Next.js server utility module has no content")
71    }
72}
73
74#[turbo_tasks::value_impl]
75impl ChunkableModule for NextServerUtilityModule {
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 NextServerUtilityModule {
95    #[turbo_tasks::function]
96    async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
97        let module_reference = ResolvedVc::upcast(
98            NextServerUtilityModuleReference::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<NextServerUtilityModule>,
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                "{TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));",
141                StringifyJs(&module_id),
142            )
143            .into(),
144            ..Default::default()
145        }
146        .cell())
147    }
148}
149
150#[turbo_tasks::value_impl]
151impl ChunkItem for NextServerComponentChunkItem {
152    #[turbo_tasks::function]
153    fn asset_ident(&self) -> Vc<AssetIdent> {
154        self.inner.ident()
155    }
156
157    #[turbo_tasks::function]
158    fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
159        *self.chunking_context
160    }
161
162    #[turbo_tasks::function]
163    fn ty(&self) -> Vc<Box<dyn ChunkType>> {
164        Vc::upcast(Vc::<EcmascriptChunkType>::default())
165    }
166
167    #[turbo_tasks::function]
168    fn module(&self) -> Vc<Box<dyn Module>> {
169        Vc::upcast(*self.inner)
170    }
171}