next_core/next_dynamic/
dynamic_module.rs1use std::collections::BTreeMap;
2
3use anyhow::Result;
4use indoc::formatdoc;
5use turbo_rcstr::{RcStr, rcstr};
6use turbo_tasks::{ResolvedVc, Vc};
7use turbo_tasks_fs::FileContent;
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, SingleChunkableModuleReference},
16 resolve::ExportUsage,
17 source::OptionSource,
18};
19use turbopack_ecmascript::{
20 chunk::{
21 EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable,
22 EcmascriptChunkType, EcmascriptExports,
23 },
24 references::esm::{EsmExport, EsmExports},
25 runtime_functions::{TURBOPACK_EXPORT_NAMESPACE, TURBOPACK_IMPORT},
26 utils::StringifyJs,
27};
28
29#[turbo_tasks::value(shared)]
32pub struct NextDynamicEntryModule {
33 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
34}
35
36#[turbo_tasks::value_impl]
37impl NextDynamicEntryModule {
38 #[turbo_tasks::function]
39 pub fn new(module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>) -> Vc<Self> {
40 NextDynamicEntryModule { module }.cell()
41 }
42}
43
44fn dynamic_ref_description() -> RcStr {
45 rcstr!("next/dynamic reference")
46}
47
48#[turbo_tasks::value_impl]
49impl Module for NextDynamicEntryModule {
50 #[turbo_tasks::function]
51 fn ident(&self) -> Vc<AssetIdent> {
52 self.module
53 .ident()
54 .with_modifier(rcstr!("next/dynamic entry"))
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 SingleChunkableModuleReference::new(
66 Vc::upcast(*self.module),
67 dynamic_ref_description(),
68 ExportUsage::all(),
69 )
70 .to_resolved()
71 .await?,
72 )]))
73 }
74 #[turbo_tasks::function]
75 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
76 ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
78 }
79}
80
81#[turbo_tasks::value_impl]
82impl Asset for NextDynamicEntryModule {
83 #[turbo_tasks::function]
84 fn content(&self) -> Vc<AssetContent> {
85 AssetContent::File(
86 FileContent::Content("// This is a marker module for Next.js dynamic.".into())
87 .resolved_cell(),
88 )
89 .cell()
90 }
91}
92
93#[turbo_tasks::value_impl]
94impl ChunkableModule for NextDynamicEntryModule {
95 #[turbo_tasks::function]
96 fn as_chunk_item(
97 self: ResolvedVc<Self>,
98 module_graph: ResolvedVc<ModuleGraph>,
99 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
100 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
101 Vc::upcast(
102 NextDynamicEntryChunkItem {
103 chunking_context,
104 module_graph,
105 inner: self,
106 }
107 .cell(),
108 )
109 }
110}
111
112#[turbo_tasks::value_impl]
113impl EcmascriptChunkPlaceable for NextDynamicEntryModule {
114 #[turbo_tasks::function]
115 async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
116 let module_reference = ResolvedVc::upcast(
117 SingleChunkableModuleReference::new(
118 Vc::upcast(*self.module),
119 dynamic_ref_description(),
120 ExportUsage::all(),
121 )
122 .to_resolved()
123 .await?,
124 );
125
126 let mut exports = BTreeMap::new();
127 let default = rcstr!("default");
128 exports.insert(
129 default.clone(),
130 EsmExport::ImportedBinding(module_reference, default, false),
131 );
132
133 Ok(EcmascriptExports::EsmExports(
134 EsmExports {
135 exports,
136 star_exports: vec![module_reference],
137 }
138 .resolved_cell(),
139 )
140 .cell())
141 }
142}
143
144#[turbo_tasks::value]
145struct NextDynamicEntryChunkItem {
146 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
147 module_graph: ResolvedVc<ModuleGraph>,
148 inner: ResolvedVc<NextDynamicEntryModule>,
149}
150
151#[turbo_tasks::value_impl]
152impl OutputAssetsReference for NextDynamicEntryChunkItem {}
153
154#[turbo_tasks::value_impl]
155impl EcmascriptChunkItem for NextDynamicEntryChunkItem {
156 #[turbo_tasks::function]
157 async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
158 let inner = self.inner.await?;
159
160 let module_id = inner.module.chunk_item_id(*self.chunking_context).await?;
161 Ok(EcmascriptChunkItemContent {
162 inner_code: formatdoc!(
163 r#"
164 {TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));
165 "#,
166 StringifyJs(&module_id),
167 )
168 .into(),
169 ..Default::default()
170 }
171 .cell())
172 }
173}
174
175#[turbo_tasks::value_impl]
176impl ChunkItem for NextDynamicEntryChunkItem {
177 #[turbo_tasks::function]
178 fn asset_ident(&self) -> Vc<AssetIdent> {
179 self.inner.ident()
180 }
181
182 #[turbo_tasks::function]
183 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
184 *self.chunking_context
185 }
186
187 #[turbo_tasks::function]
188 fn ty(&self) -> Vc<Box<dyn ChunkType>> {
189 Vc::upcast(Vc::<EcmascriptChunkType>::default())
190 }
191
192 #[turbo_tasks::function]
193 fn module(&self) -> Vc<Box<dyn Module>> {
194 Vc::upcast(*self.inner)
195 }
196}