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