Skip to main content

next_core/
hmr_entry.rs

1use std::io::Write;
2
3use anyhow::Result;
4use turbo_rcstr::rcstr;
5use turbo_tasks::{ResolvedVc, ValueToString, Vc};
6use turbo_tasks_fs::{FileSystem, VirtualFileSystem, rope::RopeBuilder};
7use turbopack_core::{
8    asset::{Asset, AssetContent},
9    chunk::{
10        AsyncModuleInfo, ChunkItem, ChunkableModule, ChunkingContext, ChunkingType,
11        EvaluatableAsset,
12    },
13    ident::AssetIdent,
14    module::{Module, ModuleSideEffects},
15    module_graph::ModuleGraph,
16    reference::{ModuleReference, ModuleReferences},
17    resolve::ModuleResolveResult,
18    source::OptionSource,
19};
20use turbopack_ecmascript::{
21    chunk::{
22        EcmascriptChunkItemContent, EcmascriptChunkItemOptions, EcmascriptChunkPlaceable,
23        EcmascriptExports, ecmascript_chunk_item,
24    },
25    runtime_functions::TURBOPACK_REQUIRE,
26    utils::StringifyJs,
27};
28
29/// Each entry point in the HMR system has an ident with a different nested asset.
30/// This produces the 'base' ident for the HMR entry point, which is then modified
31#[turbo_tasks::function]
32async fn hmr_entry_point_base_ident() -> Result<Vc<AssetIdent>> {
33    Ok(AssetIdent::from_path(
34        VirtualFileSystem::new_with_name(rcstr!("hmr-entry"))
35            .root()
36            .await?
37            .join("hmr-entry.js")?,
38    )
39    .into_vc())
40}
41
42#[turbo_tasks::value(shared)]
43pub struct HmrEntryModule {
44    pub ident: ResolvedVc<AssetIdent>,
45    pub module: ResolvedVc<Box<dyn ChunkableModule>>,
46}
47
48#[turbo_tasks::value_impl]
49impl HmrEntryModule {
50    #[turbo_tasks::function]
51    pub fn new(
52        ident: ResolvedVc<AssetIdent>,
53        module: ResolvedVc<Box<dyn ChunkableModule>>,
54    ) -> Vc<Self> {
55        Self { ident, module }.cell()
56    }
57}
58
59#[turbo_tasks::value_impl]
60impl Module for HmrEntryModule {
61    #[turbo_tasks::function]
62    async fn ident(&self) -> Result<Vc<AssetIdent>> {
63        Ok(hmr_entry_point_base_ident()
64            .owned()
65            .await?
66            .with_asset(rcstr!("ENTRY"), self.ident)
67            .into_vc())
68    }
69
70    #[turbo_tasks::function]
71    fn source(&self) -> Vc<OptionSource> {
72        Vc::cell(None)
73    }
74
75    #[turbo_tasks::function]
76    async fn references(&self) -> Result<Vc<ModuleReferences>> {
77        Ok(Vc::cell(vec![ResolvedVc::upcast(
78            HmrEntryModuleReference::new(Vc::upcast(*self.module))
79                .to_resolved()
80                .await?,
81        )]))
82    }
83    #[turbo_tasks::function]
84    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
85        ModuleSideEffects::SideEffectful.cell()
86    }
87}
88
89#[turbo_tasks::value_impl]
90impl ChunkableModule for HmrEntryModule {
91    #[turbo_tasks::function]
92    fn as_chunk_item(
93        self: ResolvedVc<Self>,
94        module_graph: ResolvedVc<ModuleGraph>,
95        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
96    ) -> Vc<Box<dyn ChunkItem>> {
97        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
98    }
99}
100
101#[turbo_tasks::value_impl]
102impl Asset for HmrEntryModule {
103    #[turbo_tasks::function]
104    fn content(self: Vc<Self>) -> Vc<AssetContent> {
105        todo!("HmrEntryModule doesn't implement content()")
106    }
107}
108
109#[turbo_tasks::value_impl]
110impl EcmascriptChunkPlaceable for HmrEntryModule {
111    #[turbo_tasks::function]
112    fn get_exports(self: Vc<Self>) -> Vc<EcmascriptExports> {
113        EcmascriptExports::None.cell()
114    }
115
116    #[turbo_tasks::function]
117    async fn chunk_item_content(
118        self: Vc<Self>,
119        chunking_context: Vc<Box<dyn ChunkingContext>>,
120        module_graph: Vc<ModuleGraph>,
121        _async_module_info: Option<Vc<AsyncModuleInfo>>,
122        _estimated: bool,
123    ) -> Result<Vc<EcmascriptChunkItemContent>> {
124        let this = self.await?;
125        let module = this.module;
126        let chunk_item = module.as_chunk_item(module_graph, chunking_context);
127        let id = chunking_context
128            .chunk_item_id_strategy()
129            .await?
130            .get_id(chunk_item)
131            .await?;
132
133        let mut code = RopeBuilder::default();
134        writeln!(code, "{TURBOPACK_REQUIRE}({});", StringifyJs(&id))?;
135        Ok(EcmascriptChunkItemContent {
136            inner_code: code.build(),
137            options: EcmascriptChunkItemOptions {
138                strict: true,
139                ..Default::default()
140            },
141            ..Default::default()
142        }
143        .cell())
144    }
145}
146
147#[turbo_tasks::value_impl]
148impl EvaluatableAsset for HmrEntryModule {}
149
150#[turbo_tasks::value]
151#[derive(ValueToString)]
152#[value_to_string("entry")]
153pub struct HmrEntryModuleReference {
154    pub module: ResolvedVc<Box<dyn Module>>,
155}
156
157#[turbo_tasks::value_impl]
158impl HmrEntryModuleReference {
159    #[turbo_tasks::function]
160    pub fn new(module: ResolvedVc<Box<dyn Module>>) -> Vc<Self> {
161        HmrEntryModuleReference { module }.cell()
162    }
163}
164
165#[turbo_tasks::value_impl]
166impl ModuleReference for HmrEntryModuleReference {
167    #[turbo_tasks::function]
168    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
169        *ModuleResolveResult::module(self.module)
170    }
171
172    fn chunking_type(&self) -> Option<ChunkingType> {
173        Some(ChunkingType::Parallel {
174            inherit_async: false,
175            hoisted: false,
176        })
177    }
178}