1#![feature(box_patterns)]
2#![feature(trivial_bounds)]
3#![feature(min_specialization)]
4#![feature(map_try_insert)]
5#![feature(hash_set_entry)]
6#![recursion_limit = "256"]
7#![feature(arbitrary_self_types)]
8#![feature(arbitrary_self_types_pointers)]
9
10pub mod evaluate_context;
11pub mod global_module_ids;
12pub mod module_options;
13pub mod transition;
14
15use anyhow::{Context as _, Result, bail};
16use module_options::{
17 ConfiguredModuleType, ModuleOptions, ModuleOptionsContext, ModuleRuleEffect, ModuleType,
18};
19use tracing::{Instrument, field::Empty};
20use turbo_rcstr::{RcStr, rcstr};
21use turbo_tasks::{ResolvedVc, TryJoinIterExt, ValueToString, Vc};
22use turbo_tasks_fs::FileSystemPath;
23pub use turbopack_core::condition;
24use turbopack_core::{
25 asset::Asset,
26 chunk::SourceMapsType,
27 compile_time_info::CompileTimeInfo,
28 context::{AssetContext, ProcessResult},
29 ident::{AssetIdent, Layer},
30 issue::{IssueExt, IssueSource, module::ModuleIssue},
31 module::{Module, ModuleSideEffects},
32 node_addon_module::NodeAddonModule,
33 output::{ExpandedOutputAssets, OutputAsset},
34 raw_module::RawModule,
35 reference_type::{
36 CssReferenceSubType, EcmaScriptModulesReferenceSubType, InnerAssets, ReferenceType,
37 },
38 resolve::{
39 ExternalTraced, ExternalType, ModulePart, ModuleResolveResult, ModuleResolveResultItem,
40 ResolveResult, ResolveResultItem, options::ResolveOptions, origin::PlainResolveOrigin,
41 parse::Request, resolve,
42 },
43 source::Source,
44 source_transform::SourceTransforms,
45};
46use turbopack_css::{CssModule, EcmascriptCssModule};
47use turbopack_ecmascript::{
48 AnalyzeMode, EcmascriptInputTransforms, EcmascriptModuleAsset, EcmascriptModuleAssetType,
49 EcmascriptOptions, TreeShakingMode,
50 chunk::EcmascriptChunkPlaceable,
51 references::{
52 FollowExportsResult,
53 external_module::{CachedExternalModule, CachedExternalTracingMode, CachedExternalType},
54 follow_reexports,
55 },
56 rename::module::EcmascriptModuleRenameModule,
57 side_effect_optimization::{
58 facade::module::EcmascriptModuleFacadeModule, locals::module::EcmascriptModuleLocalsModule,
59 },
60 tree_shake::part::module::EcmascriptModulePartAsset,
61};
62use turbopack_node::transforms::webpack::{WebpackLoaderItem, WebpackLoaderItems, WebpackLoaders};
63use turbopack_resolve::{
64 resolve::resolve_options, resolve_options_context::ResolveOptionsContext,
65 typescript::type_resolve,
66};
67use turbopack_static::{css::StaticUrlCssModule, ecma::StaticUrlJsModule};
68use turbopack_wasm::{module_asset::WebAssemblyModuleAsset, source::WebAssemblySource};
69
70use crate::{
71 evaluate_context::node_evaluate_asset_context,
72 module_options::{
73 CssOptionsContext, CustomModuleType, EcmascriptOptionsContext, TypescriptTransformOptions,
74 package_import_map_from_context, package_import_map_from_import_mapping,
75 },
76 transition::{Transition, TransitionOptions},
77};
78
79async fn apply_module_type(
80 source: ResolvedVc<Box<dyn Source>>,
81 module_asset_context: Vc<ModuleAssetContext>,
82 module_type: Vc<ModuleType>,
83 reference_type: ReferenceType,
84 inner_assets: Option<ResolvedVc<InnerAssets>>,
85) -> Result<Vc<ProcessResult>> {
86 let tree_shaking_mode = module_asset_context
87 .module_options_context()
88 .await?
89 .tree_shaking_mode;
90 let part = match &reference_type {
91 ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::ImportPart(part)) => {
92 Some(part)
93 }
94 _ => None,
95 };
96 let css_import_context = match reference_type {
97 ReferenceType::Css(CssReferenceSubType::AtImport(import)) => import,
98 _ => None,
99 };
100 let is_evaluation = matches!(&part, Some(ModulePart::Evaluation));
101
102 let module_type = &*module_type.await?;
103 let module = match module_type {
104 ModuleType::Ecmascript {
105 preprocess,
106 main,
107 postprocess,
108 options,
109 }
110 | ModuleType::EcmascriptExtensionless {
111 preprocess,
112 main,
113 postprocess,
114 options,
115 }
116 | ModuleType::Typescript {
117 preprocess,
118 main,
119 postprocess,
120 tsx: _,
121 analyze_types: _,
122 options,
123 }
124 | ModuleType::TypescriptDeclaration {
125 preprocess,
126 main,
127 postprocess,
128 options,
129 } => {
130 let context_for_module = match module_type {
131 ModuleType::Typescript { analyze_types, .. } if *analyze_types => {
132 module_asset_context.with_types_resolving_enabled()
133 }
134 ModuleType::TypescriptDeclaration { .. } => {
135 module_asset_context.with_types_resolving_enabled()
136 }
137 _ => module_asset_context,
138 }
139 .to_resolved()
140 .await?;
141 let side_effect_free_packages = module_asset_context
142 .module_options_context()
143 .await?
144 .side_effect_free_packages;
145 let mut builder = EcmascriptModuleAsset::builder(
146 source,
147 ResolvedVc::upcast(context_for_module),
148 preprocess
149 .extend(**main)
150 .extend(**postprocess)
151 .to_resolved()
152 .await?,
153 *options,
154 module_asset_context
155 .compile_time_info()
156 .to_resolved()
157 .await?,
158 side_effect_free_packages,
159 );
160 match module_type {
161 ModuleType::Ecmascript { .. } => {
162 builder = builder.with_type(EcmascriptModuleAssetType::Ecmascript)
163 }
164 ModuleType::EcmascriptExtensionless { .. } => {
165 builder = builder.with_type(EcmascriptModuleAssetType::EcmascriptExtensionless)
166 }
167 ModuleType::Typescript {
168 tsx, analyze_types, ..
169 } => {
170 builder = builder.with_type(EcmascriptModuleAssetType::Typescript {
171 tsx: *tsx,
172 analyze_types: *analyze_types,
173 })
174 }
175 ModuleType::TypescriptDeclaration { .. } => {
176 builder = builder.with_type(EcmascriptModuleAssetType::TypescriptDeclaration)
177 }
178 _ => unreachable!(),
179 }
180
181 if let Some(inner_assets) = inner_assets {
182 builder = builder.with_inner_assets(inner_assets);
183 }
184
185 let module = builder.build().to_resolved().await?;
186 if matches!(reference_type, ReferenceType::Runtime) {
187 ResolvedVc::upcast(module)
188 } else {
189 if tree_shaking_mode.is_some() && is_evaluation {
194 if *module.side_effects().await? == ModuleSideEffects::SideEffectFree {
197 return Ok(ProcessResult::Ignore.cell());
198 }
199 }
200
201 match tree_shaking_mode {
202 Some(TreeShakingMode::ModuleFragments) => {
203 Vc::upcast(EcmascriptModulePartAsset::select_part(
204 *module,
205 part.cloned().unwrap_or(ModulePart::facade()),
206 ))
207 }
208 Some(TreeShakingMode::ReexportsOnly) => {
209 if *module.get_exports().split_locals_and_reexports().await? {
210 if let Some(part) = part {
211 match part {
212 ModulePart::Evaluation => {
213 Vc::upcast(EcmascriptModuleLocalsModule::new(*module))
214 }
215 ModulePart::Export(_) => {
216 apply_reexport_tree_shaking(
217 Vc::upcast(
218 EcmascriptModuleFacadeModule::new(Vc::upcast(
219 *module,
220 ))
221 .resolve()
222 .await?,
223 ),
224 part.clone(),
225 )
226 .await?
227 }
228 _ => bail!(
229 "Invalid module part \"{}\" for reexports only tree \
230 shaking mode",
231 part
232 ),
233 }
234 } else {
235 Vc::upcast(EcmascriptModuleFacadeModule::new(Vc::upcast(*module)))
236 }
237 } else {
238 Vc::upcast(*module)
239 }
240 }
241 None => Vc::upcast(*module),
242 }
243 .to_resolved()
244 .await?
245 }
246 }
247 ModuleType::Raw => ResolvedVc::upcast(RawModule::new(*source).to_resolved().await?),
248 ModuleType::NodeAddon => {
249 ResolvedVc::upcast(NodeAddonModule::new(*source).to_resolved().await?)
250 }
251 ModuleType::CssModule => ResolvedVc::upcast(
252 EcmascriptCssModule::new(*source, Vc::upcast(module_asset_context))
253 .to_resolved()
254 .await?,
255 ),
256
257 ModuleType::Css {
258 ty,
259 environment,
260 lightningcss_features,
261 } => ResolvedVc::upcast(
262 CssModule::new(
263 *source,
264 Vc::upcast(module_asset_context),
265 *ty,
266 css_import_context.map(|c| *c),
267 environment.as_deref().copied(),
268 *lightningcss_features,
269 )
270 .to_resolved()
271 .await?,
272 ),
273 ModuleType::StaticUrlJs { tag } => ResolvedVc::upcast(
274 StaticUrlJsModule::new(*source, tag.clone())
275 .to_resolved()
276 .await?,
277 ),
278 ModuleType::StaticUrlCss { tag } => ResolvedVc::upcast(
279 StaticUrlCssModule::new(*source, tag.clone())
280 .to_resolved()
281 .await?,
282 ),
283 ModuleType::WebAssembly { source_ty } => ResolvedVc::upcast(
284 WebAssemblyModuleAsset::new(
285 WebAssemblySource::new(*source, *source_ty),
286 Vc::upcast(module_asset_context),
287 )
288 .to_resolved()
289 .await?,
290 ),
291 ModuleType::Custom(custom) => {
292 custom
293 .create_module(*source, module_asset_context, reference_type)
294 .to_resolved()
295 .await?
296 }
297 };
298
299 if tree_shaking_mode.is_some() && is_evaluation {
300 if *module.side_effects().await? == ModuleSideEffects::SideEffectFree {
303 return Ok(ProcessResult::Ignore.cell());
304 }
305 }
306
307 Ok(ProcessResult::Module(module).cell())
308}
309
310async fn apply_reexport_tree_shaking(
311 module: Vc<Box<dyn EcmascriptChunkPlaceable>>,
312 part: ModulePart,
313) -> Result<Vc<Box<dyn Module>>> {
314 if let ModulePart::Export(export) = &part {
315 let FollowExportsResult {
316 module: final_module,
317 export_name: new_export,
318 ..
319 } = &*follow_reexports(module, export.clone(), true).await?;
320 let module = if let Some(new_export) = new_export {
321 if *new_export == *export {
322 Vc::upcast(**final_module)
323 } else {
324 Vc::upcast(EcmascriptModuleRenameModule::new(
325 **final_module,
326 ModulePart::renamed_export(new_export.clone(), export.clone()),
327 ))
328 }
329 } else {
330 Vc::upcast(EcmascriptModuleRenameModule::new(
331 **final_module,
332 ModulePart::renamed_namespace(export.clone()),
333 ))
334 };
335 return Ok(module);
336 }
337 Ok(Vc::upcast(module))
338}
339
340#[turbo_tasks::value]
341#[derive(Debug)]
342pub struct ModuleAssetContext {
343 pub transitions: ResolvedVc<TransitionOptions>,
344 pub compile_time_info: ResolvedVc<CompileTimeInfo>,
345 pub module_options_context: ResolvedVc<ModuleOptionsContext>,
346 pub resolve_options_context: ResolvedVc<ResolveOptionsContext>,
347 pub layer: Layer,
348 transition: Option<ResolvedVc<Box<dyn Transition>>>,
349 replace_externals: bool,
352}
353
354#[turbo_tasks::value_impl]
355impl ModuleAssetContext {
356 #[turbo_tasks::function]
357 pub fn new(
358 transitions: ResolvedVc<TransitionOptions>,
359 compile_time_info: ResolvedVc<CompileTimeInfo>,
360 module_options_context: ResolvedVc<ModuleOptionsContext>,
361 resolve_options_context: ResolvedVc<ResolveOptionsContext>,
362 layer: Layer,
363 ) -> Vc<Self> {
364 Self::cell(ModuleAssetContext {
365 transitions,
366 compile_time_info,
367 module_options_context,
368 resolve_options_context,
369 transition: None,
370 layer,
371 replace_externals: true,
372 })
373 }
374
375 #[turbo_tasks::function]
376 pub fn new_transition(
377 transitions: ResolvedVc<TransitionOptions>,
378 compile_time_info: ResolvedVc<CompileTimeInfo>,
379 module_options_context: ResolvedVc<ModuleOptionsContext>,
380 resolve_options_context: ResolvedVc<ResolveOptionsContext>,
381 layer: Layer,
382 transition: ResolvedVc<Box<dyn Transition>>,
383 ) -> Vc<Self> {
384 Self::cell(ModuleAssetContext {
385 transitions,
386 compile_time_info,
387 module_options_context,
388 resolve_options_context,
389 layer,
390 transition: Some(transition),
391 replace_externals: true,
392 })
393 }
394
395 #[turbo_tasks::function]
397 pub fn new_without_replace_externals(
398 transitions: ResolvedVc<TransitionOptions>,
399 compile_time_info: ResolvedVc<CompileTimeInfo>,
400 module_options_context: ResolvedVc<ModuleOptionsContext>,
401 resolve_options_context: ResolvedVc<ResolveOptionsContext>,
402 layer: Layer,
403 ) -> Vc<Self> {
404 Self::cell(ModuleAssetContext {
405 transitions,
406 compile_time_info,
407 module_options_context,
408 resolve_options_context,
409 transition: None,
410 layer,
411 replace_externals: false,
412 })
413 }
414
415 #[turbo_tasks::function]
416 pub fn module_options_context(&self) -> Vc<ModuleOptionsContext> {
417 *self.module_options_context
418 }
419
420 #[turbo_tasks::function]
421 pub fn resolve_options_context(&self) -> Vc<ResolveOptionsContext> {
422 *self.resolve_options_context
423 }
424
425 #[turbo_tasks::function]
426 pub async fn is_types_resolving_enabled(&self) -> Result<Vc<bool>> {
427 let resolve_options_context = self.resolve_options_context.await?;
428 Ok(Vc::cell(
429 resolve_options_context.enable_types && resolve_options_context.enable_typescript,
430 ))
431 }
432
433 #[turbo_tasks::function]
434 pub async fn with_types_resolving_enabled(self: Vc<Self>) -> Result<Vc<ModuleAssetContext>> {
435 if *self.is_types_resolving_enabled().await? {
436 return Ok(self);
437 }
438 let this = self.await?;
439 let resolve_options_context = this
440 .resolve_options_context
441 .with_types_enabled()
442 .resolve()
443 .await?;
444
445 Ok(ModuleAssetContext::new(
446 *this.transitions,
447 *this.compile_time_info,
448 *this.module_options_context,
449 resolve_options_context,
450 this.layer.clone(),
451 ))
452 }
453}
454
455impl ModuleAssetContext {
456 async fn process_with_transition_rules(
457 self: Vc<Self>,
458 source: ResolvedVc<Box<dyn Source>>,
459 reference_type: ReferenceType,
460 ) -> Result<Vc<ProcessResult>> {
461 let this = self.await?;
462 Ok(
463 if let Some(transition) = this
464 .transitions
465 .await?
466 .get_by_rules(source, &reference_type)
467 .await?
468 {
469 transition.process(*source, self, reference_type)
470 } else {
471 self.process_default(source, reference_type).await?
472 },
473 )
474 }
475
476 async fn process_default(
477 self: Vc<Self>,
478 source: ResolvedVc<Box<dyn Source>>,
479 reference_type: ReferenceType,
480 ) -> Result<Vc<ProcessResult>> {
481 process_default(self, source, reference_type, Vec::new()).await
482 }
483}
484
485async fn process_default(
486 module_asset_context: Vc<ModuleAssetContext>,
487 source: ResolvedVc<Box<dyn Source>>,
488 reference_type: ReferenceType,
489 processed_rules: Vec<usize>,
490) -> Result<Vc<ProcessResult>> {
491 let span = tracing::info_span!(
492 "process module",
493 name = %source.ident().to_string().await?,
494 layer = Empty,
495 reference_type = display(&reference_type)
496 );
497 if !span.is_disabled() {
498 span.record("layer", module_asset_context.await?.layer.name().as_str());
500 }
501
502 process_default_internal(
503 module_asset_context,
504 source,
505 reference_type,
506 processed_rules,
507 )
508 .instrument(span)
509 .await
510}
511
512async fn apply_module_rule_transforms(
517 module_type: &mut ModuleType,
518 collected_preprocess: &mut Vec<ResolvedVc<EcmascriptInputTransforms>>,
519 collected_main: &mut Vec<ResolvedVc<EcmascriptInputTransforms>>,
520 collected_postprocess: &mut Vec<ResolvedVc<EcmascriptInputTransforms>>,
521 ident: ResolvedVc<AssetIdent>,
522 current_source: ResolvedVc<Box<dyn Source>>,
523) -> Result<()> {
524 let has_transforms = !collected_preprocess.is_empty()
525 || !collected_main.is_empty()
526 || !collected_postprocess.is_empty();
527
528 if !has_transforms {
530 return Ok(());
531 }
532
533 match module_type {
534 ModuleType::Ecmascript {
535 preprocess,
536 main,
537 postprocess,
538 ..
539 }
540 | ModuleType::Typescript {
541 preprocess,
542 main,
543 postprocess,
544 ..
545 }
546 | ModuleType::TypescriptDeclaration {
547 preprocess,
548 main,
549 postprocess,
550 ..
551 }
552 | ModuleType::EcmascriptExtensionless {
553 preprocess,
554 main,
555 postprocess,
556 ..
557 } => {
558 let mut final_preprocess = EcmascriptInputTransforms::empty();
560 for vc in collected_preprocess.drain(..) {
561 final_preprocess = final_preprocess.extend(*vc);
562 }
563 final_preprocess = final_preprocess.extend(**preprocess);
564 *preprocess = final_preprocess.to_resolved().await?;
565
566 let mut final_main = EcmascriptInputTransforms::empty();
567 for vc in collected_main.drain(..) {
568 final_main = final_main.extend(*vc);
569 }
570 final_main = final_main.extend(**main);
571 *main = final_main.to_resolved().await?;
572
573 let mut final_postprocess = **postprocess;
575 for vc in collected_postprocess.drain(..) {
576 final_postprocess = final_postprocess.extend(*vc);
577 }
578 *postprocess = final_postprocess.to_resolved().await?;
579 }
580 ModuleType::Custom(custom_module_type) => {
581 if has_transforms {
582 let mut combined_preprocess = EcmascriptInputTransforms::empty();
584 for vc in collected_preprocess.drain(..) {
585 combined_preprocess = combined_preprocess.extend(*vc);
586 }
587 let mut combined_main = EcmascriptInputTransforms::empty();
588 for vc in collected_main.drain(..) {
589 combined_main = combined_main.extend(*vc);
590 }
591 let mut combined_postprocess = EcmascriptInputTransforms::empty();
592 for vc in collected_postprocess.drain(..) {
593 combined_postprocess = combined_postprocess.extend(*vc);
594 }
595
596 match custom_module_type
597 .extend_ecmascript_transforms(
598 combined_preprocess,
599 combined_main,
600 combined_postprocess,
601 )
602 .to_resolved()
603 .await
604 {
605 Ok(new_custom_module_type) => {
606 *custom_module_type = new_custom_module_type;
607 }
608 Err(_) => {
609 ModuleIssue::new(
610 *ident,
611 rcstr!("Invalid module type"),
612 rcstr!(
613 "The custom module type didn't accept the additional Ecmascript \
614 transforms"
615 ),
616 Some(IssueSource::from_source_only(current_source)),
617 )
618 .to_resolved()
619 .await?
620 .emit();
621 }
622 }
623 }
624 }
625 other => {
626 if has_transforms {
627 ModuleIssue::new(
628 *ident,
629 rcstr!("Invalid module type"),
630 format!(
631 "The module type must be Ecmascript or Typescript to add Ecmascript \
632 transforms (got {})",
633 other
634 )
635 .into(),
636 Some(IssueSource::from_source_only(current_source)),
637 )
638 .to_resolved()
639 .await?
640 .emit();
641 collected_preprocess.clear();
642 collected_main.clear();
643 collected_postprocess.clear();
644 }
645 }
646 }
647 Ok(())
648}
649
650async fn process_default_internal(
651 module_asset_context: Vc<ModuleAssetContext>,
652 source: ResolvedVc<Box<dyn Source>>,
653 reference_type: ReferenceType,
654 processed_rules: Vec<usize>,
655) -> Result<Vc<ProcessResult>> {
656 let ident = source.ident().to_resolved().await?;
657 let path_ref = ident.path().await?;
658 let options = ModuleOptions::new(
659 path_ref.parent(),
660 module_asset_context.module_options_context(),
661 module_asset_context.resolve_options_context(),
662 );
663
664 let inner_assets = match &reference_type {
665 ReferenceType::Internal(inner_assets) => Some(*inner_assets),
666 _ => None,
667 };
668 let mut current_source = source;
669 let mut current_module_type = None;
670
671 if let ReferenceType::EcmaScriptModules(
673 EcmaScriptModulesReferenceSubType::ImportWithTurbopackUse {
674 ref loader,
675 ref rename_as,
676 ref module_type,
677 },
678 ) = reference_type
679 {
680 let module_options_context = module_asset_context.module_options_context().await?;
681 let webpack_loaders_options = module_options_context
682 .enable_webpack_loaders
683 .as_ref()
684 .context(
685 "turbopackUse import assertions require webpack loaders to be enabled \
686 (enable_webpack_loaders)",
687 )?
688 .await?;
689 let execution_context = module_options_context
690 .execution_context
691 .context("execution_context is required for turbopackUse import assertions")?;
692 let execution_context_value = execution_context.await?;
693
694 let resolve_options_context = module_asset_context
695 .resolve_options_context()
696 .to_resolved()
697 .await?;
698 let source_maps = matches!(
699 module_options_context.ecmascript.source_maps,
700 SourceMapsType::Full
701 );
702
703 let loader_runner_package = webpack_loaders_options.loader_runner_package;
705
706 let import_map = if let Some(loader_runner_package) = loader_runner_package {
707 package_import_map_from_import_mapping(rcstr!("loader-runner"), *loader_runner_package)
708 } else {
709 package_import_map_from_context(
710 rcstr!("loader-runner"),
711 execution_context_value.project_path.clone(),
712 )
713 };
714
715 let evaluate_context = node_evaluate_asset_context(
716 *execution_context,
717 Some(import_map),
718 None,
719 Layer::new(rcstr!("webpack_loaders")),
720 false,
721 )
722 .to_resolved()
723 .await?;
724
725 let loader_relative_path = execution_context_value
726 .project_path
727 .get_relative_path_to(&loader.loader)
728 .context("Loader path must be on project filesystem")?;
729 let webpack_loader_item = WebpackLoaderItem {
730 loader: loader_relative_path,
731 options: loader.options.clone(),
732 };
733 let loaders_vc = WebpackLoaderItems(vec![webpack_loader_item]).cell();
734 let webpack_loaders = WebpackLoaders::new(
735 *evaluate_context,
736 *execution_context,
737 loaders_vc,
738 rename_as.clone(),
739 *resolve_options_context,
740 source_maps,
741 )
742 .to_resolved()
743 .await?;
744
745 let transforms = Vc::<SourceTransforms>::cell(vec![ResolvedVc::upcast(webpack_loaders)]);
746 current_source = transforms
747 .transform(*current_source, Vc::upcast(module_asset_context))
748 .to_resolved()
749 .await?;
750
751 if let Some(type_str) = module_type {
755 let empty_transforms = EcmascriptInputTransforms::empty().to_resolved().await?;
756 let default_options = EcmascriptOptions::default().resolved_cell();
757 let effect = ConfiguredModuleType::parse(type_str)?
758 .into_effect(
759 empty_transforms,
760 empty_transforms,
761 empty_transforms,
762 default_options,
763 None,
764 Default::default(),
765 )
766 .await?;
767 match effect {
768 ModuleRuleEffect::ModuleType(module_type) => {
769 return apply_module_type(
770 current_source,
771 module_asset_context,
772 module_type.cell(),
773 reference_type,
774 inner_assets,
775 )
776 .await;
777 }
778 ModuleRuleEffect::SourceTransforms(transforms) => {
779 current_source = transforms
780 .transform(*current_source, Vc::upcast(module_asset_context))
781 .to_resolved()
782 .await?;
783 }
785 _ => bail!("Unexpected module rule effect for turbopackModuleType"),
786 }
787 }
788
789 if current_source.ident().to_resolved().await? != ident {
794 let plain_reference_type =
795 ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::Import);
796 if let Some(transition) = module_asset_context
797 .await?
798 .transitions
799 .await?
800 .get_by_rules(current_source, &plain_reference_type)
801 .await?
802 {
803 return Ok(transition.process(
804 *current_source,
805 module_asset_context,
806 plain_reference_type,
807 ));
808 } else {
809 return Box::pin(process_default(
810 module_asset_context,
811 current_source,
812 plain_reference_type,
813 processed_rules,
814 ))
815 .await;
816 }
817 }
818 }
819
820 let mut collected_preprocess: Vec<ResolvedVc<EcmascriptInputTransforms>> = Vec::new();
823 let mut collected_main: Vec<ResolvedVc<EcmascriptInputTransforms>> = Vec::new();
824 let mut collected_postprocess: Vec<ResolvedVc<EcmascriptInputTransforms>> = Vec::new();
825
826 let options_value = options.await?;
827 'outer: for (i, rule) in options_value.rules.iter().enumerate() {
828 if processed_rules.contains(&i) {
829 continue;
830 }
831 if rule.matches(source, &path_ref, &reference_type).await? {
832 for effect in rule.effects() {
833 match effect {
834 ModuleRuleEffect::Ignore => {
835 return Ok(ProcessResult::Ignore.cell());
836 }
837 ModuleRuleEffect::SourceTransforms(transforms) => {
838 current_source = transforms
839 .transform(*current_source, Vc::upcast(module_asset_context))
840 .to_resolved()
841 .await?;
842 if current_source.ident().to_resolved().await? != ident {
843 if let Some(transition) = module_asset_context
845 .await?
846 .transitions
847 .await?
848 .get_by_rules(current_source, &reference_type)
849 .await?
850 {
851 return Ok(transition.process(
852 *current_source,
853 module_asset_context,
854 reference_type,
855 ));
856 } else {
857 let mut processed_rules = processed_rules.clone();
858 processed_rules.push(i);
859 return Box::pin(process_default(
860 module_asset_context,
861 current_source,
862 reference_type,
863 processed_rules,
864 ))
865 .await;
866 }
867 }
868 }
869 ModuleRuleEffect::ModuleType(module) => {
870 let mut module = module.clone();
874 apply_module_rule_transforms(
875 &mut module,
876 &mut collected_preprocess,
877 &mut collected_main,
878 &mut collected_postprocess,
879 ident,
880 current_source,
881 )
882 .await?;
883 current_module_type = Some(module);
884 break 'outer;
885 }
886 ModuleRuleEffect::ExtendEcmascriptTransforms {
887 preprocess: extend_preprocess,
888 main: extend_main,
889 postprocess: extend_postprocess,
890 } => {
891 collected_preprocess.push(*extend_preprocess);
893 collected_main.push(*extend_main);
894 collected_postprocess.push(*extend_postprocess);
895 }
896 }
897 }
898 }
899 }
900
901 let Some(module_type) = current_module_type else {
902 return Ok(ProcessResult::Unknown(current_source).cell());
903 };
904
905 let module = apply_module_type(
906 current_source,
907 module_asset_context,
908 module_type.cell(),
909 reference_type,
910 inner_assets,
911 )
912 .await?;
913
914 Ok(module)
915}
916
917#[turbo_tasks::function]
918pub async fn externals_tracing_module_context(
919 compile_time_info: Vc<CompileTimeInfo>,
920 resolve_typescript: bool,
921) -> Result<Vc<ModuleAssetContext>> {
922 let mut extensions = vec![rcstr!(".js"), rcstr!(".node"), rcstr!(".json")];
923 if resolve_typescript {
924 extensions.insert(0, rcstr!(".ts"));
925 }
926
927 let resolve_options = ResolveOptionsContext {
928 custom_extensions: Some(extensions),
929 emulate_environment: Some(compile_time_info.await?.environment),
930 loose_errors: true,
931 collect_affecting_sources: true,
932 custom_conditions: vec![rcstr!("node")],
933 ..Default::default()
934 };
935
936 Ok(ModuleAssetContext::new_without_replace_externals(
937 Default::default(),
938 compile_time_info,
939 ModuleOptionsContext {
945 ecmascript: EcmascriptOptionsContext {
946 enable_typescript_transform: Some(
947 TypescriptTransformOptions::default().resolved_cell(),
948 ),
949 source_maps: SourceMapsType::None,
952 ..Default::default()
953 },
954 css: CssOptionsContext {
955 source_maps: SourceMapsType::None,
956 enable_raw_css: true,
957 ..Default::default()
958 },
959 environment: None,
962 analyze_mode: AnalyzeMode::Tracing,
963 tree_shaking_mode: None,
966 ..Default::default()
967 }
968 .cell(),
969 resolve_options.cell(),
970 Layer::new(rcstr!("externals-tracing")),
971 ))
972}
973
974#[turbo_tasks::value_impl]
975impl AssetContext for ModuleAssetContext {
976 #[turbo_tasks::function]
977 fn compile_time_info(&self) -> Vc<CompileTimeInfo> {
978 *self.compile_time_info
979 }
980
981 fn layer(&self) -> Layer {
982 self.layer.clone()
983 }
984
985 #[turbo_tasks::function]
986 async fn resolve_options(
987 self: Vc<Self>,
988 origin_path: FileSystemPath,
989 ) -> Result<Vc<ResolveOptions>> {
990 let this = self.await?;
991 let module_asset_context = if let Some(transition) = this.transition {
992 transition.process_context(self)
993 } else {
994 self
995 };
996 Ok(resolve_options(
998 origin_path.parent(),
999 *module_asset_context.await?.resolve_options_context,
1000 ))
1001 }
1002
1003 #[turbo_tasks::function]
1004 async fn resolve_asset(
1005 self: Vc<Self>,
1006 origin_path: FileSystemPath,
1007 request: Vc<Request>,
1008 resolve_options: Vc<ResolveOptions>,
1009 reference_type: ReferenceType,
1010 ) -> Result<Vc<ModuleResolveResult>> {
1011 let context_path = origin_path.parent();
1012
1013 let result = resolve(
1014 context_path,
1015 reference_type.clone(),
1016 request,
1017 resolve_options,
1018 );
1019
1020 let mut result = self.process_resolve_result(result.resolve().await?, reference_type);
1021
1022 if *self.is_types_resolving_enabled().await? {
1023 let types_result = type_resolve(
1024 Vc::upcast(PlainResolveOrigin::new(Vc::upcast(self), origin_path)),
1025 request,
1026 );
1027
1028 result = ModuleResolveResult::alternatives(vec![result, types_result]);
1029 }
1030
1031 Ok(result)
1032 }
1033
1034 #[turbo_tasks::function]
1035 async fn process_resolve_result(
1036 self: Vc<Self>,
1037 result: Vc<ResolveResult>,
1038 reference_type: ReferenceType,
1039 ) -> Result<Vc<ModuleResolveResult>> {
1040 let this = self.await?;
1041
1042 let replace_externals = this.replace_externals;
1043 let import_externals = this
1044 .module_options_context
1045 .await?
1046 .ecmascript
1047 .import_externals;
1048
1049 let result = result.await?;
1050
1051 let result = result
1052 .map_primary_items(|item| {
1053 let reference_type = reference_type.clone();
1054 async move {
1055 Ok(match item {
1056 ResolveResultItem::Source(source) => {
1057 match &*self.process(*source, reference_type).await? {
1058 ProcessResult::Module(module) => {
1059 ModuleResolveResultItem::Module(*module)
1060 }
1061 ProcessResult::Unknown(source) => {
1062 ModuleResolveResultItem::Unknown(*source)
1063 }
1064 ProcessResult::Ignore => ModuleResolveResultItem::Ignore,
1065 }
1066 }
1067 ResolveResultItem::External {
1068 name,
1069 ty,
1070 traced,
1071 target,
1072 } => {
1073 let replacement = if replace_externals {
1074 let target = if let Some(mut target) = target {
1077 loop {
1078 let parent = target.parent();
1079 if parent.is_root() {
1080 break;
1081 }
1082 if parent.file_name() == "node_modules" {
1083 break;
1084 }
1085 if parent.file_name().starts_with("@")
1086 && parent.parent().file_name() == "node_modules"
1087 {
1088 break;
1089 }
1090 target = parent;
1091 }
1092 Some(target)
1093 } else {
1094 None
1095 };
1096
1097 let analyze_mode = if traced == ExternalTraced::Traced
1098 && let Some(options) = &self
1099 .module_options_context()
1100 .await?
1101 .enable_externals_tracing
1102 {
1103 let options = options.await?;
1108 let origin = PlainResolveOrigin::new(
1109 Vc::upcast(externals_tracing_module_context(
1110 *options.compile_time_info,
1111 false,
1112 )),
1113 target
1118 .as_ref()
1119 .unwrap_or(&options.tracing_root)
1120 .join("_")?,
1121 );
1122 CachedExternalTracingMode::Traced {
1123 origin: ResolvedVc::upcast(origin.to_resolved().await?),
1124 }
1125 } else {
1126 CachedExternalTracingMode::Untraced
1127 };
1128
1129 replace_external(&name, ty, target, import_externals, analyze_mode)
1130 .await?
1131 } else {
1132 None
1133 };
1134
1135 replacement
1136 .unwrap_or_else(|| ModuleResolveResultItem::External { name, ty })
1137 }
1138 ResolveResultItem::Ignore => ModuleResolveResultItem::Ignore,
1139 ResolveResultItem::Empty => ModuleResolveResultItem::Empty,
1140 ResolveResultItem::Error(e) => ModuleResolveResultItem::Error(e),
1141 ResolveResultItem::Custom(u8) => ModuleResolveResultItem::Custom(u8),
1142 })
1143 }
1144 })
1145 .await?;
1146
1147 Ok(result.cell())
1148 }
1149
1150 #[turbo_tasks::function]
1151 async fn process(
1152 self: Vc<Self>,
1153 asset: ResolvedVc<Box<dyn Source>>,
1154 reference_type: ReferenceType,
1155 ) -> Result<Vc<ProcessResult>> {
1156 let this = self.await?;
1157 if let Some(transition) = this.transition {
1158 Ok(transition.process(*asset, self, reference_type))
1159 } else {
1160 Ok(self
1161 .process_with_transition_rules(asset, reference_type)
1162 .await?)
1163 }
1164 }
1165
1166 #[turbo_tasks::function]
1167 async fn with_transition(&self, transition: RcStr) -> Result<Vc<Box<dyn AssetContext>>> {
1168 Ok(
1169 if let Some(transition) = self.transitions.await?.get_named(transition) {
1170 Vc::upcast(ModuleAssetContext::new_transition(
1171 *self.transitions,
1172 *self.compile_time_info,
1173 *self.module_options_context,
1174 *self.resolve_options_context,
1175 self.layer.clone(),
1176 *transition,
1177 ))
1178 } else {
1179 Vc::upcast(ModuleAssetContext::new(
1181 *self.transitions,
1182 *self.compile_time_info,
1183 *self.module_options_context,
1184 *self.resolve_options_context,
1185 self.layer.clone(),
1186 ))
1187 },
1188 )
1189 }
1190}
1191
1192#[turbo_tasks::function]
1193pub async fn emit_asset(asset: Vc<Box<dyn OutputAsset>>) -> Result<()> {
1194 asset
1195 .content()
1196 .write(asset.path().owned().await?)
1197 .as_side_effect()
1198 .await?;
1199
1200 Ok(())
1201}
1202
1203#[turbo_tasks::function]
1204pub async fn emit_assets_into_dir(
1205 assets: Vc<ExpandedOutputAssets>,
1206 output_dir: FileSystemPath,
1207) -> Result<()> {
1208 let assets = assets.await?;
1209 let paths = assets.iter().map(|&asset| asset.path()).try_join().await?;
1210 for (&asset, path) in assets.iter().zip(paths.iter()) {
1211 if path.is_inside_ref(&output_dir) {
1212 emit_asset(*asset).as_side_effect().await?;
1213 }
1214 }
1215 Ok(())
1216}
1217
1218#[turbo_tasks::function(operation)]
1219pub async fn emit_assets_into_dir_operation(
1220 assets: ResolvedVc<ExpandedOutputAssets>,
1221 output_dir: FileSystemPath,
1222) -> Result<Vc<()>> {
1223 emit_assets_into_dir(*assets, output_dir)
1224 .as_side_effect()
1225 .await?;
1226 Ok(Vc::cell(()))
1227}
1228
1229pub async fn replace_external(
1231 name: &RcStr,
1232 ty: ExternalType,
1233 target: Option<FileSystemPath>,
1234 import_externals: bool,
1235 analyze_mode: CachedExternalTracingMode,
1236) -> Result<Option<ModuleResolveResultItem>> {
1237 let external_type = match ty {
1238 ExternalType::CommonJs => CachedExternalType::CommonJs,
1239 ExternalType::EcmaScriptModule => {
1240 if import_externals {
1241 CachedExternalType::EcmaScriptViaImport
1242 } else {
1243 CachedExternalType::EcmaScriptViaRequire
1244 }
1245 }
1246 ExternalType::Global => CachedExternalType::Global,
1247 ExternalType::Script => CachedExternalType::Script,
1248 ExternalType::Url => {
1249 return Ok(None);
1251 }
1252 };
1253
1254 let module = CachedExternalModule::new(name.clone(), target, external_type, analyze_mode)
1255 .to_resolved()
1256 .await?;
1257
1258 Ok(Some(ModuleResolveResultItem::Module(ResolvedVc::upcast(
1259 module,
1260 ))))
1261}