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