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, ModuleRule, ModuleRuleEffect,
16 ModuleType, RuleCondition,
17};
18pub use runtime_asset_context::get_runtime_asset_context;
19use tracing::{Instrument, field::Empty};
20use turbo_rcstr::{RcStr, rcstr};
21use turbo_tasks::{ResolvedVc, TryJoinIterExt, ValueToString, Vc};
22use turbo_tasks_fs::{FileSystemPath, glob::Glob};
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,
41 options::{ConditionValue, ResolveOptions},
42 origin::PlainResolveOrigin,
43 parse::Request,
44 resolve,
45 },
46 source::Source,
47 source_transform::SourceTransforms,
48};
49use turbopack_css::{CssModule, EcmascriptCssModule};
50use turbopack_ecmascript::{
51 AnalyzeMode, EcmascriptInputTransforms, EcmascriptModuleAsset, EcmascriptModuleAssetType,
52 EcmascriptOptions,
53 chunk::EcmascriptChunkPlaceable,
54 module_fragments::part::module::EcmascriptModulePartAsset,
55 references::{
56 FollowExportsResult,
57 external_module::{CachedExternalModule, CachedExternalTracingMode, CachedExternalType},
58 follow_reexports,
59 },
60 rename::module::EcmascriptModuleRenameModule,
61 side_effect_optimization::{
62 facade::module::EcmascriptModuleFacadeModule, locals::module::EcmascriptModuleLocalsModule,
63 },
64};
65use turbopack_node::transforms::webpack::{WebpackLoaderItem, WebpackLoaderItems, WebpackLoaders};
66use turbopack_resolve::{
67 resolve::resolve_options, resolve_options_context::ResolveOptionsContext,
68 typescript::type_resolve,
69};
70use turbopack_static::{css::StaticUrlCssModule, ecma::StaticUrlJsModule};
71use turbopack_wasm::{module_asset::WebAssemblyModuleAsset, source::WebAssemblySource};
72
73use crate::{
74 evaluate_context::node_evaluate_asset_context,
75 module_options::{
76 CssOptionsContext, CustomModuleType, EcmascriptOptionsContext, TypescriptTransformOptions,
77 package_import_map_from_context, package_import_map_from_import_mapping,
78 },
79 transition::{Transition, TransitionOptions},
80};
81
82async fn apply_module_type(
83 source: ResolvedVc<Box<dyn Source>>,
84 module_asset_context: Vc<ModuleAssetContext>,
85 module_type: Vc<ModuleType>,
86 reference_type: ReferenceType,
87 inner_assets: Option<ResolvedVc<InnerAssets>>,
88) -> Result<Vc<ProcessResult>> {
89 let module_options = module_asset_context.module_options_context().await?;
90 let follow_reexports = module_options.follow_reexports;
91 let module_fragments_enabled = module_options.module_fragments_enabled;
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 (follow_reexports || module_fragments_enabled) && is_evaluation {
196 if *module.side_effects().await? == ModuleSideEffects::SideEffectFree {
199 return Ok(ProcessResult::Ignore.cell());
200 }
201 }
202
203 if module_fragments_enabled {
204 Vc::upcast(EcmascriptModulePartAsset::select_part(
205 *module,
206 part.cloned().unwrap_or(ModulePart::facade()),
207 ))
208 } else if follow_reexports {
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(*module))
219 .to_resolved()
220 .await?,
221 ),
222 part.clone(),
223 )
224 .await?
225 }
226 _ => bail!(
227 "Invalid module part \"{}\" for reexports only tree shaking \
228 mode",
229 part
230 ),
231 }
232 } else {
233 Vc::upcast(EcmascriptModuleFacadeModule::new(Vc::upcast(*module)))
234 }
235 } else {
236 Vc::upcast(*module)
237 }
238 } else {
239 Vc::upcast(*module)
240 }
241 .to_resolved()
242 .await?
243 }
244 }
245 ModuleType::Raw => ResolvedVc::upcast(RawModule::new(*source).to_resolved().await?),
246 ModuleType::NodeAddon => {
247 ResolvedVc::upcast(NodeAddonModule::new(*source).to_resolved().await?)
248 }
249 ModuleType::CssModule => ResolvedVc::upcast(
250 EcmascriptCssModule::new(*source, Vc::upcast(module_asset_context))
251 .to_resolved()
252 .await?,
253 ),
254
255 ModuleType::Css {
256 ty,
257 environment,
258 lightningcss_features,
259 module_css_debuggable_idents,
260 } => ResolvedVc::upcast(
261 CssModule::new(
262 *source,
263 Vc::upcast(module_asset_context),
264 *ty,
265 css_import_context.map(|c| *c),
266 environment.as_deref().copied(),
267 *lightningcss_features,
268 *module_css_debuggable_idents,
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 (follow_reexports || module_fragments_enabled) && 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 with_types_resolving_enabled(self: Vc<Self>) -> Result<Vc<ModuleAssetContext>> {
427 let this = self.await?;
428 if this.is_types_resolving_enabled().await? {
429 return Ok(self);
430 }
431 let resolve_options_context = *this
432 .resolve_options_context
433 .with_types_enabled()
434 .to_resolved()
435 .await?;
436
437 Ok(ModuleAssetContext::new(
438 *this.transitions,
439 *this.compile_time_info,
440 *this.module_options_context,
441 resolve_options_context,
442 this.layer.clone(),
443 ))
444 }
445}
446
447impl ModuleAssetContext {
448 async fn is_types_resolving_enabled(&self) -> Result<bool> {
449 let resolve_options_context = self.resolve_options_context.await?;
450 Ok(resolve_options_context.enable_types && resolve_options_context.enable_typescript)
451 }
452 async fn process_with_transition_rules(
453 self: Vc<Self>,
454 source: ResolvedVc<Box<dyn Source>>,
455 reference_type: ReferenceType,
456 ) -> Result<Vc<ProcessResult>> {
457 let this = self.await?;
458 Ok(
459 if let Some(transition) = this
460 .transitions
461 .await?
462 .get_by_rules(source, &reference_type)
463 .await?
464 {
465 transition.process(*source, self, reference_type)
466 } else {
467 self.process_default(source, reference_type).await?
468 },
469 )
470 }
471
472 async fn process_default(
473 self: Vc<Self>,
474 source: ResolvedVc<Box<dyn Source>>,
475 reference_type: ReferenceType,
476 ) -> Result<Vc<ProcessResult>> {
477 process_default(self, source, reference_type, Vec::new()).await
478 }
479}
480
481async fn process_default(
482 module_asset_context: Vc<ModuleAssetContext>,
483 source: ResolvedVc<Box<dyn Source>>,
484 reference_type: ReferenceType,
485 processed_rules: Vec<usize>,
486) -> Result<Vc<ProcessResult>> {
487 let span = tracing::info_span!(
488 "process module",
489 name = %source.ident().to_string().await?,
490 layer = Empty,
491 reference_type = display(&reference_type)
492 );
493 if !span.is_disabled() {
494 span.record("layer", module_asset_context.await?.layer.name().as_str());
496 }
497
498 process_default_internal(
499 module_asset_context,
500 source,
501 reference_type,
502 processed_rules,
503 )
504 .instrument(span)
505 .await
506}
507
508async fn apply_module_rule_transforms(
513 module_type: &mut ModuleType,
514 collected_preprocess: &mut Vec<ResolvedVc<EcmascriptInputTransforms>>,
515 collected_main: &mut Vec<ResolvedVc<EcmascriptInputTransforms>>,
516 collected_postprocess: &mut Vec<ResolvedVc<EcmascriptInputTransforms>>,
517 ident: ResolvedVc<AssetIdent>,
518 current_source: ResolvedVc<Box<dyn Source>>,
519) -> Result<()> {
520 let has_transforms = !collected_preprocess.is_empty()
521 || !collected_main.is_empty()
522 || !collected_postprocess.is_empty();
523
524 if !has_transforms {
526 return Ok(());
527 }
528
529 match module_type {
530 ModuleType::Ecmascript {
531 preprocess,
532 main,
533 postprocess,
534 ..
535 }
536 | ModuleType::Typescript {
537 preprocess,
538 main,
539 postprocess,
540 ..
541 }
542 | ModuleType::TypescriptDeclaration {
543 preprocess,
544 main,
545 postprocess,
546 ..
547 }
548 | ModuleType::EcmascriptExtensionless {
549 preprocess,
550 main,
551 postprocess,
552 ..
553 } => {
554 let mut final_preprocess = EcmascriptInputTransforms::empty();
556 for vc in collected_preprocess.drain(..) {
557 final_preprocess = final_preprocess.extend(*vc);
558 }
559 final_preprocess = final_preprocess.extend(**preprocess);
560 *preprocess = final_preprocess.to_resolved().await?;
561
562 let mut final_main = EcmascriptInputTransforms::empty();
563 for vc in collected_main.drain(..) {
564 final_main = final_main.extend(*vc);
565 }
566 final_main = final_main.extend(**main);
567 *main = final_main.to_resolved().await?;
568
569 let mut final_postprocess = **postprocess;
571 for vc in collected_postprocess.drain(..) {
572 final_postprocess = final_postprocess.extend(*vc);
573 }
574 *postprocess = final_postprocess.to_resolved().await?;
575 }
576 ModuleType::Custom(custom_module_type) => {
577 if has_transforms {
578 let mut combined_preprocess = EcmascriptInputTransforms::empty();
580 for vc in collected_preprocess.drain(..) {
581 combined_preprocess = combined_preprocess.extend(*vc);
582 }
583 let mut combined_main = EcmascriptInputTransforms::empty();
584 for vc in collected_main.drain(..) {
585 combined_main = combined_main.extend(*vc);
586 }
587 let mut combined_postprocess = EcmascriptInputTransforms::empty();
588 for vc in collected_postprocess.drain(..) {
589 combined_postprocess = combined_postprocess.extend(*vc);
590 }
591
592 match custom_module_type
593 .extend_ecmascript_transforms(
594 combined_preprocess,
595 combined_main,
596 combined_postprocess,
597 )
598 .to_resolved()
599 .await
600 {
601 Ok(new_custom_module_type) => {
602 *custom_module_type = new_custom_module_type;
603 }
604 Err(_) => {
605 ModuleIssue::new(
606 *ident,
607 rcstr!("Invalid module type"),
608 rcstr!(
609 "The custom module type didn't accept the additional Ecmascript \
610 transforms"
611 ),
612 Some(IssueSource::from_source_only(current_source)),
613 )
614 .to_resolved()
615 .await?
616 .emit();
617 }
618 }
619 }
620 }
621 other => {
622 if has_transforms {
623 ModuleIssue::new(
624 *ident,
625 rcstr!("Invalid module type"),
626 format!(
627 "The module type must be Ecmascript or Typescript to add Ecmascript \
628 transforms (got {})",
629 other
630 )
631 .into(),
632 Some(IssueSource::from_source_only(current_source)),
633 )
634 .to_resolved()
635 .await?
636 .emit();
637 collected_preprocess.clear();
638 collected_main.clear();
639 collected_postprocess.clear();
640 }
641 }
642 }
643 Ok(())
644}
645
646async fn process_default_internal(
647 module_asset_context: Vc<ModuleAssetContext>,
648 source: ResolvedVc<Box<dyn Source>>,
649 reference_type: ReferenceType,
650 processed_rules: Vec<usize>,
651) -> Result<Vc<ProcessResult>> {
652 let ident = source.ident().to_resolved().await?;
653 let ident_ref = ident.await?;
654 let path_ref = &ident_ref.path;
655 let options = ModuleOptions::new(
656 path_ref.parent(),
657 module_asset_context.module_options_context(),
658 module_asset_context.resolve_options_context(),
659 );
660
661 let inner_assets = match &reference_type {
662 ReferenceType::Internal(inner_assets) => Some(*inner_assets),
663 _ => None,
664 };
665 let mut current_source = source;
666 let mut current_module_type = None;
667
668 if let ReferenceType::EcmaScriptModules(
670 EcmaScriptModulesReferenceSubType::ImportWithTurbopackUse {
671 ref loader,
672 ref rename_as,
673 ref module_type,
674 },
675 ) = reference_type
676 {
677 let module_options_context = module_asset_context.module_options_context().await?;
678 let webpack_loaders_options = module_options_context
679 .enable_webpack_loaders
680 .as_ref()
681 .context(
682 "turbopackUse import assertions require webpack loaders to be enabled \
683 (enable_webpack_loaders)",
684 )?
685 .await?;
686 let execution_context = module_options_context
687 .execution_context
688 .context("execution_context is required for turbopackUse import assertions")?;
689 let execution_context_value = execution_context.await?;
690
691 let resolve_options_context = module_asset_context
692 .resolve_options_context()
693 .to_resolved()
694 .await?;
695 let source_maps = matches!(
696 module_options_context.ecmascript.source_maps,
697 SourceMapsType::Full
698 );
699
700 let loader_runner_package = webpack_loaders_options.loader_runner_package;
702
703 let import_map = if let Some(loader_runner_package) = loader_runner_package {
704 package_import_map_from_import_mapping(rcstr!("loader-runner"), *loader_runner_package)
705 } else {
706 package_import_map_from_context(
707 rcstr!("loader-runner"),
708 execution_context_value.project_path.clone(),
709 )
710 };
711
712 let evaluate_context = node_evaluate_asset_context(
713 *execution_context,
714 Some(import_map),
715 None,
716 Layer::new(rcstr!("webpack_loaders")),
717 false,
718 )
719 .to_resolved()
720 .await?;
721
722 let loader_relative_path = execution_context_value
723 .project_path
724 .get_relative_path_to(&loader.loader)
725 .context("Loader path must be on project filesystem")?;
726 let loader_request = if loader_relative_path.starts_with("../") {
727 loader_relative_path
728 } else {
729 RcStr::from(format!("./{loader_relative_path}"))
730 };
731 let webpack_loader_item = WebpackLoaderItem {
732 loader: loader_request,
733 options: loader.options.clone(),
734 };
735 let loaders_vc = WebpackLoaderItems(vec![webpack_loader_item]).cell();
736 let webpack_loaders = WebpackLoaders::new(
737 *evaluate_context,
738 *execution_context,
739 loaders_vc,
740 *webpack_loaders_options.target,
741 rename_as.clone(),
742 *resolve_options_context,
743 source_maps,
744 )
745 .to_resolved()
746 .await?;
747
748 let transforms = Vc::<SourceTransforms>::cell(vec![ResolvedVc::upcast(webpack_loaders)]);
749 current_source = transforms
750 .transform(*current_source, Vc::upcast(module_asset_context))
751 .to_resolved()
752 .await?;
753
754 if let Some(type_str) = module_type {
758 let empty_transforms = EcmascriptInputTransforms::empty().to_resolved().await?;
759 let default_options = EcmascriptOptions::default().resolved_cell();
760 let effect = ConfiguredModuleType::parse(type_str)?
761 .into_effect(
762 empty_transforms,
763 empty_transforms,
764 empty_transforms,
765 default_options,
766 None,
767 Default::default(),
768 )
769 .await?;
770 match effect {
771 ModuleRuleEffect::ModuleType(module_type) => {
772 return apply_module_type(
773 current_source,
774 module_asset_context,
775 module_type.cell(),
776 reference_type,
777 inner_assets,
778 )
779 .await;
780 }
781 ModuleRuleEffect::SourceTransforms(transforms) => {
782 current_source = transforms
783 .transform(*current_source, Vc::upcast(module_asset_context))
784 .to_resolved()
785 .await?;
786 }
788 _ => bail!("Unexpected module rule effect for turbopackModuleType"),
789 }
790 }
791
792 if current_source.ident().to_resolved().await? != ident {
797 let plain_reference_type =
798 ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::Import);
799 if let Some(transition) = module_asset_context
800 .await?
801 .transitions
802 .await?
803 .get_by_rules(current_source, &plain_reference_type)
804 .await?
805 {
806 return Ok(transition.process(
807 *current_source,
808 module_asset_context,
809 plain_reference_type,
810 ));
811 } else {
812 return Box::pin(process_default(
813 module_asset_context,
814 current_source,
815 plain_reference_type,
816 processed_rules,
817 ))
818 .await;
819 }
820 }
821 }
822
823 let mut collected_preprocess: Vec<ResolvedVc<EcmascriptInputTransforms>> = Vec::new();
826 let mut collected_main: Vec<ResolvedVc<EcmascriptInputTransforms>> = Vec::new();
827 let mut collected_postprocess: Vec<ResolvedVc<EcmascriptInputTransforms>> = Vec::new();
828
829 let options_value = options.await?;
830 'outer: for (i, rule) in options_value.rules.iter().enumerate() {
831 if processed_rules.contains(&i) {
832 continue;
833 }
834 if rule.matches(source, path_ref, &reference_type).await? {
835 for effect in rule.effects() {
836 match effect {
837 ModuleRuleEffect::Ignore => {
838 return Ok(ProcessResult::Ignore.cell());
839 }
840 ModuleRuleEffect::SourceTransforms(transforms) => {
841 current_source = transforms
842 .transform(*current_source, Vc::upcast(module_asset_context))
843 .to_resolved()
844 .await?;
845 if current_source.ident().to_resolved().await? != ident {
846 if let Some(transition) = module_asset_context
848 .await?
849 .transitions
850 .await?
851 .get_by_rules(current_source, &reference_type)
852 .await?
853 {
854 return Ok(transition.process(
855 *current_source,
856 module_asset_context,
857 reference_type,
858 ));
859 } else {
860 let mut processed_rules = processed_rules.clone();
861 processed_rules.push(i);
862 return Box::pin(process_default(
863 module_asset_context,
864 current_source,
865 reference_type,
866 processed_rules,
867 ))
868 .await;
869 }
870 }
871 }
872 ModuleRuleEffect::ModuleType(module) => {
873 let mut module = module.clone();
877 apply_module_rule_transforms(
878 &mut module,
879 &mut collected_preprocess,
880 &mut collected_main,
881 &mut collected_postprocess,
882 ident,
883 current_source,
884 )
885 .await?;
886 current_module_type = Some(module);
887 break 'outer;
888 }
889 ModuleRuleEffect::ExtendEcmascriptTransforms {
890 preprocess: extend_preprocess,
891 main: extend_main,
892 postprocess: extend_postprocess,
893 } => {
894 collected_preprocess.push(*extend_preprocess);
896 collected_main.push(*extend_main);
897 collected_postprocess.push(*extend_postprocess);
898 }
899 }
900 }
901 }
902 }
903
904 let Some(module_type) = current_module_type else {
905 return Ok(ProcessResult::Unknown(current_source).cell());
906 };
907
908 let module = apply_module_type(
909 current_source,
910 module_asset_context,
911 module_type.cell(),
912 reference_type,
913 inner_assets,
914 )
915 .await?;
916
917 Ok(module)
918}
919
920#[turbo_tasks::function]
923pub async fn externals_tracing_module_context(
924 compile_time_info: Vc<CompileTimeInfo>,
925 resolve_typescript: bool,
926 prune: Option<(FileSystemPath, ResolvedVc<Glob>)>,
927) -> Result<Vc<ModuleAssetContext>> {
928 let mut extensions = vec![rcstr!(".js"), rcstr!(".node"), rcstr!(".json")];
929 if resolve_typescript {
930 extensions.insert(0, rcstr!(".ts"));
931 }
932
933 let prune_rules = match prune {
934 Some((base, glob)) => vec![ModuleRule::new(
935 RuleCondition::ResourcePathGlob {
936 base,
937 glob: glob.await?,
938 },
939 vec![ModuleRuleEffect::Ignore],
940 )],
941 None => vec![],
942 };
943
944 let resolve_options = ResolveOptionsContext {
945 custom_extensions: Some(extensions),
946 emulate_environment: Some(compile_time_info.await?.environment),
947 loose_errors: true,
948 collect_affecting_sources: true,
949 custom_conditions: vec![rcstr!("node")],
950 module_sync: ConditionValue::Unknown,
951 ..Default::default()
952 };
953
954 Ok(ModuleAssetContext::new_without_replace_externals(
955 Default::default(),
956 compile_time_info,
957 ModuleOptionsContext {
963 ecmascript: EcmascriptOptionsContext {
964 enable_typescript_transform: Some(
965 TypescriptTransformOptions::default().resolved_cell(),
966 ),
967 source_maps: SourceMapsType::None,
970 ..Default::default()
971 },
972 css: CssOptionsContext {
973 source_maps: SourceMapsType::None,
974 enable_raw_css: true,
975 ..Default::default()
976 },
977 environment: None,
980 analyze_mode: AnalyzeMode::Tracing,
981 module_rules: prune_rules,
982 ..Default::default()
985 }
986 .cell(),
987 resolve_options.cell(),
988 Layer::new(rcstr!("externals-tracing")),
989 ))
990}
991
992#[turbo_tasks::value_impl]
993impl AssetContext for ModuleAssetContext {
994 #[turbo_tasks::function]
995 fn compile_time_info(&self) -> Vc<CompileTimeInfo> {
996 *self.compile_time_info
997 }
998
999 fn layer(&self) -> Layer {
1000 self.layer.clone()
1001 }
1002
1003 #[turbo_tasks::function]
1004 async fn resolve_options(
1005 self: Vc<Self>,
1006 origin_path: FileSystemPath,
1007 ) -> Result<Vc<ResolveOptions>> {
1008 let this = self.await?;
1009 let module_asset_context = if let Some(transition) = this.transition {
1010 transition.process_context(self)
1011 } else {
1012 self
1013 };
1014 let options = resolve_options(
1016 origin_path.parent(),
1017 *module_asset_context.await?.resolve_options_context,
1018 );
1019 let runtime_import_map = turbopack_ecmascript_runtime::turbopack_runtime_import_map()
1023 .to_resolved()
1024 .await?;
1025 Ok(options.with_extended_import_map(*runtime_import_map))
1026 }
1027
1028 #[turbo_tasks::function]
1029 async fn resolve_asset(
1030 self: Vc<Self>,
1031 origin_path: FileSystemPath,
1032 request: Vc<Request>,
1033 resolve_options: Vc<ResolveOptions>,
1034 reference_type: ReferenceType,
1035 ) -> Result<Vc<ModuleResolveResult>> {
1036 let context_path = origin_path.parent();
1037
1038 let result = resolve(
1039 context_path,
1040 reference_type.clone(),
1041 request,
1042 resolve_options,
1043 );
1044
1045 let mut result = self.process_resolve_result(*result.to_resolved().await?, reference_type);
1046 let this = self.await?;
1047 if this.is_types_resolving_enabled().await? {
1048 let types_result = type_resolve(
1049 Vc::upcast(PlainResolveOrigin::new(Vc::upcast(self), origin_path)),
1050 request,
1051 );
1052
1053 result = ModuleResolveResult::alternatives(vec![result, types_result]);
1054 }
1055
1056 Ok(result)
1057 }
1058
1059 #[turbo_tasks::function]
1060 async fn process_resolve_result(
1061 self: Vc<Self>,
1062 result: Vc<ResolveResult>,
1063 reference_type: ReferenceType,
1064 ) -> Result<Vc<ModuleResolveResult>> {
1065 let this = self.await?;
1066
1067 let replace_externals = this.replace_externals;
1068 let import_externals = this
1069 .module_options_context
1070 .await?
1071 .ecmascript
1072 .import_externals;
1073
1074 let result = result.await?;
1075
1076 let result = result
1077 .map_primary_items(|item| {
1078 let reference_type = reference_type.clone();
1079 async move {
1080 Ok(match item {
1081 ResolveResultItem::Source(source) => {
1082 match &*self.process(*source, reference_type).await? {
1083 ProcessResult::Module(module) => {
1084 ModuleResolveResultItem::Module(*module)
1085 }
1086 ProcessResult::Unknown(source) => {
1087 ModuleResolveResultItem::Unknown(*source)
1088 }
1089 ProcessResult::Ignore => ModuleResolveResultItem::Ignore,
1090 }
1091 }
1092 ResolveResultItem::External {
1093 name,
1094 ty,
1095 traced,
1096 target,
1097 } => {
1098 let replacement = if replace_externals {
1099 let target = if let Some(mut target) = target {
1102 loop {
1103 let parent = target.parent();
1104 if parent.is_root() {
1105 break;
1106 }
1107 if parent.file_name() == "node_modules" {
1108 break;
1109 }
1110 if parent.file_name().starts_with("@")
1111 && parent.parent().file_name() == "node_modules"
1112 {
1113 break;
1114 }
1115 target = parent;
1116 }
1117 Some(target)
1118 } else {
1119 None
1120 };
1121
1122 let analyze_mode = if traced == ExternalTraced::Traced
1123 && let Some(options) = &self
1124 .module_options_context()
1125 .await?
1126 .enable_externals_tracing
1127 {
1128 let options = options.await?;
1133 let origin = PlainResolveOrigin::new(
1134 Vc::upcast(externals_tracing_module_context(
1135 *options.compile_time_info,
1136 false,
1137 None,
1138 )),
1139 target
1144 .as_ref()
1145 .unwrap_or(&options.tracing_root)
1146 .join("_")?,
1147 );
1148 CachedExternalTracingMode::Traced {
1149 origin: ResolvedVc::upcast(origin.to_resolved().await?),
1150 }
1151 } else {
1152 CachedExternalTracingMode::Untraced
1153 };
1154
1155 replace_external(&name, ty, target, import_externals, analyze_mode)
1156 .await?
1157 } else {
1158 None
1159 };
1160
1161 replacement
1162 .unwrap_or_else(|| ModuleResolveResultItem::External { name, ty })
1163 }
1164 ResolveResultItem::Ignore => ModuleResolveResultItem::Ignore,
1165 ResolveResultItem::Empty => ModuleResolveResultItem::Empty,
1166 ResolveResultItem::Error(e) => ModuleResolveResultItem::Error(e),
1167 ResolveResultItem::Custom(u8) => ModuleResolveResultItem::Custom(u8),
1168 })
1169 }
1170 })
1171 .await?;
1172
1173 Ok(result.cell())
1174 }
1175
1176 #[turbo_tasks::function]
1177 async fn process(
1178 self: Vc<Self>,
1179 asset: ResolvedVc<Box<dyn Source>>,
1180 reference_type: ReferenceType,
1181 ) -> Result<Vc<ProcessResult>> {
1182 let this = self.await?;
1183 if let Some(transition) = this.transition {
1184 Ok(transition.process(*asset, self, reference_type))
1185 } else {
1186 Ok(self
1187 .process_with_transition_rules(asset, reference_type)
1188 .await?)
1189 }
1190 }
1191
1192 #[turbo_tasks::function]
1193 async fn with_transition(&self, transition: RcStr) -> Result<Vc<Box<dyn AssetContext>>> {
1194 Ok(
1195 if let Some(transition) = self.transitions.await?.get_named(transition) {
1196 Vc::upcast(ModuleAssetContext::new_transition(
1197 *self.transitions,
1198 *self.compile_time_info,
1199 *self.module_options_context,
1200 *self.resolve_options_context,
1201 self.layer.clone(),
1202 *transition,
1203 ))
1204 } else {
1205 Vc::upcast(ModuleAssetContext::new(
1207 *self.transitions,
1208 *self.compile_time_info,
1209 *self.module_options_context,
1210 *self.resolve_options_context,
1211 self.layer.clone(),
1212 ))
1213 },
1214 )
1215 }
1216}
1217
1218#[turbo_tasks::function]
1219pub async fn emit_asset(asset: Vc<Box<dyn OutputAsset>>) -> Result<()> {
1220 asset
1221 .content()
1222 .write(asset.path().owned().await?)
1223 .as_side_effect()
1224 .await?;
1225
1226 Ok(())
1227}
1228
1229#[turbo_tasks::function]
1230pub async fn emit_assets_into_dir(
1231 assets: Vc<ExpandedOutputAssets>,
1232 output_dir: FileSystemPath,
1233) -> Result<()> {
1234 let assets = assets.await?;
1235 let paths = assets.iter().map(|&asset| asset.path()).try_join().await?;
1236 for (&asset, path) in assets.iter().zip(paths.iter()) {
1237 if path.is_inside_ref(&output_dir) {
1238 emit_asset(*asset).as_side_effect().await?;
1239 }
1240 }
1241 Ok(())
1242}
1243
1244#[turbo_tasks::function(operation, root)]
1245pub async fn emit_assets_into_dir_operation(
1246 assets: ResolvedVc<ExpandedOutputAssets>,
1247 output_dir: FileSystemPath,
1248) -> Result<()> {
1249 emit_assets_into_dir(*assets, output_dir)
1250 .as_side_effect()
1251 .await?;
1252 Ok(())
1253}
1254
1255pub async fn replace_external(
1257 name: &RcStr,
1258 ty: ExternalType,
1259 target: Option<FileSystemPath>,
1260 import_externals: bool,
1261 analyze_mode: CachedExternalTracingMode,
1262) -> Result<Option<ModuleResolveResultItem>> {
1263 let external_type = match ty {
1264 ExternalType::CommonJs => CachedExternalType::CommonJs,
1265 ExternalType::EcmaScriptModule => {
1266 if import_externals {
1267 CachedExternalType::EcmaScriptViaImport
1268 } else {
1269 CachedExternalType::EcmaScriptViaRequire
1270 }
1271 }
1272 ExternalType::Global => CachedExternalType::Global,
1273 ExternalType::Script => CachedExternalType::Script,
1274 ExternalType::Url => {
1275 return Ok(None);
1277 }
1278 };
1279
1280 let module = CachedExternalModule::new(name.clone(), target, external_type, analyze_mode)
1281 .to_resolved()
1282 .await?;
1283
1284 Ok(Some(ModuleResolveResultItem::Module(ResolvedVc::upcast(
1285 module,
1286 ))))
1287}