Skip to main content

turbopack_nodejs/
chunking_context.rs

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