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::FileSystemPath;
8use turbopack_core::{
9    chunk::{ChunkItem, ChunkType, ChunkableModule, ChunkingContext, ModuleChunkItemIdExt},
10    ident::AssetIdent,
11    module::{Module, ModuleSideEffects},
12    module_graph::ModuleGraph,
13    output::OutputAssetsReference,
14    reference::ModuleReferences,
15    source::OptionSource,
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    fn source(&self) -> Vc<OptionSource> {
58        Vc::cell(None)
59    }
60
61    #[turbo_tasks::function]
62    async fn references(&self) -> Result<Vc<ModuleReferences>> {
63        Ok(Vc::cell(vec![ResolvedVc::upcast(
64            NextServerUtilityModuleReference::new(Vc::upcast(*self.module))
65                .to_resolved()
66                .await?,
67        )]))
68    }
69
70    #[turbo_tasks::function]
71    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
72        // This just exports another import
73        ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
74    }
75}
76
77#[turbo_tasks::value_impl]
78impl ChunkableModule for NextServerUtilityModule {
79    #[turbo_tasks::function]
80    fn as_chunk_item(
81        self: ResolvedVc<Self>,
82        module_graph: ResolvedVc<ModuleGraph>,
83        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
84    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
85        Vc::upcast(
86            NextServerComponentChunkItem {
87                module_graph,
88                chunking_context,
89                inner: self,
90            }
91            .cell(),
92        )
93    }
94}
95
96#[turbo_tasks::value_impl]
97impl EcmascriptChunkPlaceable for NextServerUtilityModule {
98    #[turbo_tasks::function]
99    async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
100        let module_reference = ResolvedVc::upcast(
101            NextServerUtilityModuleReference::new(Vc::upcast(*self.module))
102                .to_resolved()
103                .await?,
104        );
105
106        let mut exports = BTreeMap::new();
107        let default = rcstr!("default");
108        exports.insert(
109            default.clone(),
110            EsmExport::ImportedBinding(module_reference, default, false),
111        );
112
113        Ok(EcmascriptExports::EsmExports(
114            EsmExports {
115                exports,
116                star_exports: vec![module_reference],
117            }
118            .resolved_cell(),
119        )
120        .cell())
121    }
122}
123
124#[turbo_tasks::value]
125struct NextServerComponentChunkItem {
126    module_graph: ResolvedVc<ModuleGraph>,
127    chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
128    inner: ResolvedVc<NextServerUtilityModule>,
129}
130
131#[turbo_tasks::value_impl]
132impl OutputAssetsReference for NextServerComponentChunkItem {}
133
134#[turbo_tasks::value_impl]
135impl EcmascriptChunkItem for NextServerComponentChunkItem {
136    #[turbo_tasks::function]
137    async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
138        let inner = self.inner.await?;
139
140        let module_id = inner.module.chunk_item_id(*self.chunking_context).await?;
141        Ok(EcmascriptChunkItemContent {
142            inner_code: formatdoc!(
143                "{TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));",
144                StringifyJs(&module_id),
145            )
146            .into(),
147            ..Default::default()
148        }
149        .cell())
150    }
151}
152
153#[turbo_tasks::value_impl]
154impl ChunkItem for NextServerComponentChunkItem {
155    #[turbo_tasks::function]
156    fn asset_ident(&self) -> Vc<AssetIdent> {
157        self.inner.ident()
158    }
159
160    #[turbo_tasks::function]
161    fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
162        *self.chunking_context
163    }
164
165    #[turbo_tasks::function]
166    fn ty(&self) -> Vc<Box<dyn ChunkType>> {
167        Vc::upcast(Vc::<EcmascriptChunkType>::default())
168    }
169
170    #[turbo_tasks::function]
171    fn module(&self) -> Vc<Box<dyn Module>> {
172        Vc::upcast(*self.inner)
173    }
174}