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