Skip to main content

next_core/next_dynamic/
dynamic_module.rs

1use anyhow::Result;
2use indoc::formatdoc;
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{ResolvedVc, Vc};
5use turbopack_core::{
6    chunk::{AsyncModuleInfo, ChunkableModule, ChunkingContext, ModuleChunkItemIdExt},
7    ident::AssetIdent,
8    module::{Module, ModuleSideEffects},
9    module_graph::ModuleGraph,
10    reference::{ModuleReference, ModuleReferences, SingleChunkableModuleReference},
11    resolve::ExportUsage,
12    source::OptionSource,
13};
14use turbopack_ecmascript::{
15    chunk::{
16        EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
17        ecmascript_chunk_item,
18    },
19    references::esm::EsmExports,
20    runtime_functions::{TURBOPACK_EXPORT_NAMESPACE, TURBOPACK_IMPORT},
21    utils::StringifyJs,
22};
23
24/// A [`NextDynamicEntryModule`] is a marker asset used to indicate which
25/// dynamic assets should appear in the dynamic manifest.
26#[turbo_tasks::value(shared)]
27pub struct NextDynamicEntryModule {
28    module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
29}
30
31#[turbo_tasks::value_impl]
32impl NextDynamicEntryModule {
33    #[turbo_tasks::function]
34    pub fn new(module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>) -> Vc<Self> {
35        NextDynamicEntryModule { module }.cell()
36    }
37}
38
39fn dynamic_ref_description() -> RcStr {
40    rcstr!("next/dynamic reference")
41}
42
43impl NextDynamicEntryModule {
44    async fn module_reference(&self) -> Result<ResolvedVc<Box<dyn ModuleReference>>> {
45        Ok(ResolvedVc::upcast(
46            SingleChunkableModuleReference::new(
47                Vc::upcast(*self.module),
48                dynamic_ref_description(),
49                ExportUsage::all(),
50            )
51            .to_resolved()
52            .await?,
53        ))
54    }
55}
56
57#[turbo_tasks::value_impl]
58impl Module for NextDynamicEntryModule {
59    #[turbo_tasks::function]
60    fn ident(&self) -> Vc<AssetIdent> {
61        self.module
62            .ident()
63            .with_modifier(rcstr!("next/dynamic entry"))
64    }
65
66    #[turbo_tasks::function]
67    fn source(&self) -> Vc<OptionSource> {
68        Vc::cell(None)
69    }
70
71    #[turbo_tasks::function]
72    async fn references(&self) -> Result<Vc<ModuleReferences>> {
73        Ok(Vc::cell(vec![self.module_reference().await?]))
74    }
75    #[turbo_tasks::function]
76    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
77        // This just exports another import
78        ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
79    }
80}
81
82#[turbo_tasks::value_impl]
83impl ChunkableModule for NextDynamicEntryModule {
84    #[turbo_tasks::function]
85    fn as_chunk_item(
86        self: ResolvedVc<Self>,
87        module_graph: ResolvedVc<ModuleGraph>,
88        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
89    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
90        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
91    }
92}
93
94#[turbo_tasks::value_impl]
95impl EcmascriptChunkPlaceable for NextDynamicEntryModule {
96    #[turbo_tasks::function]
97    fn get_exports(&self) -> Vc<EcmascriptExports> {
98        let module_reference: Vc<Box<dyn ModuleReference>> =
99            Vc::upcast(SingleChunkableModuleReference::new(
100                Vc::upcast(*self.module),
101                dynamic_ref_description(),
102                ExportUsage::all(),
103            ));
104        EsmExports::reexport_including_default(module_reference)
105    }
106
107    #[turbo_tasks::function]
108    async fn chunk_item_content(
109        &self,
110        chunking_context: Vc<Box<dyn ChunkingContext>>,
111        _module_graph: Vc<ModuleGraph>,
112        _async_module_info: Option<Vc<AsyncModuleInfo>>,
113        _estimated: bool,
114    ) -> Result<Vc<EcmascriptChunkItemContent>> {
115        let module_id = self.module.chunk_item_id(chunking_context).await?;
116        Ok(EcmascriptChunkItemContent {
117            inner_code: formatdoc!(
118                r#"
119                    {TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));
120                "#,
121                StringifyJs(&module_id),
122            )
123            .into(),
124            ..Default::default()
125        }
126        .cell())
127    }
128}