next_core/next_dynamic/
dynamic_module.rs

1use 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/// A [`NextDynamicEntryModule`] is a marker asset used to indicate which
30/// dynamic assets should appear in the dynamic manifest.
31#[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        // This just exports another import
77        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}