Skip to main content

turbopack/
lib.rs

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    async_chunk::proxy::LazyCompilationProxyModule,
54    module_canonicalization::{EcmascriptModuleCanonicalization, canonicalize_ecmascript_module},
55    references::external_module::{
56        CachedExternalModule, CachedExternalTracingMode, CachedExternalType,
57    },
58};
59use turbopack_node::transforms::webpack::{WebpackLoaderItem, WebpackLoaderItems, WebpackLoaders};
60use turbopack_resolve::{
61    resolve::resolve_options, resolve_options_context::ResolveOptionsContext,
62    typescript::type_resolve,
63};
64use turbopack_static::{css::StaticUrlCssModule, ecma::StaticUrlJsModule};
65use turbopack_wasm::{module_asset::WebAssemblyModuleAsset, source::WebAssemblySource};
66
67use crate::{
68    evaluate_context::{config_tracing_module_context, node_evaluate_asset_context},
69    module_options::{
70        CssOptionsContext, CustomModuleType, EcmascriptOptionsContext, TypescriptTransformOptions,
71        package_import_map_from_context, package_import_map_from_import_mapping,
72    },
73    transition::{Transition, TransitionOptions},
74};
75
76async fn apply_module_type(
77    source: ResolvedVc<Box<dyn Source>>,
78    module_asset_context: Vc<ModuleAssetContext>,
79    module_type: Vc<ModuleType>,
80    reference_type: ReferenceType,
81    inner_assets: Option<ResolvedVc<InnerAssets>>,
82) -> Result<Vc<ProcessResult>> {
83    let module_options = module_asset_context.module_options_context().await?;
84    let follow_reexports = module_options.follow_reexports;
85    let module_fragments_enabled = module_options.module_fragments_enabled;
86    let part = match &reference_type {
87        ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::ImportPart(part)) => {
88            Some(part)
89        }
90        _ => None,
91    };
92    let css_import_context = match reference_type {
93        ReferenceType::Css(CssReferenceSubType::AtImport(import)) => import,
94        _ => None,
95    };
96    let is_evaluation = matches!(&part, Some(ModulePart::Evaluation));
97    let is_lazy_dynamic_import = matches!(
98        &reference_type,
99        ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::LazyDynamicImport)
100    );
101
102    let module_type = &*module_type.await?;
103    let module = match module_type {
104        ModuleType::Ecmascript {
105            preprocess,
106            main,
107            postprocess,
108            options,
109        }
110        | ModuleType::EcmascriptExtensionless {
111            preprocess,
112            main,
113            postprocess,
114            options,
115        }
116        | ModuleType::Typescript {
117            preprocess,
118            main,
119            postprocess,
120            tsx: _,
121            analyze_types: _,
122            options,
123        }
124        | ModuleType::TypescriptDeclaration {
125            preprocess,
126            main,
127            postprocess,
128            options,
129        } => {
130            let context_for_module = match module_type {
131                ModuleType::Typescript { analyze_types, .. } if *analyze_types => {
132                    module_asset_context.with_types_resolving_enabled()
133                }
134                ModuleType::TypescriptDeclaration { .. } => {
135                    module_asset_context.with_types_resolving_enabled()
136                }
137                _ => module_asset_context,
138            }
139            .to_resolved()
140            .await?;
141            let side_effect_free_packages = module_asset_context
142                .module_options_context()
143                .await?
144                .side_effect_free_packages;
145            let mut builder = EcmascriptModuleAsset::builder(
146                source,
147                ResolvedVc::upcast(context_for_module),
148                preprocess
149                    .extend(**main)
150                    .extend(**postprocess)
151                    .to_resolved()
152                    .await?,
153                *options,
154                module_asset_context
155                    .compile_time_info()
156                    .to_resolved()
157                    .await?,
158                side_effect_free_packages,
159            );
160            match module_type {
161                ModuleType::Ecmascript { .. } => {
162                    builder = builder.with_type(EcmascriptModuleAssetType::Ecmascript)
163                }
164                ModuleType::EcmascriptExtensionless { .. } => {
165                    builder = builder.with_type(EcmascriptModuleAssetType::EcmascriptExtensionless)
166                }
167                ModuleType::Typescript {
168                    tsx, analyze_types, ..
169                } => {
170                    builder = builder.with_type(EcmascriptModuleAssetType::Typescript {
171                        tsx: *tsx,
172                        analyze_types: *analyze_types,
173                    })
174                }
175                ModuleType::TypescriptDeclaration { .. } => {
176                    builder = builder.with_type(EcmascriptModuleAssetType::TypescriptDeclaration)
177                }
178                _ => unreachable!(),
179            }
180
181            if let Some(inner_assets) = inner_assets {
182                builder = builder.with_inner_assets(inner_assets);
183            }
184
185            let module = builder.build().to_resolved().await?;
186            if matches!(reference_type, ReferenceType::Runtime) {
187                ResolvedVc::upcast(module)
188            } else {
189                // Check side effect free on the intermediate module before following reexports
190                // This can skip the module earlier and could skip more modules than only doing it
191                // at the end. Also we avoid parsing/analyzing the module in this
192                // case, because we would need to parse/analyze it for reexports.
193                if (follow_reexports || module_fragments_enabled) && is_evaluation {
194                    // If we are tree shaking, skip the evaluation part if the module is marked as
195                    // side effect free.
196                    if *module.side_effects().await? == ModuleSideEffects::SideEffectFree {
197                        return Ok(ProcessResult::Ignore.cell());
198                    }
199                }
200
201                let canonicalization = if module_fragments_enabled {
202                    EcmascriptModuleCanonicalization::ModuleFragments(
203                        part.cloned().unwrap_or_else(ModulePart::facade),
204                    )
205                } else if follow_reexports {
206                    EcmascriptModuleCanonicalization::FollowReexports(part.cloned())
207                } else {
208                    EcmascriptModuleCanonicalization::None
209                };
210
211                if is_lazy_dynamic_import {
212                    ResolvedVc::upcast(
213                        LazyCompilationProxyModule::new_deferred(*module, canonicalization)
214                            .to_resolved()
215                            .await?,
216                    )
217                } else {
218                    ResolvedVc::upcast(
219                        canonicalize_ecmascript_module(*module, canonicalization)
220                            .to_resolved()
221                            .await?,
222                    )
223                }
224            }
225        }
226        ModuleType::Raw => ResolvedVc::upcast(RawModule::new(*source).to_resolved().await?),
227        ModuleType::NodeAddon => {
228            ResolvedVc::upcast(NodeAddonModule::new(*source).to_resolved().await?)
229        }
230        ModuleType::CssModule => ResolvedVc::upcast(
231            EcmascriptCssModule::new(*source, Vc::upcast(module_asset_context))
232                .to_resolved()
233                .await?,
234        ),
235
236        ModuleType::Css {
237            ty,
238            environment,
239            lightningcss_features,
240            module_css_debuggable_idents,
241        } => ResolvedVc::upcast(
242            CssModule::new(
243                *source,
244                Vc::upcast(module_asset_context),
245                *ty,
246                css_import_context.map(|c| *c),
247                environment.as_deref().copied(),
248                *lightningcss_features,
249                *module_css_debuggable_idents,
250            )
251            .to_resolved()
252            .await?,
253        ),
254        ModuleType::StaticUrlJs { tag } => ResolvedVc::upcast(
255            StaticUrlJsModule::new(*source, tag.clone())
256                .to_resolved()
257                .await?,
258        ),
259        ModuleType::StaticUrlCss { tag } => ResolvedVc::upcast(
260            StaticUrlCssModule::new(*source, tag.clone())
261                .to_resolved()
262                .await?,
263        ),
264        ModuleType::WebAssembly { source_ty } => ResolvedVc::upcast(
265            WebAssemblyModuleAsset::new(
266                WebAssemblySource::new(*source, *source_ty),
267                Vc::upcast(module_asset_context),
268            )
269            .to_resolved()
270            .await?,
271        ),
272        ModuleType::Custom(custom) => {
273            custom
274                .create_module(*source, module_asset_context, reference_type)
275                .to_resolved()
276                .await?
277        }
278    };
279
280    if (follow_reexports || module_fragments_enabled) && is_evaluation {
281        // If we are tree shaking, skip the evaluation part if the module is marked as
282        // side effect free.
283        if *module.side_effects().await? == ModuleSideEffects::SideEffectFree {
284            return Ok(ProcessResult::Ignore.cell());
285        }
286    }
287
288    Ok(ProcessResult::Module(module).cell())
289}
290
291#[turbo_tasks::value]
292#[derive(Debug)]
293pub struct ModuleAssetContext {
294    pub transitions: ResolvedVc<TransitionOptions>,
295    pub compile_time_info: ResolvedVc<CompileTimeInfo>,
296    pub module_options_context: ResolvedVc<ModuleOptionsContext>,
297    pub resolve_options_context: ResolvedVc<ResolveOptionsContext>,
298    pub layer: Layer,
299    transition: Option<ResolvedVc<Box<dyn Transition>>>,
300    /// Whether to replace external resolutions with CachedExternalModules. Used with
301    /// ModuleOptionsContext.enable_externals_tracing to handle transitive external dependencies.
302    replace_externals: bool,
303}
304
305#[turbo_tasks::value_impl]
306impl ModuleAssetContext {
307    #[turbo_tasks::function]
308    pub fn new(
309        transitions: ResolvedVc<TransitionOptions>,
310        compile_time_info: ResolvedVc<CompileTimeInfo>,
311        module_options_context: ResolvedVc<ModuleOptionsContext>,
312        resolve_options_context: ResolvedVc<ResolveOptionsContext>,
313        layer: Layer,
314    ) -> Vc<Self> {
315        Self::cell(ModuleAssetContext {
316            transitions,
317            compile_time_info,
318            module_options_context,
319            resolve_options_context,
320            transition: None,
321            layer,
322            replace_externals: true,
323        })
324    }
325
326    #[turbo_tasks::function]
327    pub fn new_transition(
328        transitions: ResolvedVc<TransitionOptions>,
329        compile_time_info: ResolvedVc<CompileTimeInfo>,
330        module_options_context: ResolvedVc<ModuleOptionsContext>,
331        resolve_options_context: ResolvedVc<ResolveOptionsContext>,
332        layer: Layer,
333        transition: ResolvedVc<Box<dyn Transition>>,
334    ) -> Vc<Self> {
335        Self::cell(ModuleAssetContext {
336            transitions,
337            compile_time_info,
338            module_options_context,
339            resolve_options_context,
340            layer,
341            transition: Some(transition),
342            replace_externals: true,
343        })
344    }
345
346    /// Doesn't replace external resolve results with a CachedExternalModule.
347    #[turbo_tasks::function]
348    pub fn new_without_replace_externals(
349        transitions: ResolvedVc<TransitionOptions>,
350        compile_time_info: ResolvedVc<CompileTimeInfo>,
351        module_options_context: ResolvedVc<ModuleOptionsContext>,
352        resolve_options_context: ResolvedVc<ResolveOptionsContext>,
353        layer: Layer,
354    ) -> Vc<Self> {
355        Self::cell(ModuleAssetContext {
356            transitions,
357            compile_time_info,
358            module_options_context,
359            resolve_options_context,
360            transition: None,
361            layer,
362            replace_externals: false,
363        })
364    }
365
366    #[turbo_tasks::function]
367    pub fn module_options_context(&self) -> Vc<ModuleOptionsContext> {
368        *self.module_options_context
369    }
370
371    #[turbo_tasks::function]
372    pub fn resolve_options_context(&self) -> Vc<ResolveOptionsContext> {
373        *self.resolve_options_context
374    }
375
376    #[turbo_tasks::function]
377    pub async fn with_types_resolving_enabled(self: Vc<Self>) -> Result<Vc<ModuleAssetContext>> {
378        let this = self.await?;
379        if this.is_types_resolving_enabled().await? {
380            return Ok(self);
381        }
382        let resolve_options_context = *this
383            .resolve_options_context
384            .with_types_enabled()
385            .to_resolved()
386            .await?;
387
388        Ok(ModuleAssetContext::new(
389            *this.transitions,
390            *this.compile_time_info,
391            *this.module_options_context,
392            resolve_options_context,
393            this.layer.clone(),
394        ))
395    }
396}
397
398impl ModuleAssetContext {
399    async fn is_types_resolving_enabled(&self) -> Result<bool> {
400        let resolve_options_context = self.resolve_options_context.await?;
401        Ok(resolve_options_context.enable_types && resolve_options_context.enable_typescript)
402    }
403    async fn process_with_transition_rules(
404        self: Vc<Self>,
405        source: ResolvedVc<Box<dyn Source>>,
406        reference_type: ReferenceType,
407    ) -> Result<Vc<ProcessResult>> {
408        let this = self.await?;
409        Ok(
410            if let Some(transition) = this
411                .transitions
412                .await?
413                .get_by_rules(source, &reference_type)
414                .await?
415            {
416                transition.process(*source, self, reference_type)
417            } else {
418                self.process_default(source, reference_type).await?
419            },
420        )
421    }
422
423    async fn process_default(
424        self: Vc<Self>,
425        source: ResolvedVc<Box<dyn Source>>,
426        reference_type: ReferenceType,
427    ) -> Result<Vc<ProcessResult>> {
428        process_default(self, source, reference_type, Vec::new()).await
429    }
430}
431
432async fn process_default(
433    module_asset_context: Vc<ModuleAssetContext>,
434    source: ResolvedVc<Box<dyn Source>>,
435    reference_type: ReferenceType,
436    processed_rules: Vec<usize>,
437) -> Result<Vc<ProcessResult>> {
438    let span = tracing::info_span!(
439        "process module",
440        name = %source.ident().to_string().await?,
441        layer = Empty,
442        reference_type = display(&reference_type)
443    );
444    if !span.is_disabled() {
445        // You can't await multiple times in the span macro call parameters.
446        span.record("layer", module_asset_context.await?.layer.name().as_str());
447    }
448
449    process_default_internal(
450        module_asset_context,
451        source,
452        reference_type,
453        processed_rules,
454    )
455    .instrument(span)
456    .await
457}
458
459/// Apply collected transforms to a module type.
460/// For Ecmascript/Typescript variants: merge collected transforms into the module type.
461/// For Custom: call extend_ecmascript_transforms() if any transforms exist.
462/// For non-ecmascript types: warn if transforms exist, return unchanged.
463async fn apply_module_rule_transforms(
464    module_type: &mut ModuleType,
465    collected_preprocess: &mut Vec<ResolvedVc<EcmascriptInputTransforms>>,
466    collected_main: &mut Vec<ResolvedVc<EcmascriptInputTransforms>>,
467    collected_postprocess: &mut Vec<ResolvedVc<EcmascriptInputTransforms>>,
468    ident: ResolvedVc<AssetIdent>,
469    current_source: ResolvedVc<Box<dyn Source>>,
470) -> Result<()> {
471    let has_transforms = !collected_preprocess.is_empty()
472        || !collected_main.is_empty()
473        || !collected_postprocess.is_empty();
474
475    // If no transforms were collected, return early
476    if !has_transforms {
477        return Ok(());
478    }
479
480    match module_type {
481        ModuleType::Ecmascript {
482            preprocess,
483            main,
484            postprocess,
485            ..
486        }
487        | ModuleType::Typescript {
488            preprocess,
489            main,
490            postprocess,
491            ..
492        }
493        | ModuleType::TypescriptDeclaration {
494            preprocess,
495            main,
496            postprocess,
497            ..
498        }
499        | ModuleType::EcmascriptExtensionless {
500            preprocess,
501            main,
502            postprocess,
503            ..
504        } => {
505            // Apply collected preprocess/main in order, then module type's transforms
506            let mut final_preprocess = EcmascriptInputTransforms::empty();
507            for vc in collected_preprocess.drain(..) {
508                final_preprocess = final_preprocess.extend(*vc);
509            }
510            final_preprocess = final_preprocess.extend(**preprocess);
511            *preprocess = final_preprocess.to_resolved().await?;
512
513            let mut final_main = EcmascriptInputTransforms::empty();
514            for vc in collected_main.drain(..) {
515                final_main = final_main.extend(*vc);
516            }
517            final_main = final_main.extend(**main);
518            *main = final_main.to_resolved().await?;
519
520            // Apply module type's postprocess first, then collected postprocess
521            let mut final_postprocess = **postprocess;
522            for vc in collected_postprocess.drain(..) {
523                final_postprocess = final_postprocess.extend(*vc);
524            }
525            *postprocess = final_postprocess.to_resolved().await?;
526        }
527        ModuleType::Custom(custom_module_type) => {
528            if has_transforms {
529                // Combine collected transforms into single Vcs
530                let mut combined_preprocess = EcmascriptInputTransforms::empty();
531                for vc in collected_preprocess.drain(..) {
532                    combined_preprocess = combined_preprocess.extend(*vc);
533                }
534                let mut combined_main = EcmascriptInputTransforms::empty();
535                for vc in collected_main.drain(..) {
536                    combined_main = combined_main.extend(*vc);
537                }
538                let mut combined_postprocess = EcmascriptInputTransforms::empty();
539                for vc in collected_postprocess.drain(..) {
540                    combined_postprocess = combined_postprocess.extend(*vc);
541                }
542
543                match custom_module_type
544                    .extend_ecmascript_transforms(
545                        combined_preprocess,
546                        combined_main,
547                        combined_postprocess,
548                    )
549                    .to_resolved()
550                    .await
551                {
552                    Ok(new_custom_module_type) => {
553                        *custom_module_type = new_custom_module_type;
554                    }
555                    Err(_) => {
556                        ModuleIssue::new(
557                            *ident,
558                            rcstr!("Invalid module type"),
559                            rcstr!(
560                                "The custom module type didn't accept the additional Ecmascript \
561                                 transforms"
562                            ),
563                            Some(IssueSource::from_source_only(current_source)),
564                        )
565                        .to_resolved()
566                        .await?
567                        .emit();
568                    }
569                }
570            }
571        }
572        other => {
573            if has_transforms {
574                ModuleIssue::new(
575                    *ident,
576                    rcstr!("Invalid module type"),
577                    format!(
578                        "The module type must be Ecmascript or Typescript to add Ecmascript \
579                         transforms (got {})",
580                        other
581                    )
582                    .into(),
583                    Some(IssueSource::from_source_only(current_source)),
584                )
585                .to_resolved()
586                .await?
587                .emit();
588                collected_preprocess.clear();
589                collected_main.clear();
590                collected_postprocess.clear();
591            }
592        }
593    }
594    Ok(())
595}
596
597async fn process_default_internal(
598    module_asset_context: Vc<ModuleAssetContext>,
599    source: ResolvedVc<Box<dyn Source>>,
600    reference_type: ReferenceType,
601    processed_rules: Vec<usize>,
602) -> Result<Vc<ProcessResult>> {
603    let ident = source.ident().to_resolved().await?;
604    let ident_ref = ident.await?;
605    let path_ref = &ident_ref.path;
606    let options = ModuleOptions::new(
607        path_ref.parent(),
608        module_asset_context.module_options_context(),
609        module_asset_context.resolve_options_context(),
610    );
611
612    let inner_assets = match &reference_type {
613        ReferenceType::Internal(inner_assets) => Some(*inner_assets),
614        _ => None,
615    };
616    let mut current_source = source;
617    let mut current_module_type = None;
618
619    // Handle turbopackLoader import attributes: apply inline loader as source transform
620    if let ReferenceType::EcmaScriptModules(
621        EcmaScriptModulesReferenceSubType::ImportWithTurbopackUse {
622            ref loader,
623            ref rename_as,
624            ref module_type,
625        },
626    ) = reference_type
627    {
628        let module_options_context = module_asset_context.module_options_context().await?;
629        let webpack_loaders_options = module_options_context
630            .enable_webpack_loaders
631            .as_ref()
632            .context(
633                "turbopackUse import assertions require webpack loaders to be enabled \
634                 (enable_webpack_loaders)",
635            )?
636            .await?;
637        let execution_context = module_options_context
638            .execution_context
639            .context("execution_context is required for turbopackUse import assertions")?;
640        let execution_context_value = execution_context.await?;
641
642        let resolve_options_context = module_asset_context
643            .resolve_options_context()
644            .to_resolved()
645            .await?;
646        let source_maps = matches!(
647            module_options_context.ecmascript.source_maps,
648            SourceMapsType::Full
649        );
650
651        // Determine the import map for loader-runner
652        let loader_runner_package = webpack_loaders_options.loader_runner_package;
653
654        let import_map = if let Some(loader_runner_package) = loader_runner_package {
655            package_import_map_from_import_mapping(rcstr!("loader-runner"), *loader_runner_package)
656        } else {
657            package_import_map_from_context(
658                rcstr!("loader-runner"),
659                execution_context_value.project_path.clone(),
660            )
661        };
662
663        let evaluate_context = node_evaluate_asset_context(
664            *execution_context,
665            Some(import_map),
666            None,
667            Layer::new(rcstr!("webpack_loaders")),
668            false,
669        )
670        .to_resolved()
671        .await?;
672
673        let loader_request = execution_context_value
674            .project_path
675            .get_relative_request_to(&loader.loader)
676            .context("Loader path must be on project filesystem")?;
677        let webpack_loader_item = WebpackLoaderItem {
678            loader: loader_request,
679            options: loader.options.clone(),
680        };
681        let loaders_vc = WebpackLoaderItems(vec![webpack_loader_item]).cell();
682        let webpack_loaders = WebpackLoaders::new(
683            *evaluate_context,
684            *execution_context,
685            loaders_vc,
686            *webpack_loaders_options.target,
687            webpack_loaders_options.mode.clone(),
688            rename_as.clone(),
689            *resolve_options_context,
690            source_maps,
691            config_tracing_module_context(*execution_context),
692        )
693        .to_resolved()
694        .await?;
695
696        let transforms = Vc::<SourceTransforms>::cell(vec![ResolvedVc::upcast(webpack_loaders)]);
697        current_source = transforms
698            .transform(*current_source, Vc::upcast(module_asset_context))
699            .to_resolved()
700            .await?;
701
702        // If turbopackModuleType is specified, skip rule matching and directly
703        // apply the requested module type with empty transforms (loader output
704        // is already processed).
705        if let Some(type_str) = module_type {
706            let empty_transforms = EcmascriptInputTransforms::empty().to_resolved().await?;
707            let default_options = EcmascriptOptions::default().resolved_cell();
708            let effect = ConfiguredModuleType::parse(type_str)?
709                .into_effect(
710                    empty_transforms,
711                    empty_transforms,
712                    empty_transforms,
713                    default_options,
714                    None,
715                    Default::default(),
716                )
717                .await?;
718            match effect {
719                ModuleRuleEffect::ModuleType(module_type) => {
720                    return apply_module_type(
721                        current_source,
722                        module_asset_context,
723                        module_type.cell(),
724                        reference_type,
725                        inner_assets,
726                    )
727                    .await;
728                }
729                ModuleRuleEffect::SourceTransforms(transforms) => {
730                    current_source = transforms
731                        .transform(*current_source, Vc::upcast(module_asset_context))
732                        .to_resolved()
733                        .await?;
734                    // Fall through to re-process with new ident
735                }
736                _ => bail!("Unexpected module rule effect for turbopackModuleType"),
737            }
738        }
739
740        // If the ident changed (e.g., due to rename_as), re-process from the
741        // beginning so the new extension is matched by the correct rules.
742        // Use a plain Import reference type to avoid re-applying turbopackUse
743        // loaders in the recursive call (which would cause an infinite loop).
744        if current_source.ident().to_resolved().await? != ident {
745            let plain_reference_type =
746                ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::Import);
747            if let Some(transition) = module_asset_context
748                .await?
749                .transitions
750                .await?
751                .get_by_rules(current_source, &plain_reference_type)
752                .await?
753            {
754                return Ok(transition.process(
755                    *current_source,
756                    module_asset_context,
757                    plain_reference_type,
758                ));
759            } else {
760                return Box::pin(process_default(
761                    module_asset_context,
762                    current_source,
763                    plain_reference_type,
764                    processed_rules,
765                ))
766                .await;
767            }
768        }
769    }
770
771    // Collect transforms from ExtendEcmascriptTransforms effects.
772    // They will be applied when ModuleType is set.
773    let mut collected_preprocess: Vec<ResolvedVc<EcmascriptInputTransforms>> = Vec::new();
774    let mut collected_main: Vec<ResolvedVc<EcmascriptInputTransforms>> = Vec::new();
775    let mut collected_postprocess: Vec<ResolvedVc<EcmascriptInputTransforms>> = Vec::new();
776
777    let options_value = options.await?;
778    'outer: for (i, rule) in options_value.rules.iter().enumerate() {
779        if processed_rules.contains(&i) {
780            continue;
781        }
782        if rule.matches(source, path_ref, &reference_type).await? {
783            for effect in rule.effects() {
784                match effect {
785                    ModuleRuleEffect::Ignore => {
786                        return Ok(ProcessResult::Ignore.cell());
787                    }
788                    ModuleRuleEffect::SourceTransforms(transforms) => {
789                        current_source = transforms
790                            .transform(*current_source, Vc::upcast(module_asset_context))
791                            .to_resolved()
792                            .await?;
793                        if current_source.ident().to_resolved().await? != ident {
794                            // The ident has been changed, so we need to apply new rules.
795                            if let Some(transition) = module_asset_context
796                                .await?
797                                .transitions
798                                .await?
799                                .get_by_rules(current_source, &reference_type)
800                                .await?
801                            {
802                                return Ok(transition.process(
803                                    *current_source,
804                                    module_asset_context,
805                                    reference_type,
806                                ));
807                            } else {
808                                let mut processed_rules = processed_rules.clone();
809                                processed_rules.push(i);
810                                return Box::pin(process_default(
811                                    module_asset_context,
812                                    current_source,
813                                    reference_type,
814                                    processed_rules,
815                                ))
816                                .await;
817                            }
818                        }
819                    }
820                    ModuleRuleEffect::ModuleType(module) => {
821                        // Apply any collected transforms to this module type and exit rule
822                        // processing. Once a ModuleType is determined, we
823                        // stop processing further rules.
824                        let mut module = module.clone();
825                        apply_module_rule_transforms(
826                            &mut module,
827                            &mut collected_preprocess,
828                            &mut collected_main,
829                            &mut collected_postprocess,
830                            ident,
831                            current_source,
832                        )
833                        .await?;
834                        current_module_type = Some(module);
835                        break 'outer;
836                    }
837                    ModuleRuleEffect::ExtendEcmascriptTransforms {
838                        preprocess: extend_preprocess,
839                        main: extend_main,
840                        postprocess: extend_postprocess,
841                    } => {
842                        // Collect transforms. They will be applied when ModuleType is set.
843                        collected_preprocess.push(*extend_preprocess);
844                        collected_main.push(*extend_main);
845                        collected_postprocess.push(*extend_postprocess);
846                    }
847                }
848            }
849        }
850    }
851
852    let Some(module_type) = current_module_type else {
853        return Ok(ProcessResult::Unknown(current_source).cell());
854    };
855
856    let module = apply_module_type(
857        current_source,
858        module_asset_context,
859        module_type.cell(),
860        reference_type,
861        inner_assets,
862    )
863    .await?;
864
865    Ok(module)
866}
867
868/// `prune` skips matching files as the graph is walked, rather than filtering them out of the
869/// result afterwards.
870#[turbo_tasks::function]
871pub async fn externals_tracing_module_context(
872    compile_time_info: Vc<CompileTimeInfo>,
873    resolve_typescript: bool,
874    prune: Option<(FileSystemPath, ResolvedVc<Glob>)>,
875    trace_file_references: bool,
876) -> Result<Vc<ModuleAssetContext>> {
877    let mut extensions = vec![rcstr!(".js"), rcstr!(".node"), rcstr!(".json")];
878    if resolve_typescript {
879        extensions.insert(0, rcstr!(".ts"));
880    }
881
882    let prune_rules = match prune {
883        Some((base, glob)) => vec![ModuleRule::new(
884            RuleCondition::ResourcePathGlob {
885                base,
886                glob: glob.await?,
887            },
888            vec![ModuleRuleEffect::Ignore],
889        )],
890        None => vec![],
891    };
892
893    let resolve_options = ResolveOptionsContext {
894        custom_extensions: Some(extensions),
895        emulate_environment: Some(compile_time_info.await?.environment),
896        loose_errors: true,
897        collect_affecting_sources: true,
898        custom_conditions: vec![rcstr!("node")],
899        module_sync: ConditionValue::Unknown,
900        ..Default::default()
901    };
902
903    Ok(ModuleAssetContext::new_without_replace_externals(
904        Default::default(),
905        compile_time_info,
906        // This config should be kept in sync with
907        // turbopack/crates/turbopack-tracing/tests/node-file-trace.rs and
908        // turbopack/crates/turbopack-tracing/tests/unit.rs and
909        // turbopack/crates/turbopack/src/lib.rs and
910        // turbopack/crates/turbopack-nft/src/nft.rs
911        ModuleOptionsContext {
912            ecmascript: EcmascriptOptionsContext {
913                enable_typescript_transform: Some(
914                    TypescriptTransformOptions::default().resolved_cell(),
915                ),
916                // enable_types should not be enabled here. It gets set automatically when a TS file
917                // is encountered.
918                source_maps: SourceMapsType::None,
919                ..Default::default()
920            },
921            css: CssOptionsContext {
922                source_maps: SourceMapsType::None,
923                enable_raw_css: true,
924                ..Default::default()
925            },
926            // Environment is not passed in order to avoid downleveling JS / CSS for
927            // node-file-trace.
928            environment: None,
929            analyze_mode: AnalyzeMode {
930                is_codegen: false,
931                trace_file_references,
932            },
933            module_rules: prune_rules,
934            // Disable tree shaking. Even side-effect-free imports need to be traced, as they will
935            // execute at runtime.
936            ..Default::default()
937        }
938        .cell(),
939        resolve_options.cell(),
940        Layer::new(rcstr!("externals-tracing")),
941    ))
942}
943
944#[turbo_tasks::value_impl]
945impl AssetContext for ModuleAssetContext {
946    #[turbo_tasks::function]
947    fn compile_time_info(&self) -> Vc<CompileTimeInfo> {
948        *self.compile_time_info
949    }
950
951    fn layer(&self) -> Layer {
952        self.layer.clone()
953    }
954
955    #[turbo_tasks::function]
956    async fn resolve_options(
957        self: Vc<Self>,
958        origin_path: FileSystemPath,
959    ) -> Result<Vc<ResolveOptions>> {
960        let this = self.await?;
961        let module_asset_context = if let Some(transition) = this.transition {
962            transition.process_context(self)
963        } else {
964            self
965        };
966        // TODO move `apply_commonjs/esm_resolve_options` etc. to here
967        let options = resolve_options(
968            origin_path.parent(),
969            *module_asset_context.await?.resolve_options_context,
970        );
971        // Inject the turbopack-ecmascript-runtime import map so that
972        // @turbopack/* built-in modules and @vercel/turbopack-ecmascript-runtime/*
973        // paths are always resolvable.
974        let runtime_import_map = turbopack_ecmascript_runtime::turbopack_runtime_import_map()
975            .to_resolved()
976            .await?;
977        Ok(options.with_extended_import_map(*runtime_import_map))
978    }
979
980    #[turbo_tasks::function]
981    async fn resolve_asset(
982        self: Vc<Self>,
983        origin_path: FileSystemPath,
984        request: Vc<Request>,
985        resolve_options: Vc<ResolveOptions>,
986        reference_type: ReferenceType,
987    ) -> Result<Vc<ModuleResolveResult>> {
988        let context_path = origin_path.parent();
989
990        let result = resolve(
991            context_path,
992            reference_type.clone(),
993            request,
994            resolve_options,
995        );
996
997        let mut result = self.process_resolve_result(*result.to_resolved().await?, reference_type);
998        let this = self.await?;
999        if this.is_types_resolving_enabled().await? {
1000            let types_result = type_resolve(
1001                Vc::upcast(PlainResolveOrigin::new(Vc::upcast(self), origin_path)),
1002                request,
1003            );
1004
1005            result = ModuleResolveResult::alternatives(vec![result, types_result]);
1006        }
1007
1008        Ok(result)
1009    }
1010
1011    #[turbo_tasks::function]
1012    async fn process_resolve_result(
1013        self: Vc<Self>,
1014        result: Vc<ResolveResult>,
1015        reference_type: ReferenceType,
1016    ) -> Result<Vc<ModuleResolveResult>> {
1017        let this = self.await?;
1018
1019        let replace_externals = this.replace_externals;
1020        let import_externals = this
1021            .module_options_context
1022            .await?
1023            .ecmascript
1024            .import_externals;
1025
1026        let result = result.await?;
1027
1028        let result = result
1029            .map_primary_items(|item| {
1030                let reference_type = reference_type.clone();
1031                async move {
1032                    Ok(match item {
1033                        ResolveResultItem::Source(source) => {
1034                            match &*self.process(*source, reference_type).await? {
1035                                ProcessResult::Module(module) => {
1036                                    ModuleResolveResultItem::Module(*module)
1037                                }
1038                                ProcessResult::Unknown(source) => {
1039                                    ModuleResolveResultItem::Unknown(*source)
1040                                }
1041                                ProcessResult::Ignore => ModuleResolveResultItem::Ignore,
1042                            }
1043                        }
1044                        ResolveResultItem::External {
1045                            name,
1046                            ty,
1047                            traced,
1048                            target,
1049                        } => {
1050                            let replacement = if replace_externals {
1051                                // Determine the package folder, `target` is the full path to the
1052                                // resolved file.
1053                                let target = if let Some(mut target) = target {
1054                                    loop {
1055                                        let parent = target.parent();
1056                                        if parent.is_root() {
1057                                            break;
1058                                        }
1059                                        if parent.file_name() == "node_modules" {
1060                                            break;
1061                                        }
1062                                        if parent.file_name().starts_with("@")
1063                                            && parent.parent().file_name() == "node_modules"
1064                                        {
1065                                            break;
1066                                        }
1067                                        target = parent;
1068                                    }
1069                                    Some(target)
1070                                } else {
1071                                    None
1072                                };
1073
1074                                let analyze_mode = if traced == ExternalTraced::Traced
1075                                    && let Some(options) = &self
1076                                        .module_options_context()
1077                                        .await?
1078                                        .enable_externals_tracing
1079                                {
1080                                    // result.affecting_sources can be ignored for tracing, as this
1081                                    // request will later be resolved relative to tracing_root (or
1082                                    // the .next/node_modules/lodash-1238123 symlink) anyway.
1083
1084                                    let options = options.await?;
1085                                    let origin = PlainResolveOrigin::new(
1086                                        Vc::upcast(externals_tracing_module_context(
1087                                            *options.compile_time_info,
1088                                            /* resolve_typescript */ false,
1089                                            /* prune */ None,
1090                                            /* trace_file_references */ true,
1091                                        )),
1092                                        // If target is specified, a symlink will be created to
1093                                        // make the folder
1094                                        // itself available, but we still need to trace
1095                                        // resolving the individual file(s) inside the package.
1096                                        target
1097                                            .as_ref()
1098                                            .unwrap_or(&options.tracing_root)
1099                                            .join("_")?,
1100                                    );
1101                                    CachedExternalTracingMode::Traced {
1102                                        origin: ResolvedVc::upcast(origin.to_resolved().await?),
1103                                    }
1104                                } else {
1105                                    CachedExternalTracingMode::Untraced
1106                                };
1107
1108                                replace_external(&name, ty, target, import_externals, analyze_mode)
1109                                    .await?
1110                            } else {
1111                                None
1112                            };
1113
1114                            replacement
1115                                .unwrap_or_else(|| ModuleResolveResultItem::External { name, ty })
1116                        }
1117                        ResolveResultItem::Ignore => ModuleResolveResultItem::Ignore,
1118                        ResolveResultItem::Empty => ModuleResolveResultItem::Empty,
1119                        ResolveResultItem::Error(e) => ModuleResolveResultItem::Error(e),
1120                        ResolveResultItem::Custom(u8) => ModuleResolveResultItem::Custom(u8),
1121                    })
1122                }
1123            })
1124            .await?;
1125
1126        Ok(result.cell())
1127    }
1128
1129    #[turbo_tasks::function]
1130    async fn process(
1131        self: Vc<Self>,
1132        asset: ResolvedVc<Box<dyn Source>>,
1133        reference_type: ReferenceType,
1134    ) -> Result<Vc<ProcessResult>> {
1135        let this = self.await?;
1136        if let Some(transition) = this.transition {
1137            Ok(transition.process(*asset, self, reference_type))
1138        } else {
1139            Ok(self
1140                .process_with_transition_rules(asset, reference_type)
1141                .await?)
1142        }
1143    }
1144
1145    #[turbo_tasks::function]
1146    async fn with_transition(&self, transition: RcStr) -> Result<Vc<Box<dyn AssetContext>>> {
1147        Ok(
1148            if let Some(transition) = self.transitions.await?.get_named(transition) {
1149                Vc::upcast(ModuleAssetContext::new_transition(
1150                    *self.transitions,
1151                    *self.compile_time_info,
1152                    *self.module_options_context,
1153                    *self.resolve_options_context,
1154                    self.layer.clone(),
1155                    *transition,
1156                ))
1157            } else {
1158                // TODO report issue
1159                Vc::upcast(ModuleAssetContext::new(
1160                    *self.transitions,
1161                    *self.compile_time_info,
1162                    *self.module_options_context,
1163                    *self.resolve_options_context,
1164                    self.layer.clone(),
1165                ))
1166            },
1167        )
1168    }
1169}
1170
1171#[turbo_tasks::function]
1172pub async fn emit_asset(asset: Vc<Box<dyn OutputAsset>>) -> Result<()> {
1173    asset
1174        .content()
1175        .write(asset.path().owned().await?)
1176        .as_side_effect()
1177        .await?;
1178
1179    Ok(())
1180}
1181
1182#[turbo_tasks::function]
1183pub async fn emit_assets_into_dir(
1184    assets: Vc<ExpandedOutputAssets>,
1185    output_dir: FileSystemPath,
1186) -> Result<()> {
1187    let assets = assets.await?;
1188    let paths = assets.iter().map(|&asset| asset.path()).try_join().await?;
1189    for (&asset, path) in assets.iter().zip(paths.iter()) {
1190        if path.is_inside_ref(&output_dir) {
1191            emit_asset(*asset).as_side_effect().await?;
1192        }
1193    }
1194    Ok(())
1195}
1196
1197#[turbo_tasks::function(operation, root)]
1198pub async fn emit_assets_into_dir_operation(
1199    assets: ResolvedVc<ExpandedOutputAssets>,
1200    output_dir: FileSystemPath,
1201) -> Result<()> {
1202    emit_assets_into_dir(*assets, output_dir)
1203        .as_side_effect()
1204        .await?;
1205    Ok(())
1206}
1207
1208/// Replaces the externals in the result with `ExternalModuleAsset` instances.
1209pub async fn replace_external(
1210    name: &RcStr,
1211    ty: ExternalType,
1212    target: Option<FileSystemPath>,
1213    import_externals: bool,
1214    analyze_mode: CachedExternalTracingMode,
1215) -> Result<Option<ModuleResolveResultItem>> {
1216    let external_type = match ty {
1217        ExternalType::CommonJs => CachedExternalType::CommonJs,
1218        ExternalType::EcmaScriptModule => {
1219            if import_externals {
1220                CachedExternalType::EcmaScriptViaImport
1221            } else {
1222                CachedExternalType::EcmaScriptViaRequire
1223            }
1224        }
1225        ExternalType::Global => CachedExternalType::Global,
1226        ExternalType::Script => CachedExternalType::Script,
1227        ExternalType::Url => {
1228            // we don't want to wrap url externals.
1229            return Ok(None);
1230        }
1231    };
1232
1233    let module = CachedExternalModule::new(name.clone(), target, external_type, analyze_mode)
1234        .to_resolved()
1235        .await?;
1236
1237    Ok(Some(ModuleResolveResultItem::Module(ResolvedVc::upcast(
1238        module,
1239    ))))
1240}