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