Skip to main content

turbopack_nodejs/
chunking_context.rs

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