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