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