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