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#[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#[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}