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