Skip to main content

turbopack_nodejs/
chunking_context.rs

1use anyhow::{Context, Result, bail};
2use smallvec::{SmallVec, smallvec};
3use tracing::Instrument;
4use turbo_rcstr::{RcStr, rcstr};
5use turbo_tasks::{
6    FxIndexMap, ResolvedVc, TryJoinIterExt, Upcast, ValueToString, ValueToStringRef, Vc,
7};
8use turbo_tasks_fs::FileSystemPath;
9use turbo_tasks_hash::HashAlgorithm;
10use turbopack_core::{
11    asset::{Asset, AssetContent},
12    chunk::{
13        AssetSuffix, Chunk, ChunkGroupResult, ChunkItem, ChunkItemOrBatchWithAsyncModuleInfo,
14        ChunkItemWithAsyncModuleInfo, ChunkType, ChunkableModule, ChunkingConfig, ChunkingConfigs,
15        ChunkingContext, ContentHashing, EntryChunkGroupResult, EvaluatableAsset, MinifyType,
16        SourceMapSourceType, SourceMapsType, UnusedReferences, UrlBehavior,
17        WorkerConfigurationOptions,
18        availability_info::AvailabilityInfo,
19        chunk_group::{MakeChunkGroupResult, make_chunk_group},
20        chunk_id_strategy::ModuleIdStrategy,
21    },
22    environment::Environment,
23    ident::AssetIdent,
24    module::Module,
25    module_graph::{
26        ModuleGraph,
27        binding_usage_info::{BindingUsageInfo, ModuleExportUsage},
28        chunk_group_info::ChunkGroup,
29    },
30    output::{OutputAsset, OutputAssets},
31};
32use turbopack_ecmascript::{
33    async_chunk::module::AsyncLoaderModule,
34    chunk::{EcmascriptChunk, EcmascriptChunkPlaceable},
35    manifest::{chunk_asset::ManifestAsyncModule, loader_module::ManifestLoaderModule},
36};
37use turbopack_ecmascript_runtime::RuntimeType;
38
39use crate::ecmascript::node::{
40    chunk::EcmascriptBuildNodeChunk,
41    entry::{chunk::EcmascriptBuildNodeEntryChunk, chunk_list::EcmascriptBuildNodeChunkList},
42};
43
44/// A builder for [`Vc<NodeJsChunkingContext>`].
45pub struct NodeJsChunkingContextBuilder {
46    chunking_context: NodeJsChunkingContext,
47}
48
49impl NodeJsChunkingContextBuilder {
50    pub fn asset_prefix(mut self, asset_prefix: Option<RcStr>) -> Self {
51        self.chunking_context.asset_prefix = asset_prefix;
52        self
53    }
54
55    pub fn asset_prefix_override(mut self, tag: RcStr, prefix: RcStr) -> Self {
56        self.chunking_context.asset_prefixes.insert(tag, prefix);
57        self
58    }
59
60    pub fn asset_root_path_override(mut self, tag: RcStr, path: FileSystemPath) -> Self {
61        self.chunking_context.asset_root_paths.insert(tag, path);
62        self
63    }
64
65    pub fn client_roots_override(mut self, tag: RcStr, path: FileSystemPath) -> Self {
66        self.chunking_context.client_roots.insert(tag, path);
67        self
68    }
69
70    pub fn url_behavior_override(mut self, tag: RcStr, behavior: UrlBehavior) -> Self {
71        self.chunking_context.url_behaviors.insert(tag, behavior);
72        self
73    }
74
75    pub fn default_url_behavior(mut self, behavior: UrlBehavior) -> Self {
76        self.chunking_context.default_url_behavior = Some(behavior);
77        self
78    }
79
80    pub fn minify_type(mut self, minify_type: MinifyType) -> Self {
81        self.chunking_context.minify_type = minify_type;
82        self
83    }
84
85    pub fn source_maps(mut self, source_maps: SourceMapsType) -> Self {
86        self.chunking_context.source_maps_type = source_maps;
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    /// Marks this context as being shared by multiple independent module graphs, each of which
172    /// only sees part of what is written to `chunk_root_path`.
173    ///
174    /// The runtime chunk is emitted to a fixed path (`[turbopack]_runtime.js`), so every graph
175    /// sharing this context writes the same file. Optional runtime features must therefore not be
176    /// decided from a single graph: one graph would omit a helper that another graph's chunks
177    /// call, and which variant lands on disk depends on emission order.
178    pub fn shared_runtime_chunk(mut self, shared_runtime_chunk: bool) -> Self {
179        self.chunking_context.shared_runtime_chunk = shared_runtime_chunk;
180        self
181    }
182
183    /// Builds the chunking context.
184    pub fn build(self) -> Vc<NodeJsChunkingContext> {
185        NodeJsChunkingContext::cell(self.chunking_context)
186    }
187}
188
189/// A chunking context for build mode.
190#[turbo_tasks::value]
191#[derive(Debug, Clone)]
192pub struct NodeJsChunkingContext {
193    /// The root path of the project
194    root_path: FileSystemPath,
195    /// This path is used to compute the url to request chunks or assets from
196    output_root: FileSystemPath,
197    /// The relative path from the output_root to the root_path.
198    output_root_to_root_path: RcStr,
199    /// This path is used to compute the url to request chunks or assets from
200    client_root: FileSystemPath,
201    /// This path is used to compute the url to request chunks or assets from
202    #[bincode(with = "turbo_bincode::indexmap")]
203    client_roots: FxIndexMap<RcStr, FileSystemPath>,
204    /// Chunks are placed at this path
205    chunk_root_path: FileSystemPath,
206    /// Static assets are placed at this path
207    asset_root_path: FileSystemPath,
208    /// Static assets are placed at this path
209    #[bincode(with = "turbo_bincode::indexmap")]
210    asset_root_paths: FxIndexMap<RcStr, FileSystemPath>,
211    /// Static assets requested from this url base
212    asset_prefix: Option<RcStr>,
213    /// Static assets requested from this url base
214    #[bincode(with = "turbo_bincode::indexmap")]
215    asset_prefixes: FxIndexMap<RcStr, RcStr>,
216    /// URL behavior overrides for different tags.
217    #[bincode(with = "turbo_bincode::indexmap")]
218    url_behaviors: FxIndexMap<RcStr, UrlBehavior>,
219    /// Default URL behavior when no tag-specific override is found.
220    default_url_behavior: Option<UrlBehavior>,
221    /// The environment chunks will be evaluated in.
222    environment: ResolvedVc<Environment>,
223    /// The kind of runtime to include in the output.
224    runtime_type: RuntimeType,
225    /// Enable nested async availability for this chunking
226    enable_nested_async_availability: bool,
227    /// Enable module merging
228    enable_module_merging: bool,
229    /// Enable dynamic chunk content loading.
230    enable_dynamic_chunk_content_loading: bool,
231    /// Whether to minify resulting chunks
232    minify_type: MinifyType,
233    /// Whether to generate source maps
234    source_maps_type: SourceMapsType,
235    /// Whether to use manifest chunks for lazy compilation
236    manifest_chunks: bool,
237    /// The strategy to use for generating module ids
238    module_id_strategy: Option<ResolvedVc<ModuleIdStrategy>>,
239    /// The module export usage info, if available.
240    export_usage: Option<ResolvedVc<BindingUsageInfo>>,
241    /// Which references are unused and should be skipped (e.g. during codegen).
242    unused_references: Option<ResolvedVc<UnusedReferences>>,
243    /// The strategy to use for generating source map source uris
244    source_map_source_type: SourceMapSourceType,
245    /// The chunking configs
246    chunking_configs: Vec<(ResolvedVc<Box<dyn ChunkType>>, ChunkingConfig)>,
247    /// Enable debug IDs for chunks and source maps.
248    debug_ids: bool,
249    /// Global variable names to forward to workers (e.g. NEXT_DEPLOYMENT_ID)
250    worker_forwarded_globals: Vec<RcStr>,
251    /// Content hashing for asset filenames.
252    asset_content_hashing: ContentHashing,
253    /// Salt mixed into chunk and asset content hashes. Empty string means no salt.
254    hash_salt: ResolvedVc<RcStr>,
255    /// Whether the runtime chunk is shared with other module graphs using this context.
256    /// See [`NodeJsChunkingContextBuilder::shared_runtime_chunk`].
257    shared_runtime_chunk: bool,
258}
259
260impl NodeJsChunkingContext {
261    /// Creates a new chunking context builder.
262    pub fn builder(
263        root_path: FileSystemPath,
264        output_root: FileSystemPath,
265        output_root_to_root_path: RcStr,
266        client_root: FileSystemPath,
267        chunk_root_path: FileSystemPath,
268        asset_root_path: FileSystemPath,
269        environment: ResolvedVc<Environment>,
270        runtime_type: RuntimeType,
271    ) -> NodeJsChunkingContextBuilder {
272        NodeJsChunkingContextBuilder {
273            chunking_context: NodeJsChunkingContext {
274                root_path,
275                output_root,
276                output_root_to_root_path,
277                client_root,
278                client_roots: Default::default(),
279                chunk_root_path,
280                asset_root_path,
281                asset_root_paths: Default::default(),
282                asset_prefix: None,
283                asset_prefixes: Default::default(),
284                url_behaviors: Default::default(),
285                default_url_behavior: None,
286                enable_nested_async_availability: false,
287                enable_module_merging: false,
288                enable_dynamic_chunk_content_loading: false,
289                environment,
290                runtime_type,
291                minify_type: MinifyType::NoMinify,
292                source_maps_type: SourceMapsType::Full,
293                manifest_chunks: false,
294                source_map_source_type: SourceMapSourceType::TurbopackUri,
295                module_id_strategy: None,
296                export_usage: None,
297                unused_references: None,
298                chunking_configs: Default::default(),
299                debug_ids: false,
300                worker_forwarded_globals: vec![],
301                asset_content_hashing: ContentHashing::Direct { length: 13 },
302                hash_salt: ResolvedVc::cell(RcStr::default()),
303                shared_runtime_chunk: false,
304            },
305        }
306    }
307}
308
309#[turbo_tasks::value_impl]
310impl NodeJsChunkingContext {
311    /// Returns the kind of runtime to include in output chunks.
312    ///
313    /// This is defined directly on `NodeJsChunkingContext` so it is zero-cost
314    /// when `RuntimeType` has a single variant.
315    #[turbo_tasks::function]
316    pub fn runtime_type(&self) -> Vc<RuntimeType> {
317        self.runtime_type.cell()
318    }
319
320    /// Returns the minify type.
321    #[turbo_tasks::function]
322    pub fn minify_type(&self) -> Vc<MinifyType> {
323        self.minify_type.cell()
324    }
325
326    #[turbo_tasks::function]
327    pub fn hash_salt(&self) -> Vc<RcStr> {
328        *self.hash_salt
329    }
330
331    #[turbo_tasks::function]
332    pub fn asset_prefix(&self) -> Vc<Option<RcStr>> {
333        Vc::cell(self.asset_prefix.clone())
334    }
335
336    /// Creates a standalone server-HMR tracking anchor at `path` covering
337    /// `chunks`, without producing an evaluate chunk.
338    ///
339    /// Unlike the browser's `hmr_chunk_list`, the caller supplies an explicit
340    /// output `path` so the anchor can be placed alongside the App Router
341    /// entries it belongs to (under `server/app/`). This is what lets the
342    /// aggregate server-HMR subscription scope tracking to App Router: the
343    /// anchor for client-component SSR chunks (which are physically emitted
344    /// under the shared `server/chunks/ssr/`) is registered under the app
345    /// entry's directory so it rides the same App Router scope.
346    #[turbo_tasks::function]
347    pub async fn server_hmr_chunk_list(
348        self: ResolvedVc<Self>,
349        path: FileSystemPath,
350        chunks: Vc<OutputAssets>,
351    ) -> Result<Vc<Box<dyn OutputAsset>>> {
352        #[cfg(debug_assertions)]
353        if !matches!(*self.runtime_type().await?, RuntimeType::Development) {
354            bail!("server_hmr_chunk_list can only be used in development");
355        }
356        Ok(Vc::upcast(EcmascriptBuildNodeChunkList::new(
357            *self, path, chunks,
358        )))
359    }
360
361    /// Whether the runtime chunk is shared with other module graphs using this context, meaning no
362    /// single graph may decide which optional runtime features to omit.
363    /// See [`NodeJsChunkingContextBuilder::shared_runtime_chunk`].
364    #[turbo_tasks::function]
365    pub fn shared_runtime_chunk(&self) -> Vc<bool> {
366        Vc::cell(self.shared_runtime_chunk)
367    }
368}
369
370impl NodeJsChunkingContext {
371    async fn generate_chunk(
372        self: Vc<Self>,
373        chunk: ResolvedVc<Box<dyn Chunk>>,
374    ) -> Result<ResolvedVc<Box<dyn OutputAsset>>> {
375        Ok(
376            if let Some(ecmascript_chunk) = ResolvedVc::try_downcast_type::<EcmascriptChunk>(chunk)
377            {
378                ResolvedVc::upcast(
379                    EcmascriptBuildNodeChunk::new(self, *ecmascript_chunk)
380                        .to_resolved()
381                        .await?,
382                )
383            } else if let Some(output_asset) =
384                ResolvedVc::try_sidecast::<Box<dyn OutputAsset>>(chunk)
385            {
386                output_asset
387            } else {
388                bail!("Unable to generate output asset for chunk");
389            },
390        )
391    }
392}
393
394#[turbo_tasks::value_impl]
395impl ChunkingContext for NodeJsChunkingContext {
396    #[turbo_tasks::function]
397    fn name(&self) -> Vc<RcStr> {
398        Vc::cell(rcstr!("unknown"))
399    }
400
401    #[turbo_tasks::function]
402    fn root_path(&self) -> Vc<FileSystemPath> {
403        self.root_path.clone().cell()
404    }
405
406    #[turbo_tasks::function]
407    fn output_root(&self) -> Vc<FileSystemPath> {
408        self.output_root.clone().cell()
409    }
410
411    #[turbo_tasks::function]
412    fn output_root_to_root_path(&self) -> Vc<RcStr> {
413        Vc::cell(self.output_root_to_root_path.clone())
414    }
415
416    #[turbo_tasks::function]
417    fn environment(&self) -> Vc<Environment> {
418        *self.environment
419    }
420
421    #[turbo_tasks::function]
422    fn is_nested_async_availability_enabled(&self) -> Vc<bool> {
423        Vc::cell(self.enable_nested_async_availability)
424    }
425
426    #[turbo_tasks::function]
427    fn is_module_merging_enabled(&self) -> Vc<bool> {
428        Vc::cell(self.enable_module_merging)
429    }
430
431    #[turbo_tasks::function]
432    fn is_dynamic_chunk_content_loading_enabled(&self) -> Vc<bool> {
433        Vc::cell(self.enable_dynamic_chunk_content_loading)
434    }
435
436    #[turbo_tasks::function]
437    pub fn minify_type(&self) -> Vc<MinifyType> {
438        self.minify_type.cell()
439    }
440
441    #[turbo_tasks::function]
442    async fn asset_url(&self, ident: FileSystemPath, tag: Option<RcStr>) -> Result<Vc<RcStr>> {
443        let asset_path = ident.to_string();
444
445        let client_root = tag
446            .as_ref()
447            .and_then(|tag| self.client_roots.get(tag))
448            .unwrap_or(&self.client_root);
449
450        let asset_prefix = tag
451            .as_ref()
452            .and_then(|tag| self.asset_prefixes.get(tag))
453            .or(self.asset_prefix.as_ref());
454
455        let asset_path = asset_path
456            .strip_prefix(&format!("{}/", client_root.path))
457            .context("expected client root to contain asset path")?;
458
459        Ok(Vc::cell(
460            format!(
461                "{}{}",
462                asset_prefix.map(|s| s.as_str()).unwrap_or("/"),
463                asset_path
464            )
465            .into(),
466        ))
467    }
468
469    #[turbo_tasks::function]
470    fn chunk_root_path(&self) -> Vc<FileSystemPath> {
471        self.chunk_root_path.clone().cell()
472    }
473
474    #[turbo_tasks::function]
475    async fn chunk_path(
476        &self,
477        _asset: Option<Vc<Box<dyn Asset>>>,
478        ident: Vc<AssetIdent>,
479        prefix: Option<RcStr>,
480        extension: RcStr,
481    ) -> Result<Vc<FileSystemPath>> {
482        let root_path = self.chunk_root_path.clone();
483        let name = ident
484            .output_name(self.root_path.clone(), prefix, extension)
485            .owned()
486            .await?;
487        Ok(root_path.join(&name)?.cell())
488    }
489
490    #[turbo_tasks::function]
491    fn reference_chunk_source_maps(&self, _chunk: Vc<Box<dyn OutputAsset>>) -> Vc<bool> {
492        Vc::cell(match self.source_maps_type {
493            SourceMapsType::Full => true,
494            SourceMapsType::Partial => true,
495            SourceMapsType::None => false,
496        })
497    }
498
499    #[turbo_tasks::function]
500    fn reference_module_source_maps(&self, _module: Vc<Box<dyn Module>>) -> Vc<bool> {
501        Vc::cell(match self.source_maps_type {
502            SourceMapsType::Full => true,
503            SourceMapsType::Partial => true,
504            SourceMapsType::None => false,
505        })
506    }
507
508    #[turbo_tasks::function]
509    fn source_map_source_type(&self) -> Vc<SourceMapSourceType> {
510        self.source_map_source_type.cell()
511    }
512
513    #[turbo_tasks::function]
514    fn chunking_configs(&self) -> Result<Vc<ChunkingConfigs>> {
515        Ok(Vc::cell(self.chunking_configs.iter().cloned().collect()))
516    }
517
518    #[turbo_tasks::function]
519    async fn asset_path(
520        self: Vc<Self>,
521        content: Vc<AssetContent>,
522        original_asset_ident: Vc<AssetIdent>,
523        tag: Option<RcStr>,
524    ) -> Result<Vc<FileSystemPath>> {
525        let this = self.await?;
526        let source_path = original_asset_ident.await?.path.clone();
527        let basename = source_path.file_name();
528        let ContentHashing::Direct { length } = this.asset_content_hashing;
529        let hash = content
530            .content_hash(self.hash_salt(), HashAlgorithm::Xxh3Hash128Base38)
531            .await?;
532        let hash = hash
533            .as_ref()
534            .context("Missing content when trying to generate the content hash for static asset")?;
535        let short_hash = &hash[..length as usize];
536        let asset_path = match source_path.extension() {
537            Some(ext) => format!(
538                "{basename}.{short_hash}.{ext}",
539                basename = &basename[..basename.len() - ext.len() - 1],
540            ),
541            None => format!("{basename}.{short_hash}"),
542        };
543
544        let asset_root_path = tag
545            .as_ref()
546            .and_then(|tag| this.asset_root_paths.get(tag))
547            .unwrap_or(&this.asset_root_path);
548
549        Ok(asset_root_path.join(&asset_path)?.cell())
550    }
551
552    #[turbo_tasks::function]
553    fn url_behavior(&self, tag: Option<RcStr>) -> Vc<UrlBehavior> {
554        tag.as_ref()
555            .and_then(|tag| self.url_behaviors.get(tag))
556            .cloned()
557            .or_else(|| self.default_url_behavior.clone())
558            .unwrap_or(UrlBehavior {
559                suffix: AssetSuffix::Inferred,
560                static_suffix: ResolvedVc::cell(None),
561            })
562            .cell()
563    }
564
565    #[turbo_tasks::function]
566    async fn chunk_group(
567        self: ResolvedVc<Self>,
568        ident: Vc<AssetIdent>,
569        chunk_groups: SmallVec<[ChunkGroup; 1]>,
570        module_graph: ResolvedVc<ModuleGraph>,
571        availability_info: AvailabilityInfo,
572    ) -> Result<Vc<ChunkGroupResult>> {
573        let span = tracing::info_span!("chunking", name = display(ident.to_string().await?));
574        async move {
575            let MakeChunkGroupResult {
576                chunks,
577                references,
578                availability_info,
579            } = make_chunk_group(
580                chunk_groups,
581                module_graph,
582                ResolvedVc::upcast(self),
583                availability_info,
584            )
585            .await?;
586
587            let chunks = chunks.await?;
588
589            let assets = chunks
590                .iter()
591                .map(|chunk| self.generate_chunk(*chunk))
592                .try_join()
593                .await?;
594
595            Ok(ChunkGroupResult {
596                assets: ResolvedVc::cell(assets),
597                referenced_assets: OutputAssets::empty_resolved(),
598                references: ResolvedVc::cell(references),
599                availability_info,
600                chunk_group_bootstrap_params: None,
601            }
602            .cell())
603        }
604        .instrument(span)
605        .await
606    }
607
608    #[turbo_tasks::function]
609    pub async fn entry_chunk_group(
610        self: ResolvedVc<Self>,
611        path: FileSystemPath,
612        chunk_group: ChunkGroup,
613        module_graph: ResolvedVc<ModuleGraph>,
614        extra_chunks: Vc<OutputAssets>,
615        extra_referenced_assets: Vc<OutputAssets>,
616        availability_info: AvailabilityInfo,
617    ) -> Result<Vc<EntryChunkGroupResult>> {
618        let span = tracing::info_span!(
619            "chunking",
620            name = display(path.to_string_ref().await?),
621            chunking_type = "entry",
622        );
623        async move {
624            let MakeChunkGroupResult {
625                chunks,
626                references,
627                availability_info,
628            } = make_chunk_group(
629                smallvec![chunk_group.clone()],
630                module_graph,
631                ResolvedVc::upcast(self),
632                availability_info,
633            )
634            .await?;
635
636            let chunks = chunks.await?;
637
638            let extra_chunks = extra_chunks.await?;
639            let mut other_chunks = chunks
640                .iter()
641                .map(|chunk| self.generate_chunk(*chunk))
642                .try_join()
643                .await?;
644            other_chunks.extend(extra_chunks.iter().copied());
645
646            let module = chunk_group.entries().last().unwrap();
647            let Some(module) =
648                ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(module)
649            else {
650                bail!("last entry must be EcmascriptChunkPlaceable {:?}", module);
651            };
652
653            let evaluatable_assets = chunk_group
654                .entries()
655                .map(|entry| {
656                    ResolvedVc::try_sidecast::<Box<dyn EvaluatableAsset>>(entry).with_context(
657                        || {
658                            format!(
659                                "entry_chunk_group entries must be EvaluatableAssets {:?}",
660                                entry
661                            )
662                        },
663                    )
664                })
665                .collect::<Result<Vec<_>>>()?;
666
667            let asset = ResolvedVc::upcast(
668                EcmascriptBuildNodeEntryChunk::new(
669                    path,
670                    Vc::cell(other_chunks),
671                    Vc::cell(evaluatable_assets),
672                    *module,
673                    extra_referenced_assets,
674                    Vc::cell(references),
675                    *module_graph,
676                    *self,
677                )
678                .to_resolved()
679                .await?,
680            );
681
682            Ok(EntryChunkGroupResult {
683                asset,
684                availability_info,
685            }
686            .cell())
687        }
688        .instrument(span)
689        .await
690    }
691
692    #[turbo_tasks::function]
693    fn evaluated_chunk_group(
694        self: Vc<Self>,
695        _ident: Vc<AssetIdent>,
696        _chunk_group: ChunkGroup,
697        _module_graph: Vc<ModuleGraph>,
698        _extra_chunks: Vc<OutputAssets>,
699        _availability_info: AvailabilityInfo,
700    ) -> Result<Vc<ChunkGroupResult>> {
701        bail!("the Node.js chunking context does not support evaluated chunk groups")
702    }
703
704    #[turbo_tasks::function]
705    fn chunk_item_id_strategy(&self) -> Vc<ModuleIdStrategy> {
706        *self
707            .module_id_strategy
708            .unwrap_or_else(|| ModuleIdStrategy::default().resolved_cell())
709    }
710
711    #[turbo_tasks::function]
712    async fn async_loader_chunk_item(
713        self: Vc<Self>,
714        module: Vc<Box<dyn ChunkableModule>>,
715        module_graph: Vc<ModuleGraph>,
716        availability_info: AvailabilityInfo,
717    ) -> Result<Vc<Box<dyn ChunkItem>>> {
718        let chunking_context: ResolvedVc<Box<dyn ChunkingContext>> =
719            Vc::upcast::<Box<dyn ChunkingContext>>(self)
720                .to_resolved()
721                .await?;
722        let use_manifest = self.await?.manifest_chunks
723            // This guard is in place so that only javascript goes
724            // this path for lazy loading dynamic imports not things like css.
725            && ResolvedVc::try_downcast::<Box<dyn EcmascriptChunkPlaceable>>(
726                module.to_resolved().await?,
727            )
728            .is_some();
729        Ok(if use_manifest {
730            let manifest_asset = ManifestAsyncModule::new(
731                module,
732                module_graph,
733                *chunking_context,
734                availability_info,
735            )
736            .to_resolved()
737            .await?;
738            let loader_module = ManifestLoaderModule::new(*manifest_asset);
739            loader_module.as_chunk_item(module_graph, *chunking_context)
740        } else {
741            let module = AsyncLoaderModule::new(module, *chunking_context, availability_info);
742            module.as_chunk_item(module_graph, *chunking_context)
743        })
744    }
745
746    #[turbo_tasks::function]
747    async fn standalone_chunk(
748        self: Vc<Self>,
749        chunk_item: ResolvedVc<Box<dyn ChunkItem>>,
750    ) -> Result<Vc<Box<dyn OutputAsset>>> {
751        let chunk_type = chunk_item
752            .into_trait_ref()
753            .await?
754            .ty()
755            .to_resolved()
756            .await?;
757        let chunk = chunk_type
758            .chunk(
759                Vc::upcast(self),
760                vec![ChunkItemOrBatchWithAsyncModuleInfo::ChunkItem(
761                    ChunkItemWithAsyncModuleInfo {
762                        chunk_item,
763                        chunk_type,
764                        module: None,
765                        async_info: None,
766                    },
767                )],
768                Vec::new(),
769                Vec::new(),
770            )
771            .to_resolved()
772            .await?;
773        Ok(*self.generate_chunk(chunk).await?)
774    }
775
776    #[turbo_tasks::function]
777    async fn async_loader_chunk_item_ident(
778        self: Vc<Self>,
779        module: Vc<Box<dyn ChunkableModule>>,
780    ) -> Result<Vc<AssetIdent>> {
781        let use_manifest = self.await?.manifest_chunks
782            // This guard is in place so that only javascript goes
783            // this path for lazy loading dynamic imports not things like css.
784            && ResolvedVc::try_downcast::<Box<dyn EcmascriptChunkPlaceable>>(
785                module.to_resolved().await?,
786            )
787            .is_some();
788        Ok(if use_manifest {
789            ManifestLoaderModule::asset_ident_for(module)
790        } else {
791            AsyncLoaderModule::asset_ident_for(module)
792        })
793    }
794
795    #[turbo_tasks::function]
796    async fn module_export_usage(
797        &self,
798        module: ResolvedVc<Box<dyn Module>>,
799    ) -> Result<Vc<ModuleExportUsage>> {
800        if let Some(export_usage) = self.export_usage {
801            Ok(export_usage.await?.used_exports(module).await?)
802        } else {
803            Ok(ModuleExportUsage::unknown())
804        }
805    }
806
807    #[turbo_tasks::function]
808    fn unused_references(&self) -> Vc<UnusedReferences> {
809        if let Some(unused_references) = self.unused_references {
810            *unused_references
811        } else {
812            Vc::cell(Default::default())
813        }
814    }
815
816    #[turbo_tasks::function]
817    fn debug_ids_enabled(&self) -> Vc<bool> {
818        Vc::cell(self.debug_ids)
819    }
820
821    #[turbo_tasks::function]
822    fn worker_configuration_options(&self) -> Vc<WorkerConfigurationOptions> {
823        WorkerConfigurationOptions {
824            asset_prefix: None,
825            forwarded_globals: self.worker_forwarded_globals.clone(),
826        }
827        .cell()
828    }
829}