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