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