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