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        ChunkingTypeOption, 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}
40
41#[turbo_tasks::value(shared)]
42pub struct HmrEntryModule {
43    pub ident: ResolvedVc<AssetIdent>,
44    pub module: ResolvedVc<Box<dyn ChunkableModule>>,
45}
46
47#[turbo_tasks::value_impl]
48impl HmrEntryModule {
49    #[turbo_tasks::function]
50    pub fn new(
51        ident: ResolvedVc<AssetIdent>,
52        module: ResolvedVc<Box<dyn ChunkableModule>>,
53    ) -> Vc<Self> {
54        Self { ident, module }.cell()
55    }
56}
57
58#[turbo_tasks::value_impl]
59impl Module for HmrEntryModule {
60    #[turbo_tasks::function]
61    fn ident(&self) -> Vc<AssetIdent> {
62        hmr_entry_point_base_ident().with_asset(rcstr!("ENTRY"), *self.ident)
63    }
64
65    #[turbo_tasks::function]
66    fn source(&self) -> Vc<OptionSource> {
67        Vc::cell(None)
68    }
69
70    #[turbo_tasks::function]
71    async fn references(&self) -> Result<Vc<ModuleReferences>> {
72        Ok(Vc::cell(vec![ResolvedVc::upcast(
73            HmrEntryModuleReference::new(Vc::upcast(*self.module))
74                .to_resolved()
75                .await?,
76        )]))
77    }
78    #[turbo_tasks::function]
79    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
80        ModuleSideEffects::SideEffectful.cell()
81    }
82}
83
84#[turbo_tasks::value_impl]
85impl ChunkableModule for HmrEntryModule {
86    #[turbo_tasks::function]
87    fn as_chunk_item(
88        self: ResolvedVc<Self>,
89        module_graph: ResolvedVc<ModuleGraph>,
90        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
91    ) -> Vc<Box<dyn ChunkItem>> {
92        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
93    }
94}
95
96#[turbo_tasks::value_impl]
97impl Asset for HmrEntryModule {
98    #[turbo_tasks::function]
99    fn content(self: Vc<Self>) -> Vc<AssetContent> {
100        todo!("HmrEntryModule doesn't implement content()")
101    }
102}
103
104#[turbo_tasks::value_impl]
105impl EcmascriptChunkPlaceable for HmrEntryModule {
106    #[turbo_tasks::function]
107    fn get_exports(self: Vc<Self>) -> Vc<EcmascriptExports> {
108        EcmascriptExports::None.cell()
109    }
110
111    #[turbo_tasks::function]
112    async fn chunk_item_content(
113        self: Vc<Self>,
114        chunking_context: Vc<Box<dyn ChunkingContext>>,
115        module_graph: Vc<ModuleGraph>,
116        _async_module_info: Option<Vc<AsyncModuleInfo>>,
117        _estimated: bool,
118    ) -> Result<Vc<EcmascriptChunkItemContent>> {
119        let this = self.await?;
120        let module = this.module;
121        let chunk_item = module.as_chunk_item(module_graph, chunking_context);
122        let id = chunking_context
123            .chunk_item_id_strategy()
124            .await?
125            .get_id(chunk_item)
126            .await?;
127
128        let mut code = RopeBuilder::default();
129        writeln!(code, "{TURBOPACK_REQUIRE}({});", StringifyJs(&id))?;
130        Ok(EcmascriptChunkItemContent {
131            inner_code: code.build(),
132            options: EcmascriptChunkItemOptions {
133                strict: true,
134                ..Default::default()
135            },
136            ..Default::default()
137        }
138        .cell())
139    }
140}
141
142#[turbo_tasks::value_impl]
143impl EvaluatableAsset for HmrEntryModule {}
144
145#[turbo_tasks::value]
146#[derive(ValueToString)]
147#[value_to_string("entry")]
148pub struct HmrEntryModuleReference {
149    pub module: ResolvedVc<Box<dyn Module>>,
150}
151
152#[turbo_tasks::value_impl]
153impl HmrEntryModuleReference {
154    #[turbo_tasks::function]
155    pub fn new(module: ResolvedVc<Box<dyn Module>>) -> Vc<Self> {
156        HmrEntryModuleReference { module }.cell()
157    }
158}
159
160#[turbo_tasks::value_impl]
161impl ModuleReference for HmrEntryModuleReference {
162    #[turbo_tasks::function]
163    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
164        *ModuleResolveResult::module(self.module)
165    }
166
167    #[turbo_tasks::function]
168    fn chunking_type(&self) -> Vc<ChunkingTypeOption> {
169        Vc::cell(Some(ChunkingType::Parallel {
170            inherit_async: false,
171            hoisted: false,
172        }))
173    }
174}