next_core/
hmr_entry.rs

1use std::io::Write;
2
3use anyhow::Result;
4use turbo_rcstr::{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        ChunkItem, ChunkType, ChunkableModule, ChunkableModuleReference, ChunkingContext,
11        EvaluatableAsset,
12    },
13    ident::AssetIdent,
14    module::{Module, ModuleSideEffects},
15    module_graph::ModuleGraph,
16    output::OutputAssetsReference,
17    reference::{ModuleReference, ModuleReferences},
18    resolve::ModuleResolveResult,
19    source::OptionSource,
20};
21use turbopack_ecmascript::{
22    chunk::{
23        EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkItemOptions,
24        EcmascriptChunkPlaceable, EcmascriptChunkType, EcmascriptExports,
25    },
26    runtime_functions::TURBOPACK_REQUIRE,
27    utils::StringifyJs,
28};
29
30/// Each entry point in the HMR system has an ident with a different nested asset.
31/// This produces the 'base' ident for the HMR entry point, which is then modified
32#[turbo_tasks::function]
33async fn hmr_entry_point_base_ident() -> Result<Vc<AssetIdent>> {
34    Ok(AssetIdent::from_path(
35        VirtualFileSystem::new_with_name(rcstr!("hmr-entry"))
36            .root()
37            .await?
38            .join("hmr-entry.js")?,
39    ))
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    fn ident(&self) -> Vc<AssetIdent> {
63        hmr_entry_point_base_ident().with_asset(rcstr!("ENTRY"), *self.ident)
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![ResolvedVc::upcast(
74            HmrEntryModuleReference::new(Vc::upcast(*self.module))
75                .to_resolved()
76                .await?,
77        )]))
78    }
79    #[turbo_tasks::function]
80    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
81        ModuleSideEffects::SideEffectful.cell()
82    }
83}
84
85#[turbo_tasks::value_impl]
86impl ChunkableModule for HmrEntryModule {
87    #[turbo_tasks::function]
88    fn as_chunk_item(
89        self: ResolvedVc<Self>,
90        module_graph: ResolvedVc<ModuleGraph>,
91        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
92    ) -> Vc<Box<dyn ChunkItem>> {
93        Vc::upcast(
94            HmrEntryChunkItem {
95                module: self,
96                module_graph,
97                chunking_context,
98            }
99            .cell(),
100        )
101    }
102}
103
104#[turbo_tasks::value_impl]
105impl Asset for HmrEntryModule {
106    #[turbo_tasks::function]
107    fn content(self: Vc<Self>) -> Vc<AssetContent> {
108        todo!("HmrEntryModule doesn't implement content()")
109    }
110}
111
112#[turbo_tasks::value_impl]
113impl EcmascriptChunkPlaceable for HmrEntryModule {
114    #[turbo_tasks::function]
115    fn get_exports(self: Vc<Self>) -> Vc<EcmascriptExports> {
116        EcmascriptExports::None.cell()
117    }
118}
119
120#[turbo_tasks::value_impl]
121impl EvaluatableAsset for HmrEntryModule {}
122
123#[turbo_tasks::value]
124pub struct HmrEntryModuleReference {
125    pub module: ResolvedVc<Box<dyn Module>>,
126}
127
128#[turbo_tasks::value_impl]
129impl HmrEntryModuleReference {
130    #[turbo_tasks::function]
131    pub fn new(module: ResolvedVc<Box<dyn Module>>) -> Vc<Self> {
132        HmrEntryModuleReference { module }.cell()
133    }
134}
135
136#[turbo_tasks::value_impl]
137impl ValueToString for HmrEntryModuleReference {
138    #[turbo_tasks::function]
139    fn to_string(&self) -> Vc<RcStr> {
140        Vc::cell(rcstr!("entry"))
141    }
142}
143
144#[turbo_tasks::value_impl]
145impl ModuleReference for HmrEntryModuleReference {
146    #[turbo_tasks::function]
147    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
148        *ModuleResolveResult::module(self.module)
149    }
150}
151
152#[turbo_tasks::value_impl]
153impl ChunkableModuleReference for HmrEntryModuleReference {}
154
155/// The chunk item for [`HmrEntryModule`].
156#[turbo_tasks::value]
157struct HmrEntryChunkItem {
158    module: ResolvedVc<HmrEntryModule>,
159    module_graph: ResolvedVc<ModuleGraph>,
160    chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
161}
162
163#[turbo_tasks::value_impl]
164impl OutputAssetsReference for HmrEntryChunkItem {}
165
166#[turbo_tasks::value_impl]
167impl ChunkItem for HmrEntryChunkItem {
168    #[turbo_tasks::function]
169    fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
170        *self.chunking_context
171    }
172
173    #[turbo_tasks::function]
174    fn asset_ident(&self) -> Vc<AssetIdent> {
175        self.module.ident()
176    }
177
178    #[turbo_tasks::function]
179    fn ty(&self) -> Vc<Box<dyn ChunkType>> {
180        Vc::upcast(Vc::<EcmascriptChunkType>::default())
181    }
182
183    #[turbo_tasks::function]
184    fn module(&self) -> Vc<Box<dyn Module>> {
185        Vc::upcast(*self.module)
186    }
187}
188
189#[turbo_tasks::value_impl]
190impl EcmascriptChunkItem for HmrEntryChunkItem {
191    #[turbo_tasks::function]
192    async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
193        let this = self.module.await?;
194        let module = this.module;
195        let chunk_item = module.as_chunk_item(*self.module_graph, *self.chunking_context);
196        let id = self.chunking_context.chunk_item_id(chunk_item).await?;
197
198        let mut code = RopeBuilder::default();
199        writeln!(code, "{TURBOPACK_REQUIRE}({});", StringifyJs(&id))?;
200        Ok(EcmascriptChunkItemContent {
201            inner_code: code.build(),
202            options: EcmascriptChunkItemOptions {
203                strict: true,
204                ..Default::default()
205            },
206            ..Default::default()
207        }
208        .cell())
209    }
210}