Skip to main content

next_core/next_dynamic/
dynamic_module.rs

1use anyhow::Result;
2use indoc::formatdoc;
3use turbo_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
39impl NextDynamicEntryModule {
40    fn module_reference(&self) -> Vc<Box<dyn ModuleReference>> {
41        Vc::upcast(SingleChunkableModuleReference::new(
42            Vc::upcast(*self.module),
43            rcstr!("next/dynamic reference"),
44            ExportUsage::all(),
45        ))
46    }
47}
48
49#[turbo_tasks::value_impl]
50impl Module for NextDynamicEntryModule {
51    #[turbo_tasks::function]
52    async fn ident(&self) -> Result<Vc<AssetIdent>> {
53        Ok(self
54            .module
55            .ident()
56            .owned()
57            .await?
58            .with_modifier(rcstr!("next/dynamic entry"))
59            .into_vc())
60    }
61
62    #[turbo_tasks::function]
63    fn source(&self) -> Vc<OptionSource> {
64        Vc::cell(None)
65    }
66
67    #[turbo_tasks::function]
68    async fn references(&self) -> Result<Vc<ModuleReferences>> {
69        Ok(Vc::cell(vec![self.module_reference().to_resolved().await?]))
70    }
71
72    #[turbo_tasks::function]
73    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
74        // This just exports another import
75        ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
76    }
77}
78
79#[turbo_tasks::value_impl]
80impl ChunkableModule for NextDynamicEntryModule {
81    #[turbo_tasks::function]
82    fn as_chunk_item(
83        self: ResolvedVc<Self>,
84        module_graph: ResolvedVc<ModuleGraph>,
85        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
86    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
87        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
88    }
89}
90
91#[turbo_tasks::value_impl]
92impl EcmascriptChunkPlaceable for NextDynamicEntryModule {
93    #[turbo_tasks::function]
94    fn get_exports(&self) -> Vc<EcmascriptExports> {
95        let module_reference = self.module_reference();
96        EsmExports::reexport_including_default(module_reference)
97    }
98
99    #[turbo_tasks::function]
100    async fn chunk_item_content(
101        &self,
102        chunking_context: Vc<Box<dyn ChunkingContext>>,
103        _module_graph: Vc<ModuleGraph>,
104        _async_module_info: Option<Vc<AsyncModuleInfo>>,
105        _estimated: bool,
106    ) -> Result<Vc<EcmascriptChunkItemContent>> {
107        let module_id = self.module.chunk_item_id(chunking_context).await?;
108        Ok(EcmascriptChunkItemContent {
109            inner_code: formatdoc!(
110                r#"
111                    {TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));
112                "#,
113                StringifyJs(&module_id),
114            )
115            .into(),
116            ..Default::default()
117        }
118        .cell())
119    }
120}