1use anyhow::{Context, Result, bail};
2use bincode::{Decode, Encode};
3use tracing::Instrument;
4use turbo_rcstr::{RcStr, rcstr};
5use turbo_tasks::{
6 FxIndexMap, NonLocalValue, ResolvedVc, TaskInput, TryJoinIterExt, Upcast, ValueToString, Vc,
7 trace::TraceRawVcs,
8};
9use turbo_tasks_fs::FileSystemPath;
10use turbo_tasks_hash::{DeterministicHash, hash_xxh3_hash64};
11use turbopack_core::{
12 asset::{Asset, AssetContent},
13 chunk::{
14 Chunk, ChunkGroupResult, ChunkItem, ChunkType, ChunkableModule, ChunkingConfig,
15 ChunkingConfigs, ChunkingContext, EntryChunkGroupResult, EvaluatableAsset,
16 EvaluatableAssets, MinifyType, ModuleId, SourceMapSourceType, SourceMapsType,
17 availability_info::AvailabilityInfo,
18 chunk_group::{MakeChunkGroupResult, make_chunk_group},
19 module_id_strategies::{DevModuleIdStrategy, 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 reference::ModuleReference,
31};
32use turbopack_ecmascript::{
33 async_chunk::module::AsyncLoaderModule,
34 chunk::EcmascriptChunk,
35 manifest::{chunk_asset::ManifestAsyncModule, loader_item::ManifestLoaderChunkItem},
36};
37use turbopack_ecmascript_runtime::{ChunkSuffix, RuntimeType};
38
39use crate::ecmascript::{
40 chunk::EcmascriptBrowserChunk,
41 evaluate::chunk::EcmascriptBrowserEvaluateChunk,
42 list::asset::{EcmascriptDevChunkList, EcmascriptDevChunkListSource},
43};
44
45#[turbo_tasks::value]
46#[derive(Debug, Clone, Copy, Hash, TaskInput)]
47pub enum CurrentChunkMethod {
48 StringLiteral,
49 DocumentCurrentScript,
50}
51
52pub const CURRENT_CHUNK_METHOD_DOCUMENT_CURRENT_SCRIPT_EXPR: &str =
53 "typeof document === \"object\" ? document.currentScript : undefined";
54
55#[derive(
56 Debug,
57 TaskInput,
58 Clone,
59 Copy,
60 PartialEq,
61 Eq,
62 Hash,
63 TraceRawVcs,
64 DeterministicHash,
65 NonLocalValue,
66 Encode,
67 Decode,
68)]
69pub enum ContentHashing {
70 Direct {
74 length: u8,
77 },
78}
79
80pub struct BrowserChunkingContextBuilder {
81 chunking_context: BrowserChunkingContext,
82}
83
84impl BrowserChunkingContextBuilder {
85 pub fn name(mut self, name: RcStr) -> Self {
86 self.chunking_context.name = Some(name);
87 self
88 }
89
90 pub fn hot_module_replacement(mut self) -> Self {
91 self.chunking_context.enable_hot_module_replacement = true;
92 self
93 }
94
95 pub fn source_map_source_type(mut self, source_map_source_type: SourceMapSourceType) -> Self {
96 self.chunking_context.source_map_source_type = source_map_source_type;
97 self
98 }
99
100 pub fn tracing(mut self, enable_tracing: bool) -> Self {
101 self.chunking_context.enable_tracing = enable_tracing;
102 self
103 }
104
105 pub fn nested_async_availability(mut self, enable_nested_async_availability: bool) -> Self {
106 self.chunking_context.enable_nested_async_availability = enable_nested_async_availability;
107 self
108 }
109
110 pub fn module_merging(mut self, enable_module_merging: bool) -> Self {
111 self.chunking_context.enable_module_merging = enable_module_merging;
112 self
113 }
114
115 pub fn dynamic_chunk_content_loading(
116 mut self,
117 enable_dynamic_chunk_content_loading: bool,
118 ) -> Self {
119 self.chunking_context.enable_dynamic_chunk_content_loading =
120 enable_dynamic_chunk_content_loading;
121 self
122 }
123
124 pub fn asset_base_path(mut self, asset_base_path: Option<RcStr>) -> Self {
125 self.chunking_context.asset_base_path = asset_base_path;
126 self
127 }
128
129 pub fn chunk_base_path(mut self, chunk_base_path: Option<RcStr>) -> Self {
130 self.chunking_context.chunk_base_path = chunk_base_path;
131 self
132 }
133
134 pub fn chunk_suffix(mut self, chunk_suffix: ResolvedVc<ChunkSuffix>) -> Self {
135 self.chunking_context.chunk_suffix = Some(chunk_suffix);
136 self
137 }
138
139 pub fn runtime_type(mut self, runtime_type: RuntimeType) -> Self {
140 self.chunking_context.runtime_type = runtime_type;
141 self
142 }
143
144 pub fn manifest_chunks(mut self, manifest_chunks: bool) -> Self {
145 self.chunking_context.manifest_chunks = manifest_chunks;
146 self
147 }
148
149 pub fn minify_type(mut self, minify_type: MinifyType) -> Self {
150 self.chunking_context.minify_type = minify_type;
151 self
152 }
153
154 pub fn source_maps(mut self, source_maps: SourceMapsType) -> Self {
155 self.chunking_context.source_maps_type = source_maps;
156 self
157 }
158
159 pub fn current_chunk_method(mut self, method: CurrentChunkMethod) -> Self {
160 self.chunking_context.current_chunk_method = method;
161 self
162 }
163
164 pub fn module_id_strategy(
165 mut self,
166 module_id_strategy: ResolvedVc<Box<dyn ModuleIdStrategy>>,
167 ) -> Self {
168 self.chunking_context.module_id_strategy = module_id_strategy;
169 self
170 }
171
172 pub fn export_usage(mut self, export_usage: Option<ResolvedVc<BindingUsageInfo>>) -> Self {
173 self.chunking_context.export_usage = export_usage;
174 self
175 }
176
177 pub fn unused_references(
178 mut self,
179 unused_references: Option<ResolvedVc<BindingUsageInfo>>,
180 ) -> Self {
181 self.chunking_context.unused_references = unused_references;
182 self
183 }
184
185 pub fn debug_ids(mut self, debug_ids: bool) -> Self {
186 self.chunking_context.debug_ids = debug_ids;
187 self
188 }
189
190 pub fn should_use_absolute_url_references(
191 mut self,
192 should_use_absolute_url_references: bool,
193 ) -> Self {
194 self.chunking_context.should_use_absolute_url_references =
195 should_use_absolute_url_references;
196 self
197 }
198
199 pub fn asset_root_path_override(mut self, tag: RcStr, path: FileSystemPath) -> Self {
200 self.chunking_context.asset_root_paths.insert(tag, path);
201 self
202 }
203
204 pub fn client_roots_override(mut self, tag: RcStr, path: FileSystemPath) -> Self {
205 self.chunking_context.client_roots.insert(tag, path);
206 self
207 }
208
209 pub fn asset_base_path_override(mut self, tag: RcStr, path: RcStr) -> Self {
210 self.chunking_context.asset_base_paths.insert(tag, path);
211 self
212 }
213
214 pub fn chunking_config<T>(mut self, ty: ResolvedVc<T>, chunking_config: ChunkingConfig) -> Self
215 where
216 T: Upcast<Box<dyn ChunkType>>,
217 {
218 self.chunking_context
219 .chunking_configs
220 .push((ResolvedVc::upcast_non_strict(ty), chunking_config));
221 self
222 }
223
224 pub fn use_content_hashing(mut self, content_hashing: ContentHashing) -> Self {
225 self.chunking_context.content_hashing = Some(content_hashing);
226 self
227 }
228
229 pub fn build(self) -> Vc<BrowserChunkingContext> {
230 BrowserChunkingContext::cell(self.chunking_context)
231 }
232}
233
234#[turbo_tasks::value]
241#[derive(Debug, Clone)]
242pub struct BrowserChunkingContext {
243 name: Option<RcStr>,
244 root_path: FileSystemPath,
246 source_map_source_type: SourceMapSourceType,
248 output_root: FileSystemPath,
250 output_root_to_root_path: RcStr,
252 client_root: FileSystemPath,
254 #[bincode(with = "turbo_bincode::indexmap")]
256 client_roots: FxIndexMap<RcStr, FileSystemPath>,
257 chunk_root_path: FileSystemPath,
259 asset_root_path: FileSystemPath,
261 #[bincode(with = "turbo_bincode::indexmap")]
263 asset_root_paths: FxIndexMap<RcStr, FileSystemPath>,
264 chunk_base_path: Option<RcStr>,
267 chunk_suffix: Option<ResolvedVc<ChunkSuffix>>,
270 asset_base_path: Option<RcStr>,
273 #[bincode(with = "turbo_bincode::indexmap")]
276 asset_base_paths: FxIndexMap<RcStr, RcStr>,
277 enable_hot_module_replacement: bool,
279 enable_tracing: bool,
281 enable_nested_async_availability: bool,
283 enable_module_merging: bool,
285 enable_dynamic_chunk_content_loading: bool,
287 debug_ids: bool,
289 environment: ResolvedVc<Environment>,
291 runtime_type: RuntimeType,
293 minify_type: MinifyType,
295 content_hashing: Option<ContentHashing>,
297 source_maps_type: SourceMapsType,
299 current_chunk_method: CurrentChunkMethod,
301 manifest_chunks: bool,
303 module_id_strategy: ResolvedVc<Box<dyn ModuleIdStrategy>>,
305 export_usage: Option<ResolvedVc<BindingUsageInfo>>,
307 unused_references: Option<ResolvedVc<BindingUsageInfo>>,
309 chunking_configs: Vec<(ResolvedVc<Box<dyn ChunkType>>, ChunkingConfig)>,
311 should_use_absolute_url_references: bool,
313}
314
315impl BrowserChunkingContext {
316 pub fn builder(
317 root_path: FileSystemPath,
318 output_root: FileSystemPath,
319 output_root_to_root_path: RcStr,
320 client_root: FileSystemPath,
321 chunk_root_path: FileSystemPath,
322 asset_root_path: FileSystemPath,
323 environment: ResolvedVc<Environment>,
324 runtime_type: RuntimeType,
325 ) -> BrowserChunkingContextBuilder {
326 BrowserChunkingContextBuilder {
327 chunking_context: BrowserChunkingContext {
328 name: None,
329 root_path,
330 output_root,
331 output_root_to_root_path,
332 client_root,
333 client_roots: Default::default(),
334 chunk_root_path,
335 source_map_source_type: SourceMapSourceType::TurbopackUri,
336 asset_root_path,
337 asset_root_paths: Default::default(),
338 chunk_base_path: None,
339 chunk_suffix: None,
340 asset_base_path: None,
341 asset_base_paths: Default::default(),
342 enable_hot_module_replacement: false,
343 enable_tracing: false,
344 enable_nested_async_availability: false,
345 enable_module_merging: false,
346 enable_dynamic_chunk_content_loading: false,
347 debug_ids: false,
348 environment,
349 runtime_type,
350 minify_type: MinifyType::NoMinify,
351 content_hashing: None,
352 source_maps_type: SourceMapsType::Full,
353 current_chunk_method: CurrentChunkMethod::StringLiteral,
354 manifest_chunks: false,
355 module_id_strategy: ResolvedVc::upcast(DevModuleIdStrategy::new_resolved()),
356 export_usage: None,
357 unused_references: None,
358 chunking_configs: Default::default(),
359 should_use_absolute_url_references: false,
360 },
361 }
362 }
363}
364
365#[turbo_tasks::value_impl]
366impl BrowserChunkingContext {
367 #[turbo_tasks::function]
368 fn generate_evaluate_chunk(
369 self: Vc<Self>,
370 ident: Vc<AssetIdent>,
371 other_chunks: Vc<OutputAssets>,
372 evaluatable_assets: Vc<EvaluatableAssets>,
373 module_graph: Vc<ModuleGraph>,
375 ) -> Vc<Box<dyn OutputAsset>> {
376 Vc::upcast(EcmascriptBrowserEvaluateChunk::new(
377 self,
378 ident,
379 other_chunks,
380 evaluatable_assets,
381 module_graph,
382 ))
383 }
384
385 #[turbo_tasks::function]
386 fn generate_chunk_list_register_chunk(
387 self: Vc<Self>,
388 ident: Vc<AssetIdent>,
389 evaluatable_assets: Vc<EvaluatableAssets>,
390 other_chunks: Vc<OutputAssets>,
391 source: EcmascriptDevChunkListSource,
392 ) -> Vc<Box<dyn OutputAsset>> {
393 Vc::upcast(EcmascriptDevChunkList::new(
394 self,
395 ident,
396 evaluatable_assets,
397 other_chunks,
398 source,
399 ))
400 }
401
402 #[turbo_tasks::function]
403 async fn generate_chunk(
404 self: Vc<Self>,
405 chunk: ResolvedVc<Box<dyn Chunk>>,
406 ) -> Result<Vc<Box<dyn OutputAsset>>> {
407 Ok(
408 if let Some(ecmascript_chunk) = ResolvedVc::try_downcast_type::<EcmascriptChunk>(chunk)
409 {
410 Vc::upcast(EcmascriptBrowserChunk::new(self, *ecmascript_chunk))
411 } else if let Some(output_asset) =
412 ResolvedVc::try_sidecast::<Box<dyn OutputAsset>>(chunk)
413 {
414 *output_asset
415 } else {
416 bail!("Unable to generate output asset for chunk");
417 },
418 )
419 }
420
421 #[turbo_tasks::function]
422 pub fn current_chunk_method(&self) -> Vc<CurrentChunkMethod> {
423 self.current_chunk_method.cell()
424 }
425
426 #[turbo_tasks::function]
431 pub fn runtime_type(&self) -> Vc<RuntimeType> {
432 self.runtime_type.cell()
433 }
434
435 #[turbo_tasks::function]
437 pub fn chunk_base_path(&self) -> Vc<Option<RcStr>> {
438 Vc::cell(self.chunk_base_path.clone())
439 }
440
441 #[turbo_tasks::function]
443 pub fn chunk_suffix(&self) -> Vc<ChunkSuffix> {
444 if let Some(chunk_suffix) = self.chunk_suffix {
445 *chunk_suffix
446 } else {
447 ChunkSuffix::None.cell()
448 }
449 }
450
451 #[turbo_tasks::function]
453 pub fn source_maps_type(&self) -> Vc<SourceMapsType> {
454 self.source_maps_type.cell()
455 }
456
457 #[turbo_tasks::function]
459 pub fn minify_type(&self) -> Vc<MinifyType> {
460 self.minify_type.cell()
461 }
462
463 #[turbo_tasks::function]
465 fn chunk_path_info(&self) -> Vc<ChunkPathInfo> {
466 ChunkPathInfo {
467 root_path: self.root_path.clone(),
468 chunk_root_path: self.chunk_root_path.clone(),
469 content_hashing: self.content_hashing,
470 }
471 .cell()
472 }
473}
474
475#[turbo_tasks::value_impl]
476impl ChunkingContext for BrowserChunkingContext {
477 #[turbo_tasks::function]
478 fn name(&self) -> Vc<RcStr> {
479 if let Some(name) = &self.name {
480 Vc::cell(name.clone())
481 } else {
482 Vc::cell(rcstr!("unknown"))
483 }
484 }
485
486 #[turbo_tasks::function]
487 fn root_path(&self) -> Vc<FileSystemPath> {
488 self.root_path.clone().cell()
489 }
490
491 #[turbo_tasks::function]
492 fn output_root(&self) -> Vc<FileSystemPath> {
493 self.output_root.clone().cell()
494 }
495
496 #[turbo_tasks::function]
497 fn output_root_to_root_path(&self) -> Vc<RcStr> {
498 Vc::cell(self.output_root_to_root_path.clone())
499 }
500
501 #[turbo_tasks::function]
502 fn environment(&self) -> Vc<Environment> {
503 *self.environment
504 }
505
506 #[turbo_tasks::function]
507 fn chunk_root_path(&self) -> Vc<FileSystemPath> {
508 self.chunk_root_path.clone().cell()
509 }
510
511 #[turbo_tasks::function]
512 async fn chunk_path(
513 self: Vc<Self>,
514 asset: Option<Vc<Box<dyn Asset>>>,
515 ident: Vc<AssetIdent>,
516 prefix: Option<RcStr>,
517 extension: RcStr,
518 ) -> Result<Vc<FileSystemPath>> {
519 debug_assert!(
520 extension.starts_with("."),
521 "`extension` should include the leading '.', got '{extension}'"
522 );
523 let ChunkPathInfo {
524 chunk_root_path,
525 content_hashing,
526 root_path,
527 } = &*self.chunk_path_info().await?;
528 let name = match *content_hashing {
529 None => {
530 ident
531 .output_name(root_path.clone(), prefix, extension)
532 .owned()
533 .await?
534 }
535 Some(ContentHashing::Direct { length }) => {
536 let Some(asset) = asset else {
537 bail!("chunk_path requires an asset when content hashing is enabled");
538 };
539 let content = asset.content().await?;
540 if let AssetContent::File(file) = &*content {
541 let hash = hash_xxh3_hash64(&file.await?);
542 let length = length as usize;
543 if let Some(prefix) = prefix {
544 format!("{prefix}-{hash:0length$x}{extension}").into()
545 } else {
546 format!("{hash:0length$x}{extension}").into()
547 }
548 } else {
549 bail!(
550 "chunk_path requires an asset with file content when content hashing is \
551 enabled"
552 );
553 }
554 }
555 };
556 Ok(chunk_root_path.join(&name)?.cell())
557 }
558
559 #[turbo_tasks::function]
560 async fn asset_url(&self, ident: FileSystemPath, tag: Option<RcStr>) -> Result<Vc<RcStr>> {
561 let asset_path = ident.to_string();
562
563 let client_root = tag
564 .as_ref()
565 .and_then(|tag| self.client_roots.get(tag))
566 .unwrap_or(&self.client_root);
567
568 let asset_base_path = tag
569 .as_ref()
570 .and_then(|tag| self.asset_base_paths.get(tag))
571 .or(self.asset_base_path.as_ref());
572
573 let asset_path = asset_path
574 .strip_prefix(&format!("{}/", client_root.path))
575 .context("expected asset_path to contain client_root")?;
576
577 Ok(Vc::cell(
578 format!(
579 "{}{}",
580 asset_base_path.map(|s| s.as_str()).unwrap_or("/"),
581 asset_path
582 )
583 .into(),
584 ))
585 }
586
587 #[turbo_tasks::function]
588 fn reference_chunk_source_maps(&self, _chunk: Vc<Box<dyn OutputAsset>>) -> Vc<bool> {
589 Vc::cell(match self.source_maps_type {
590 SourceMapsType::Full => true,
591 SourceMapsType::Partial => true,
592 SourceMapsType::None => false,
593 })
594 }
595
596 #[turbo_tasks::function]
597 fn reference_module_source_maps(&self, _module: Vc<Box<dyn Module>>) -> Vc<bool> {
598 Vc::cell(match self.source_maps_type {
599 SourceMapsType::Full => true,
600 SourceMapsType::Partial => true,
601 SourceMapsType::None => false,
602 })
603 }
604
605 #[turbo_tasks::function]
606 async fn asset_path(
607 &self,
608 content_hash: RcStr,
609 original_asset_ident: Vc<AssetIdent>,
610 tag: Option<RcStr>,
611 ) -> Result<Vc<FileSystemPath>> {
612 let source_path = original_asset_ident.path().await?;
613 let basename = source_path.file_name();
614 let asset_path = match source_path.extension_ref() {
615 Some(ext) => format!(
616 "{basename}.{content_hash}.{ext}",
617 basename = &basename[..basename.len() - ext.len() - 1],
618 content_hash = &content_hash[..8]
619 ),
620 None => format!(
621 "{basename}.{content_hash}",
622 content_hash = &content_hash[..8]
623 ),
624 };
625
626 let asset_root_path = tag
627 .as_ref()
628 .and_then(|tag| self.asset_root_paths.get(tag))
629 .unwrap_or(&self.asset_root_path);
630
631 Ok(asset_root_path.join(&asset_path)?.cell())
632 }
633
634 #[turbo_tasks::function]
635 fn is_hot_module_replacement_enabled(&self) -> Vc<bool> {
636 Vc::cell(self.enable_hot_module_replacement)
637 }
638
639 #[turbo_tasks::function]
640 fn chunking_configs(&self) -> Result<Vc<ChunkingConfigs>> {
641 Ok(Vc::cell(self.chunking_configs.iter().cloned().collect()))
642 }
643
644 #[turbo_tasks::function]
645 fn source_map_source_type(&self) -> Vc<SourceMapSourceType> {
646 self.source_map_source_type.cell()
647 }
648
649 #[turbo_tasks::function]
650 fn is_tracing_enabled(&self) -> Vc<bool> {
651 Vc::cell(self.enable_tracing)
652 }
653
654 #[turbo_tasks::function]
655 fn is_nested_async_availability_enabled(&self) -> Vc<bool> {
656 Vc::cell(self.enable_nested_async_availability)
657 }
658
659 #[turbo_tasks::function]
660 fn is_module_merging_enabled(&self) -> Vc<bool> {
661 Vc::cell(self.enable_module_merging)
662 }
663
664 #[turbo_tasks::function]
665 fn is_dynamic_chunk_content_loading_enabled(&self) -> Vc<bool> {
666 Vc::cell(self.enable_dynamic_chunk_content_loading)
667 }
668
669 #[turbo_tasks::function]
670 pub fn minify_type(&self) -> Vc<MinifyType> {
671 self.minify_type.cell()
672 }
673
674 #[turbo_tasks::function]
675 fn should_use_absolute_url_references(&self) -> Vc<bool> {
676 Vc::cell(self.should_use_absolute_url_references)
677 }
678
679 #[turbo_tasks::function]
680 async fn chunk_group(
681 self: ResolvedVc<Self>,
682 ident: Vc<AssetIdent>,
683 chunk_group: ChunkGroup,
684 module_graph: Vc<ModuleGraph>,
685 availability_info: AvailabilityInfo,
686 ) -> Result<Vc<ChunkGroupResult>> {
687 let span = tracing::info_span!("chunking", name = display(ident.to_string().await?));
688 async move {
689 let this = self.await?;
690 let entries = chunk_group.entries();
691 let input_availability_info = availability_info;
692 let MakeChunkGroupResult {
693 chunks,
694 referenced_output_assets,
695 references,
696 availability_info,
697 } = make_chunk_group(
698 entries,
699 module_graph,
700 ResolvedVc::upcast(self),
701 input_availability_info,
702 )
703 .await?;
704
705 let mut assets = chunks
706 .iter()
707 .map(|chunk| self.generate_chunk(**chunk).to_resolved())
708 .try_join()
709 .await?;
710
711 if this.enable_hot_module_replacement {
712 let mut ident = ident;
713 if let Some(input_availability_info_ident) = input_availability_info.ident().await?
714 {
715 ident = ident.with_modifier(input_availability_info_ident);
716 }
717 let other_assets = Vc::cell(assets.clone());
718 assets.push(
719 self.generate_chunk_list_register_chunk(
720 ident,
721 EvaluatableAssets::empty(),
722 other_assets,
723 EcmascriptDevChunkListSource::Dynamic,
724 )
725 .to_resolved()
726 .await?,
727 );
728 }
729
730 Ok(ChunkGroupResult {
731 assets: ResolvedVc::cell(assets),
732 referenced_assets: ResolvedVc::cell(referenced_output_assets),
733 references: ResolvedVc::cell(references),
734 availability_info,
735 }
736 .cell())
737 }
738 .instrument(span)
739 .await
740 }
741
742 #[turbo_tasks::function]
743 async fn evaluated_chunk_group(
744 self: ResolvedVc<Self>,
745 ident: Vc<AssetIdent>,
746 chunk_group: ChunkGroup,
747 module_graph: Vc<ModuleGraph>,
748 input_availability_info: AvailabilityInfo,
749 ) -> Result<Vc<ChunkGroupResult>> {
750 let span = tracing::info_span!(
751 "chunking",
752 name = display(ident.to_string().await?),
753 chunking_type = "evaluated",
754 );
755 async move {
756 let this = self.await?;
757 let entries = chunk_group.entries();
758 let MakeChunkGroupResult {
759 chunks,
760 referenced_output_assets,
761 references,
762 availability_info,
763 } = make_chunk_group(
764 entries,
765 module_graph,
766 ResolvedVc::upcast(self),
767 input_availability_info,
768 )
769 .await?;
770
771 let mut assets: Vec<ResolvedVc<Box<dyn OutputAsset>>> = chunks
772 .iter()
773 .map(|chunk| self.generate_chunk(**chunk).to_resolved())
774 .try_join()
775 .await?;
776
777 let other_assets = Vc::cell(assets.clone());
778
779 let entries = Vc::cell(
780 chunk_group
781 .entries()
782 .map(|m| {
783 ResolvedVc::try_downcast::<Box<dyn EvaluatableAsset>>(m)
784 .context("evaluated_chunk_group entries must be evaluatable assets")
785 })
786 .collect::<Result<Vec<_>>>()?,
787 );
788
789 if this.enable_hot_module_replacement {
790 let mut ident = ident;
791 if let Some(input_availability_info_ident) = input_availability_info.ident().await?
792 {
793 ident = ident.with_modifier(input_availability_info_ident);
794 }
795 assets.push(
796 self.generate_chunk_list_register_chunk(
797 ident,
798 entries,
799 other_assets,
800 EcmascriptDevChunkListSource::Entry,
801 )
802 .to_resolved()
803 .await?,
804 );
805 }
806
807 assets.push(
808 self.generate_evaluate_chunk(ident, other_assets, entries, module_graph)
809 .to_resolved()
810 .await?,
811 );
812
813 Ok(ChunkGroupResult {
814 assets: ResolvedVc::cell(assets),
815 referenced_assets: ResolvedVc::cell(referenced_output_assets),
816 references: ResolvedVc::cell(references),
817 availability_info,
818 }
819 .cell())
820 }
821 .instrument(span)
822 .await
823 }
824
825 #[turbo_tasks::function]
826 fn entry_chunk_group(
827 self: Vc<Self>,
828 _path: FileSystemPath,
829 _evaluatable_assets: Vc<EvaluatableAssets>,
830 _module_graph: Vc<ModuleGraph>,
831 _extra_chunks: Vc<OutputAssets>,
832 _extra_referenced_assets: Vc<OutputAssets>,
833 _availability_info: AvailabilityInfo,
834 ) -> Result<Vc<EntryChunkGroupResult>> {
835 bail!("Browser chunking context does not support entry chunk groups")
836 }
837
838 #[turbo_tasks::function]
839 fn chunk_item_id_from_ident(&self, ident: Vc<AssetIdent>) -> Vc<ModuleId> {
840 self.module_id_strategy.get_module_id(ident)
841 }
842
843 #[turbo_tasks::function]
844 async fn async_loader_chunk_item(
845 self: Vc<Self>,
846 module: Vc<Box<dyn ChunkableModule>>,
847 module_graph: Vc<ModuleGraph>,
848 availability_info: AvailabilityInfo,
849 ) -> Result<Vc<Box<dyn ChunkItem>>> {
850 Ok(if self.await?.manifest_chunks {
851 let manifest_asset =
852 ManifestAsyncModule::new(module, module_graph, Vc::upcast(self), availability_info);
853 Vc::upcast(ManifestLoaderChunkItem::new(
854 manifest_asset,
855 module_graph,
856 Vc::upcast(self),
857 ))
858 } else {
859 let module = AsyncLoaderModule::new(module, Vc::upcast(self), availability_info);
860 module.as_chunk_item(module_graph, Vc::upcast(self))
861 })
862 }
863
864 #[turbo_tasks::function]
865 async fn async_loader_chunk_item_id(
866 self: Vc<Self>,
867 module: Vc<Box<dyn ChunkableModule>>,
868 ) -> Result<Vc<ModuleId>> {
869 Ok(if self.await?.manifest_chunks {
870 self.chunk_item_id_from_ident(ManifestLoaderChunkItem::asset_ident_for(module))
871 } else {
872 self.chunk_item_id_from_ident(AsyncLoaderModule::asset_ident_for(module))
873 })
874 }
875
876 #[turbo_tasks::function]
877 async fn module_export_usage(
878 self: Vc<Self>,
879 module: ResolvedVc<Box<dyn Module>>,
880 ) -> Result<Vc<ModuleExportUsage>> {
881 if let Some(export_usage) = self.await?.export_usage {
882 Ok(export_usage.await?.used_exports(module).await?)
883 } else {
884 Ok(ModuleExportUsage::all())
885 }
886 }
887
888 #[turbo_tasks::function]
889 async fn is_reference_unused(
890 self: Vc<Self>,
891 reference: ResolvedVc<Box<dyn ModuleReference>>,
892 ) -> Result<Vc<bool>> {
893 if let Some(unused_references) = self.await?.unused_references {
894 Ok(Vc::cell(
895 unused_references.await?.is_reference_unused(&reference),
896 ))
897 } else {
898 Ok(Vc::cell(false))
899 }
900 }
901
902 #[turbo_tasks::function]
903 async fn debug_ids_enabled(self: Vc<Self>) -> Result<Vc<bool>> {
904 Ok(Vc::cell(self.await?.debug_ids))
905 }
906}
907
908#[turbo_tasks::value]
909struct ChunkPathInfo {
910 root_path: FileSystemPath,
911 chunk_root_path: FileSystemPath,
912 content_hashing: Option<ContentHashing>,
913}