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