turbopack_ecmascript/worker_chunk/
chunk_item.rs1use anyhow::{Result, bail};
2use indoc::formatdoc;
3use turbo_rcstr::rcstr;
4use turbo_tasks::{ResolvedVc, TryJoinIterExt, Vc};
5use turbopack_core::{
6 chunk::{
7 AsyncModuleInfo, ChunkData, ChunkItem, ChunkType, ChunkingContext, ChunkingContextExt,
8 ChunksData, EvaluatableAsset, EvaluatableAssets, availability_info::AvailabilityInfo,
9 },
10 context::AssetContext,
11 ident::AssetIdent,
12 module::Module,
13 module_graph::{ModuleGraph, chunk_group_info::ChunkGroup},
14 output::{OutputAsset, OutputAssets, OutputAssetsReference, OutputAssetsWithReferenced},
15};
16
17use super::{module::WorkerLoaderModule, worker_type::WorkerType};
18use crate::{
19 chunk::{
20 EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkType,
21 data::EcmascriptChunkData,
22 },
23 runtime_functions::{TURBOPACK_CREATE_WORKER, TURBOPACK_EXPORT_VALUE},
24 utils::StringifyJs,
25};
26
27#[turbo_tasks::value(shared)]
28pub struct WorkerLoaderChunkItem {
29 pub module: ResolvedVc<WorkerLoaderModule>,
30 pub module_graph: ResolvedVc<ModuleGraph>,
31 pub chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
32 pub worker_type: WorkerType,
33 pub asset_context: ResolvedVc<Box<dyn AssetContext>>,
34}
35
36#[turbo_tasks::value_impl]
37impl WorkerLoaderChunkItem {
38 #[turbo_tasks::function]
39 async fn chunk_group(&self) -> Result<Vc<OutputAssetsWithReferenced>> {
40 let module = self.module.await?;
41
42 Ok(match self.worker_type {
43 WorkerType::WebWorker | WorkerType::SharedWebWorker => {
44 self.chunking_context.evaluated_chunk_group_assets(
45 module
46 .inner
47 .ident()
48 .with_modifier(self.worker_type.chunk_modifier_str()),
49 ChunkGroup::Isolated(ResolvedVc::upcast(module.inner)),
50 *self.module_graph,
51 AvailabilityInfo::root(),
52 )
53 }
54 WorkerType::NodeWorkerThread => {
57 let Some(evaluatable) =
58 ResolvedVc::try_sidecast::<Box<dyn EvaluatableAsset>>(module.inner)
59 else {
60 bail!("Worker module must be evaluatable");
61 };
62
63 let worker_path = self
64 .chunking_context
65 .chunk_path(
66 None,
67 module.inner.ident(),
68 Some(rcstr!("[worker thread]")),
69 rcstr!(".js"),
70 )
71 .owned()
72 .await?;
73
74 let entry_result = self
75 .chunking_context
76 .root_entry_chunk_group(
77 worker_path,
78 EvaluatableAssets::one(*evaluatable),
79 *self.module_graph,
80 OutputAssets::empty(),
81 OutputAssets::empty(),
82 )
83 .await?;
84
85 OutputAssetsWithReferenced {
86 assets: ResolvedVc::cell(vec![entry_result.asset]),
87 referenced_assets: ResolvedVc::cell(vec![]),
88 references: ResolvedVc::cell(vec![]),
89 }
90 .cell()
91 }
92 })
93 }
94
95 #[turbo_tasks::function]
96 async fn chunks_data(self: Vc<Self>) -> Result<Vc<ChunksData>> {
97 let this = self.await?;
98 Ok(ChunkData::from_assets(
99 this.chunking_context.output_root().owned().await?,
100 *self.chunk_group().await?.assets,
101 ))
102 }
103}
104
105#[turbo_tasks::value_impl]
106impl EcmascriptChunkItem for WorkerLoaderChunkItem {
107 #[turbo_tasks::function]
108 fn content(self: Vc<Self>) -> Vc<EcmascriptChunkItemContent> {
109 panic!("should not be called");
110 }
111
112 #[turbo_tasks::function]
113 async fn content_with_async_module_info(
114 self: Vc<Self>,
115 _async_module_info: Option<Vc<AsyncModuleInfo>>,
116 estimated: bool,
117 ) -> Result<Vc<EcmascriptChunkItemContent>> {
118 let this = self.await?;
119
120 if estimated {
121 return Ok(EcmascriptChunkItemContent {
126 inner_code: formatdoc! {
127 r#"
128 {TURBOPACK_EXPORT_VALUE}(function(Ctor, opts) {{
129 return {TURBOPACK_CREATE_WORKER}(Ctor, __dirname + "/" + {worker_path:#}, opts);
130 }});
131 "#,
132 worker_path = StringifyJs(&"a_fake_path_for_size_estimation"),
133 }.into(),
134 ..Default::default()
135 }
136 .cell());
137 }
138
139 let code = match this.worker_type {
140 WorkerType::WebWorker | WorkerType::SharedWebWorker => {
141 let entrypoint_full_path = this.chunking_context.worker_entrypoint().path().await?;
145
146 let output_root = this.chunking_context.output_root().owned().await?;
148 let entrypoint_path = output_root
149 .get_path_to(&entrypoint_full_path)
150 .map(|s| s.to_string())
151 .unwrap_or_else(|| entrypoint_full_path.path.to_string());
152
153 let chunks_data = self.chunks_data().await?;
155 let chunks_data = chunks_data.iter().try_join().await?;
156 let chunks_data: Vec<_> = chunks_data
157 .iter()
158 .map(|chunk_data| EcmascriptChunkData::new(chunk_data))
159 .collect();
160
161 formatdoc! {
162 r#"
163 {TURBOPACK_EXPORT_VALUE}(function(Ctor, opts) {{
164 return {TURBOPACK_CREATE_WORKER}(Ctor, {entrypoint}, {chunks}, opts);
165 }});
166 "#,
167 entrypoint = StringifyJs(&entrypoint_path),
168 chunks = StringifyJs(&chunks_data),
169 }
170 }
171 WorkerType::NodeWorkerThread => {
172 let chunk_group = self.chunk_group().await?;
176 let assets = chunk_group.assets.await?;
177
178 let Some(entry_asset) = assets.last() else {
185 bail!("cannot find worker entry point asset");
186 };
187 let entry_path = entry_asset.path().await?;
188
189 formatdoc! {
195 r#"
196 {TURBOPACK_EXPORT_VALUE}(function(Ctor, opts) {{
197 return {TURBOPACK_CREATE_WORKER}(Ctor, __dirname + "/" + {worker_path:#}, opts);
198 }});
199 "#,
200 worker_path = StringifyJs(entry_path.file_name()),
201 }
202 }
203 };
204
205 Ok(EcmascriptChunkItemContent {
206 inner_code: code.into(),
207 ..Default::default()
208 }
209 .cell())
210 }
211}
212
213#[turbo_tasks::value_impl]
214impl OutputAssetsReference for WorkerLoaderChunkItem {
215 #[turbo_tasks::function]
216 async fn references(self: Vc<Self>) -> Result<Vc<OutputAssetsWithReferenced>> {
217 let this = self.await?;
218 match this.worker_type {
219 WorkerType::WebWorker | WorkerType::SharedWebWorker => Ok(self
220 .chunk_group()
221 .concatenate_asset(this.chunking_context.worker_entrypoint())),
222 WorkerType::NodeWorkerThread => {
223 Ok(self.chunk_group())
225 }
226 }
227 }
228}
229
230#[turbo_tasks::value_impl]
231impl ChunkItem for WorkerLoaderChunkItem {
232 #[turbo_tasks::function]
233 fn asset_ident(&self) -> Vc<AssetIdent> {
234 self.module.ident()
235 }
236
237 #[turbo_tasks::function]
238 fn content_ident(&self) -> Vc<AssetIdent> {
239 self.module.ident()
240 }
241
242 #[turbo_tasks::function]
243 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
244 *self.chunking_context
245 }
246
247 #[turbo_tasks::function]
248 async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
249 Ok(Vc::upcast(
250 Vc::<EcmascriptChunkType>::default().resolve().await?,
251 ))
252 }
253
254 #[turbo_tasks::function]
255 fn module(&self) -> Vc<Box<dyn Module>> {
256 *ResolvedVc::upcast(self.module)
257 }
258}