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,
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 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    /// Builds the chunking context.
174    pub fn build(self) -> Vc<NodeJsChunkingContext> {
175        NodeJsChunkingContext::cell(self.chunking_context)
176    }
177}
178
179/// A chunking context for build mode.
180#[turbo_tasks::value]
181#[derive(Debug, Clone)]
182pub struct NodeJsChunkingContext {
183    /// The root path of the project
184    root_path: FileSystemPath,
185    /// This path is used to compute the url to request chunks or assets from
186    output_root: FileSystemPath,
187    /// The relative path from the output_root to the root_path.
188    output_root_to_root_path: RcStr,
189    /// This path is used to compute the url to request chunks or assets from
190    client_root: FileSystemPath,
191    /// This path is used to compute the url to request chunks or assets from
192    #[bincode(with = "turbo_bincode::indexmap")]
193    client_roots: FxIndexMap<RcStr, FileSystemPath>,
194    /// Chunks are placed at this path
195    chunk_root_path: FileSystemPath,
196    /// Static assets are placed at this path
197    asset_root_path: FileSystemPath,
198    /// Static assets are placed at this path
199    #[bincode(with = "turbo_bincode::indexmap")]
200    asset_root_paths: FxIndexMap<RcStr, FileSystemPath>,
201    /// Static assets requested from this url base
202    asset_prefix: Option<RcStr>,
203    /// Static assets requested from this url base
204    #[bincode(with = "turbo_bincode::indexmap")]
205    asset_prefixes: FxIndexMap<RcStr, RcStr>,
206    /// URL behavior overrides for different tags.
207    #[bincode(with = "turbo_bincode::indexmap")]
208    url_behaviors: FxIndexMap<RcStr, UrlBehavior>,
209    /// Default URL behavior when no tag-specific override is found.
210    default_url_behavior: Option<UrlBehavior>,
211    /// The environment chunks will be evaluated in.
212    environment: ResolvedVc<Environment>,
213    /// The kind of runtime to include in the output.
214    runtime_type: RuntimeType,
215    /// Enable tracing for this chunking
216    enable_file_tracing: bool,
217    /// Enable nested async availability for this chunking
218    enable_nested_async_availability: bool,
219    /// Enable module merging
220    enable_module_merging: bool,
221    /// Enable dynamic chunk content loading.
222    enable_dynamic_chunk_content_loading: bool,
223    /// Whether to minify resulting chunks
224    minify_type: MinifyType,
225    /// Whether to generate source maps
226    source_maps_type: SourceMapsType,
227    /// Whether to use manifest chunks for lazy compilation
228    manifest_chunks: bool,
229    /// The strategy to use for generating module ids
230    module_id_strategy: Option<ResolvedVc<ModuleIdStrategy>>,
231    /// The module export usage info, if available.
232    export_usage: Option<ResolvedVc<BindingUsageInfo>>,
233    /// Which references are unused and should be skipped (e.g. during codegen).
234    unused_references: Option<ResolvedVc<UnusedReferences>>,
235    /// The strategy to use for generating source map source uris
236    source_map_source_type: SourceMapSourceType,
237    /// The chunking configs
238    chunking_configs: Vec<(ResolvedVc<Box<dyn ChunkType>>, ChunkingConfig)>,
239    /// Enable debug IDs for chunks and source maps.
240    debug_ids: bool,
241    /// Global variable names to forward to workers (e.g. NEXT_DEPLOYMENT_ID)
242    worker_forwarded_globals: Vec<RcStr>,
243    /// Content hashing for asset filenames.
244    asset_content_hashing: ContentHashing,
245    /// Salt mixed into chunk and asset content hashes. Empty string means no salt.
246    hash_salt: ResolvedVc<RcStr>,
247}
248
249impl NodeJsChunkingContext {
250    /// Creates a new chunking context builder.
251    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    /// Returns the kind of runtime to include in output chunks.
301    ///
302    /// This is defined directly on `NodeJsChunkingContext` so it is zero-cost
303    /// when `RuntimeType` has a single variant.
304    #[turbo_tasks::function]
305    pub fn runtime_type(&self) -> Vc<RuntimeType> {
306        self.runtime_type.cell()
307    }
308
309    /// Returns the minify type.
310    #[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}