1use anyhow::{Context, Result, bail};
2use tracing::Instrument;
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{
5 FxIndexMap, ResolvedVc, TryJoinIterExt, Upcast, ValueToString, ValueToStringRef, Vc,
6};
7use turbo_tasks_fs::FileSystemPath;
8use turbo_tasks_hash::HashAlgorithm;
9use turbopack_core::{
10 asset::{Asset, AssetContent},
11 chunk::{
12 AssetSuffix, Chunk, ChunkGroupResult, ChunkItem, ChunkType, ChunkableModule,
13 ChunkingConfig, ChunkingConfigs, ChunkingContext, ContentHashing, EntryChunkGroupResult,
14 EvaluatableAsset, MinifyType, SourceMapSourceType, SourceMapsType, UnusedReferences,
15 UrlBehavior,
16 availability_info::AvailabilityInfo,
17 chunk_group::{MakeChunkGroupResult, make_chunk_group},
18 chunk_id_strategy::ModuleIdStrategy,
19 },
20 environment::Environment,
21 ident::AssetIdent,
22 module::Module,
23 module_graph::{
24 ModuleGraph,
25 binding_usage_info::{BindingUsageInfo, ModuleExportUsage},
26 chunk_group_info::ChunkGroup,
27 },
28 output::{OutputAsset, OutputAssets},
29};
30use turbopack_ecmascript::{
31 async_chunk::module::AsyncLoaderModule,
32 chunk::EcmascriptChunk,
33 manifest::{chunk_asset::ManifestAsyncModule, loader_module::ManifestLoaderModule},
34};
35use turbopack_ecmascript_runtime::RuntimeType;
36
37use crate::ecmascript::node::{
38 chunk::EcmascriptBuildNodeChunk, entry::chunk::EcmascriptBuildNodeEntryChunk,
39};
40
41pub struct NodeJsChunkingContextBuilder {
43 chunking_context: NodeJsChunkingContext,
44}
45
46impl NodeJsChunkingContextBuilder {
47 pub fn asset_prefix(mut self, asset_prefix: Option<RcStr>) -> Self {
48 self.chunking_context.asset_prefix = asset_prefix;
49 self
50 }
51
52 pub fn asset_prefix_override(mut self, tag: RcStr, prefix: RcStr) -> Self {
53 self.chunking_context.asset_prefixes.insert(tag, prefix);
54 self
55 }
56
57 pub fn asset_root_path_override(mut self, tag: RcStr, path: FileSystemPath) -> Self {
58 self.chunking_context.asset_root_paths.insert(tag, path);
59 self
60 }
61
62 pub fn client_roots_override(mut self, tag: RcStr, path: FileSystemPath) -> Self {
63 self.chunking_context.client_roots.insert(tag, path);
64 self
65 }
66
67 pub fn url_behavior_override(mut self, tag: RcStr, behavior: UrlBehavior) -> Self {
68 self.chunking_context.url_behaviors.insert(tag, behavior);
69 self
70 }
71
72 pub fn default_url_behavior(mut self, behavior: UrlBehavior) -> Self {
73 self.chunking_context.default_url_behavior = Some(behavior);
74 self
75 }
76
77 pub fn minify_type(mut self, minify_type: MinifyType) -> Self {
78 self.chunking_context.minify_type = minify_type;
79 self
80 }
81
82 pub fn source_maps(mut self, source_maps: SourceMapsType) -> Self {
83 self.chunking_context.source_maps_type = source_maps;
84 self
85 }
86
87 pub fn file_tracing(mut self, enable_tracing: bool) -> Self {
88 self.chunking_context.enable_file_tracing = enable_tracing;
89 self
90 }
91
92 pub fn nested_async_availability(mut self, enable_nested_async_availability: bool) -> Self {
93 self.chunking_context.enable_nested_async_availability = enable_nested_async_availability;
94 self
95 }
96
97 pub fn module_merging(mut self, enable_module_merging: bool) -> Self {
98 self.chunking_context.enable_module_merging = enable_module_merging;
99 self
100 }
101
102 pub fn dynamic_chunk_content_loading(
103 mut self,
104 enable_dynamic_chunk_content_loading: bool,
105 ) -> Self {
106 self.chunking_context.enable_dynamic_chunk_content_loading =
107 enable_dynamic_chunk_content_loading;
108 self
109 }
110
111 pub fn runtime_type(mut self, runtime_type: RuntimeType) -> Self {
112 self.chunking_context.runtime_type = runtime_type;
113 self
114 }
115
116 pub fn manifest_chunks(mut self, manifest_chunks: bool) -> Self {
117 self.chunking_context.manifest_chunks = manifest_chunks;
118 self
119 }
120
121 pub fn source_map_source_type(mut self, source_map_source_type: SourceMapSourceType) -> Self {
122 self.chunking_context.source_map_source_type = source_map_source_type;
123 self
124 }
125
126 pub fn module_id_strategy(mut self, module_id_strategy: ResolvedVc<ModuleIdStrategy>) -> Self {
127 self.chunking_context.module_id_strategy = Some(module_id_strategy);
128 self
129 }
130
131 pub fn export_usage(mut self, export_usage: Option<ResolvedVc<BindingUsageInfo>>) -> Self {
132 self.chunking_context.export_usage = export_usage;
133 self
134 }
135
136 pub fn unused_references(mut self, unused_references: ResolvedVc<UnusedReferences>) -> Self {
137 self.chunking_context.unused_references = Some(unused_references);
138 self
139 }
140
141 pub fn chunking_config<T>(mut self, ty: ResolvedVc<T>, chunking_config: ChunkingConfig) -> Self
142 where
143 T: Upcast<Box<dyn ChunkType>>,
144 {
145 self.chunking_context
146 .chunking_configs
147 .push((ResolvedVc::upcast_non_strict(ty), chunking_config));
148 self
149 }
150
151 pub fn debug_ids(mut self, debug_ids: bool) -> Self {
152 self.chunking_context.debug_ids = debug_ids;
153 self
154 }
155
156 pub fn worker_forwarded_globals(mut self, globals: Vec<RcStr>) -> Self {
157 self.chunking_context
158 .worker_forwarded_globals
159 .extend(globals);
160 self
161 }
162
163 pub fn asset_content_hashing(mut self, content_hashing: ContentHashing) -> Self {
164 self.chunking_context.asset_content_hashing = content_hashing;
165 self
166 }
167
168 pub fn hash_salt(mut self, salt: ResolvedVc<RcStr>) -> Self {
169 self.chunking_context.hash_salt = salt;
170 self
171 }
172
173 pub fn build(self) -> Vc<NodeJsChunkingContext> {
175 NodeJsChunkingContext::cell(self.chunking_context)
176 }
177}
178
179#[turbo_tasks::value]
181#[derive(Debug, Clone)]
182pub struct NodeJsChunkingContext {
183 root_path: FileSystemPath,
185 output_root: FileSystemPath,
187 output_root_to_root_path: RcStr,
189 client_root: FileSystemPath,
191 #[bincode(with = "turbo_bincode::indexmap")]
193 client_roots: FxIndexMap<RcStr, FileSystemPath>,
194 chunk_root_path: FileSystemPath,
196 asset_root_path: FileSystemPath,
198 #[bincode(with = "turbo_bincode::indexmap")]
200 asset_root_paths: FxIndexMap<RcStr, FileSystemPath>,
201 asset_prefix: Option<RcStr>,
203 #[bincode(with = "turbo_bincode::indexmap")]
205 asset_prefixes: FxIndexMap<RcStr, RcStr>,
206 #[bincode(with = "turbo_bincode::indexmap")]
208 url_behaviors: FxIndexMap<RcStr, UrlBehavior>,
209 default_url_behavior: Option<UrlBehavior>,
211 environment: ResolvedVc<Environment>,
213 runtime_type: RuntimeType,
215 enable_file_tracing: bool,
217 enable_nested_async_availability: bool,
219 enable_module_merging: bool,
221 enable_dynamic_chunk_content_loading: bool,
223 minify_type: MinifyType,
225 source_maps_type: SourceMapsType,
227 manifest_chunks: bool,
229 module_id_strategy: Option<ResolvedVc<ModuleIdStrategy>>,
231 export_usage: Option<ResolvedVc<BindingUsageInfo>>,
233 unused_references: Option<ResolvedVc<UnusedReferences>>,
235 source_map_source_type: SourceMapSourceType,
237 chunking_configs: Vec<(ResolvedVc<Box<dyn ChunkType>>, ChunkingConfig)>,
239 debug_ids: bool,
241 worker_forwarded_globals: Vec<RcStr>,
243 asset_content_hashing: ContentHashing,
245 hash_salt: ResolvedVc<RcStr>,
247}
248
249impl NodeJsChunkingContext {
250 pub fn builder(
252 root_path: FileSystemPath,
253 output_root: FileSystemPath,
254 output_root_to_root_path: RcStr,
255 client_root: FileSystemPath,
256 chunk_root_path: FileSystemPath,
257 asset_root_path: FileSystemPath,
258 environment: ResolvedVc<Environment>,
259 runtime_type: RuntimeType,
260 ) -> NodeJsChunkingContextBuilder {
261 NodeJsChunkingContextBuilder {
262 chunking_context: NodeJsChunkingContext {
263 root_path,
264 output_root,
265 output_root_to_root_path,
266 client_root,
267 client_roots: Default::default(),
268 chunk_root_path,
269 asset_root_path,
270 asset_root_paths: Default::default(),
271 asset_prefix: None,
272 asset_prefixes: Default::default(),
273 url_behaviors: Default::default(),
274 default_url_behavior: None,
275 enable_file_tracing: false,
276 enable_nested_async_availability: false,
277 enable_module_merging: false,
278 enable_dynamic_chunk_content_loading: false,
279 environment,
280 runtime_type,
281 minify_type: MinifyType::NoMinify,
282 source_maps_type: SourceMapsType::Full,
283 manifest_chunks: false,
284 source_map_source_type: SourceMapSourceType::TurbopackUri,
285 module_id_strategy: None,
286 export_usage: None,
287 unused_references: None,
288 chunking_configs: Default::default(),
289 debug_ids: false,
290 worker_forwarded_globals: vec![],
291 asset_content_hashing: ContentHashing::Direct { length: 13 },
292 hash_salt: ResolvedVc::cell(RcStr::default()),
293 },
294 }
295 }
296}
297
298#[turbo_tasks::value_impl]
299impl NodeJsChunkingContext {
300 #[turbo_tasks::function]
305 pub fn runtime_type(&self) -> Vc<RuntimeType> {
306 self.runtime_type.cell()
307 }
308
309 #[turbo_tasks::function]
311 pub fn minify_type(&self) -> Vc<MinifyType> {
312 self.minify_type.cell()
313 }
314
315 #[turbo_tasks::function]
316 pub fn hash_salt(&self) -> Vc<RcStr> {
317 *self.hash_salt
318 }
319
320 #[turbo_tasks::function]
321 pub fn asset_prefix(&self) -> Vc<Option<RcStr>> {
322 Vc::cell(self.asset_prefix.clone())
323 }
324}
325
326impl NodeJsChunkingContext {
327 async fn generate_chunk(
328 self: Vc<Self>,
329 chunk: ResolvedVc<Box<dyn Chunk>>,
330 ) -> Result<ResolvedVc<Box<dyn OutputAsset>>> {
331 Ok(
332 if let Some(ecmascript_chunk) = ResolvedVc::try_downcast_type::<EcmascriptChunk>(chunk)
333 {
334 ResolvedVc::upcast(
335 EcmascriptBuildNodeChunk::new(self, *ecmascript_chunk)
336 .to_resolved()
337 .await?,
338 )
339 } else if let Some(output_asset) =
340 ResolvedVc::try_sidecast::<Box<dyn OutputAsset>>(chunk)
341 {
342 output_asset
343 } else {
344 bail!("Unable to generate output asset for chunk");
345 },
346 )
347 }
348}
349
350#[turbo_tasks::value_impl]
351impl ChunkingContext for NodeJsChunkingContext {
352 #[turbo_tasks::function]
353 fn name(&self) -> Vc<RcStr> {
354 Vc::cell(rcstr!("unknown"))
355 }
356
357 #[turbo_tasks::function]
358 fn root_path(&self) -> Vc<FileSystemPath> {
359 self.root_path.clone().cell()
360 }
361
362 #[turbo_tasks::function]
363 fn output_root(&self) -> Vc<FileSystemPath> {
364 self.output_root.clone().cell()
365 }
366
367 #[turbo_tasks::function]
368 fn output_root_to_root_path(&self) -> Vc<RcStr> {
369 Vc::cell(self.output_root_to_root_path.clone())
370 }
371
372 #[turbo_tasks::function]
373 fn environment(&self) -> Vc<Environment> {
374 *self.environment
375 }
376
377 #[turbo_tasks::function]
378 fn is_tracing_enabled(&self) -> Vc<bool> {
379 Vc::cell(self.enable_file_tracing)
380 }
381
382 #[turbo_tasks::function]
383 fn is_nested_async_availability_enabled(&self) -> Vc<bool> {
384 Vc::cell(self.enable_nested_async_availability)
385 }
386
387 #[turbo_tasks::function]
388 fn is_module_merging_enabled(&self) -> Vc<bool> {
389 Vc::cell(self.enable_module_merging)
390 }
391
392 #[turbo_tasks::function]
393 fn is_dynamic_chunk_content_loading_enabled(&self) -> Vc<bool> {
394 Vc::cell(self.enable_dynamic_chunk_content_loading)
395 }
396
397 #[turbo_tasks::function]
398 pub fn minify_type(&self) -> Vc<MinifyType> {
399 self.minify_type.cell()
400 }
401
402 #[turbo_tasks::function]
403 async fn asset_url(&self, ident: FileSystemPath, tag: Option<RcStr>) -> Result<Vc<RcStr>> {
404 let asset_path = ident.to_string();
405
406 let client_root = tag
407 .as_ref()
408 .and_then(|tag| self.client_roots.get(tag))
409 .unwrap_or(&self.client_root);
410
411 let asset_prefix = tag
412 .as_ref()
413 .and_then(|tag| self.asset_prefixes.get(tag))
414 .or(self.asset_prefix.as_ref());
415
416 let asset_path = asset_path
417 .strip_prefix(&format!("{}/", client_root.path))
418 .context("expected client root to contain asset path")?;
419
420 Ok(Vc::cell(
421 format!(
422 "{}{}",
423 asset_prefix.map(|s| s.as_str()).unwrap_or("/"),
424 asset_path
425 )
426 .into(),
427 ))
428 }
429
430 #[turbo_tasks::function]
431 fn chunk_root_path(&self) -> Vc<FileSystemPath> {
432 self.chunk_root_path.clone().cell()
433 }
434
435 #[turbo_tasks::function]
436 async fn chunk_path(
437 &self,
438 _asset: Option<Vc<Box<dyn Asset>>>,
439 ident: Vc<AssetIdent>,
440 prefix: Option<RcStr>,
441 extension: RcStr,
442 ) -> Result<Vc<FileSystemPath>> {
443 let root_path = self.chunk_root_path.clone();
444 let name = ident
445 .output_name(self.root_path.clone(), prefix, extension)
446 .owned()
447 .await?;
448 Ok(root_path.join(&name)?.cell())
449 }
450
451 #[turbo_tasks::function]
452 fn reference_chunk_source_maps(&self, _chunk: Vc<Box<dyn OutputAsset>>) -> Vc<bool> {
453 Vc::cell(match self.source_maps_type {
454 SourceMapsType::Full => true,
455 SourceMapsType::Partial => true,
456 SourceMapsType::None => false,
457 })
458 }
459
460 #[turbo_tasks::function]
461 fn reference_module_source_maps(&self, _module: Vc<Box<dyn Module>>) -> Vc<bool> {
462 Vc::cell(match self.source_maps_type {
463 SourceMapsType::Full => true,
464 SourceMapsType::Partial => true,
465 SourceMapsType::None => false,
466 })
467 }
468
469 #[turbo_tasks::function]
470 fn source_map_source_type(&self) -> Vc<SourceMapSourceType> {
471 self.source_map_source_type.cell()
472 }
473
474 #[turbo_tasks::function]
475 fn chunking_configs(&self) -> Result<Vc<ChunkingConfigs>> {
476 Ok(Vc::cell(self.chunking_configs.iter().cloned().collect()))
477 }
478
479 #[turbo_tasks::function]
480 async fn asset_path(
481 self: Vc<Self>,
482 content: Vc<AssetContent>,
483 original_asset_ident: Vc<AssetIdent>,
484 tag: Option<RcStr>,
485 ) -> Result<Vc<FileSystemPath>> {
486 let this = self.await?;
487 let source_path = original_asset_ident.path().await?;
488 let basename = source_path.file_name();
489 let ContentHashing::Direct { length } = this.asset_content_hashing;
490 let hash = content
491 .content_hash(self.hash_salt(), HashAlgorithm::Xxh3Hash128Base38)
492 .await?;
493 let hash = hash
494 .as_ref()
495 .context("Missing content when trying to generate the content hash for static asset")?;
496 let short_hash = &hash[..length as usize];
497 let asset_path = match source_path.extension() {
498 Some(ext) => format!(
499 "{basename}.{short_hash}.{ext}",
500 basename = &basename[..basename.len() - ext.len() - 1],
501 ),
502 None => format!("{basename}.{short_hash}"),
503 };
504
505 let asset_root_path = tag
506 .as_ref()
507 .and_then(|tag| this.asset_root_paths.get(tag))
508 .unwrap_or(&this.asset_root_path);
509
510 Ok(asset_root_path.join(&asset_path)?.cell())
511 }
512
513 #[turbo_tasks::function]
514 fn url_behavior(&self, tag: Option<RcStr>) -> Vc<UrlBehavior> {
515 tag.as_ref()
516 .and_then(|tag| self.url_behaviors.get(tag))
517 .cloned()
518 .or_else(|| self.default_url_behavior.clone())
519 .unwrap_or(UrlBehavior {
520 suffix: AssetSuffix::Inferred,
521 static_suffix: ResolvedVc::cell(None),
522 })
523 .cell()
524 }
525
526 #[turbo_tasks::function]
527 async fn chunk_group(
528 self: ResolvedVc<Self>,
529 ident: Vc<AssetIdent>,
530 chunk_group: ChunkGroup,
531 module_graph: Vc<ModuleGraph>,
532 availability_info: AvailabilityInfo,
533 ) -> Result<Vc<ChunkGroupResult>> {
534 let span = tracing::info_span!("chunking", name = display(ident.to_string().await?));
535 async move {
536 let modules = chunk_group.entries();
537 let MakeChunkGroupResult {
538 chunks,
539 referenced_output_assets,
540 references,
541 availability_info,
542 } = make_chunk_group(
543 modules,
544 module_graph,
545 ResolvedVc::upcast(self),
546 availability_info,
547 )
548 .await?;
549
550 let chunks = chunks.await?;
551
552 let assets = chunks
553 .iter()
554 .map(|chunk| self.generate_chunk(*chunk))
555 .try_join()
556 .await?;
557
558 Ok(ChunkGroupResult {
559 assets: ResolvedVc::cell(assets),
560 referenced_assets: ResolvedVc::cell(referenced_output_assets),
561 references: ResolvedVc::cell(references),
562 availability_info,
563 }
564 .cell())
565 }
566 .instrument(span)
567 .await
568 }
569
570 #[turbo_tasks::function]
571 pub async fn entry_chunk_group(
572 self: ResolvedVc<Self>,
573 path: FileSystemPath,
574 chunk_group: ChunkGroup,
575 module_graph: Vc<ModuleGraph>,
576 extra_chunks: Vc<OutputAssets>,
577 extra_referenced_assets: Vc<OutputAssets>,
578 availability_info: AvailabilityInfo,
579 ) -> Result<Vc<EntryChunkGroupResult>> {
580 let span = tracing::info_span!(
581 "chunking",
582 name = display(path.to_string_ref().await?),
583 chunking_type = "entry",
584 );
585 async move {
586 let entries = chunk_group.entries();
587 let MakeChunkGroupResult {
588 chunks,
589 mut referenced_output_assets,
590 references,
591 availability_info,
592 } = make_chunk_group(
593 entries,
594 module_graph,
595 ResolvedVc::upcast(self),
596 availability_info,
597 )
598 .await?;
599
600 let chunks = chunks.await?;
601
602 let extra_chunks = extra_chunks.await?;
603 let mut other_chunks = chunks
604 .iter()
605 .map(|chunk| self.generate_chunk(*chunk))
606 .try_join()
607 .await?;
608 other_chunks.extend(extra_chunks.iter().copied());
609
610 referenced_output_assets.extend(extra_referenced_assets.await?.iter().copied());
611
612 let Some(module) = ResolvedVc::try_sidecast(chunk_group.entries().last().unwrap())
613 else {
614 bail!("module must be placeable in an ecmascript chunk");
615 };
616
617 let evaluatable_assets = chunk_group
618 .entries()
619 .map(|entry| {
620 ResolvedVc::try_sidecast::<Box<dyn EvaluatableAsset>>(entry)
621 .context("entry_chunk_group entries must be evaluatable")
622 })
623 .collect::<Result<Vec<_>>>()?;
624
625 let asset = ResolvedVc::upcast(
626 EcmascriptBuildNodeEntryChunk::new(
627 path,
628 Vc::cell(other_chunks),
629 Vc::cell(evaluatable_assets),
630 *module,
631 Vc::cell(referenced_output_assets),
632 Vc::cell(references),
633 module_graph,
634 *self,
635 )
636 .to_resolved()
637 .await?,
638 );
639
640 Ok(EntryChunkGroupResult {
641 asset,
642 availability_info,
643 }
644 .cell())
645 }
646 .instrument(span)
647 .await
648 }
649
650 #[turbo_tasks::function]
651 fn evaluated_chunk_group(
652 self: Vc<Self>,
653 _ident: Vc<AssetIdent>,
654 _chunk_group: ChunkGroup,
655 _module_graph: Vc<ModuleGraph>,
656 _availability_info: AvailabilityInfo,
657 ) -> Result<Vc<ChunkGroupResult>> {
658 bail!("the Node.js chunking context does not support evaluated chunk groups")
659 }
660
661 #[turbo_tasks::function]
662 fn chunk_item_id_strategy(&self) -> Vc<ModuleIdStrategy> {
663 *self
664 .module_id_strategy
665 .unwrap_or_else(|| ModuleIdStrategy::default().resolved_cell())
666 }
667
668 #[turbo_tasks::function]
669 async fn async_loader_chunk_item(
670 self: Vc<Self>,
671 module: Vc<Box<dyn ChunkableModule>>,
672 module_graph: Vc<ModuleGraph>,
673 availability_info: AvailabilityInfo,
674 ) -> Result<Vc<Box<dyn ChunkItem>>> {
675 let chunking_context: ResolvedVc<Box<dyn ChunkingContext>> =
676 Vc::upcast::<Box<dyn ChunkingContext>>(self)
677 .to_resolved()
678 .await?;
679 Ok(if self.await?.manifest_chunks {
680 let manifest_asset = ManifestAsyncModule::new(
681 module,
682 module_graph,
683 *chunking_context,
684 availability_info,
685 )
686 .to_resolved()
687 .await?;
688 let loader_module = ManifestLoaderModule::new(*manifest_asset);
689 loader_module.as_chunk_item(module_graph, *chunking_context)
690 } else {
691 let module = AsyncLoaderModule::new(module, *chunking_context, availability_info);
692 module.as_chunk_item(module_graph, *chunking_context)
693 })
694 }
695
696 #[turbo_tasks::function]
697 async fn async_loader_chunk_item_ident(
698 self: Vc<Self>,
699 module: Vc<Box<dyn ChunkableModule>>,
700 ) -> Result<Vc<AssetIdent>> {
701 Ok(if self.await?.manifest_chunks {
702 ManifestLoaderModule::asset_ident_for(module)
703 } else {
704 AsyncLoaderModule::asset_ident_for(module)
705 })
706 }
707
708 #[turbo_tasks::function]
709 async fn module_export_usage(
710 &self,
711 module: ResolvedVc<Box<dyn Module>>,
712 ) -> Result<Vc<ModuleExportUsage>> {
713 if let Some(export_usage) = self.export_usage {
714 Ok(export_usage.await?.used_exports(module).await?)
715 } else {
716 Ok(ModuleExportUsage::all())
717 }
718 }
719
720 #[turbo_tasks::function]
721 fn unused_references(&self) -> Vc<UnusedReferences> {
722 if let Some(unused_references) = self.unused_references {
723 *unused_references
724 } else {
725 Vc::cell(Default::default())
726 }
727 }
728
729 #[turbo_tasks::function]
730 fn debug_ids_enabled(&self) -> Vc<bool> {
731 Vc::cell(self.debug_ids)
732 }
733
734 #[turbo_tasks::function]
735 fn worker_forwarded_globals(&self) -> Vc<Vec<RcStr>> {
736 Vc::cell(self.worker_forwarded_globals.clone())
737 }
738}