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    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                // Check side effect free on the intermediate module before following reexports
192                // This can skip the module earlier and could skip more modules than only doing it
193                // at the end. Also we avoid parsing/analyzing the module in this
194                // case, because we would need to parse/analyze it for reexports.
195                if (follow_reexports || module_fragments_enabled) && is_evaluation {
196                    // If we are tree shaking, skip the evaluation part if the module is marked as
197                    // side effect free.
198                    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 we are tree shaking, skip the evaluation part if the module is marked as
301        // side effect free.
302        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    /// Whether to replace external resolutions with CachedExternalModules. Used with
350    /// ModuleOptionsContext.enable_externals_tracing to handle transitive external dependencies.
351    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    /// Doesn't replace external resolve results with a CachedExternalModule.
396    #[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        // You can't await multiple times in the span macro call parameters.
495        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
508/// Apply collected transforms to a module type.
509/// For Ecmascript/Typescript variants: merge collected transforms into the module type.
510/// For Custom: call extend_ecmascript_transforms() if any transforms exist.
511/// For non-ecmascript types: warn if transforms exist, return unchanged.
512async 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 no transforms were collected, return early
525    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            // Apply collected preprocess/main in order, then module type's transforms
555            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            // Apply module type's postprocess first, then collected postprocess
570            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                // Combine collected transforms into single Vcs
579                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    // Handle turbopackLoader import attributes: apply inline loader as source transform
669    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        // Determine the import map for loader-runner
701        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 turbopackModuleType is specified, skip rule matching and directly
755        // apply the requested module type with empty transforms (loader output
756        // is already processed).
757        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                    // Fall through to re-process with new ident
787                }
788                _ => bail!("Unexpected module rule effect for turbopackModuleType"),
789            }
790        }
791
792        // If the ident changed (e.g., due to rename_as), re-process from the
793        // beginning so the new extension is matched by the correct rules.
794        // Use a plain Import reference type to avoid re-applying turbopackUse
795        // loaders in the recursive call (which would cause an infinite loop).
796        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    // Collect transforms from ExtendEcmascriptTransforms effects.
824    // They will be applied when ModuleType is set.
825    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                            // The ident has been changed, so we need to apply new rules.
847                            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                        // Apply any collected transforms to this module type and exit rule
874                        // processing. Once a ModuleType is determined, we
875                        // stop processing further rules.
876                        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                        // Collect transforms. They will be applied when ModuleType is set.
895                        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/// `prune` skips matching files as the graph is walked, rather than filtering them out of the
921/// result afterwards.
922#[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        // This config should be kept in sync with
958        // turbopack/crates/turbopack-tracing/tests/node-file-trace.rs and
959        // turbopack/crates/turbopack-tracing/tests/unit.rs and
960        // turbopack/crates/turbopack/src/lib.rs and
961        // turbopack/crates/turbopack-nft/src/nft.rs
962        ModuleOptionsContext {
963            ecmascript: EcmascriptOptionsContext {
964                enable_typescript_transform: Some(
965                    TypescriptTransformOptions::default().resolved_cell(),
966                ),
967                // enable_types should not be enabled here. It gets set automatically when a TS file
968                // is encountered.
969                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 is not passed in order to avoid downleveling JS / CSS for
978            // node-file-trace.
979            environment: None,
980            analyze_mode: AnalyzeMode::Tracing,
981            module_rules: prune_rules,
982            // Disable tree shaking. Even side-effect-free imports need to be traced, as they will
983            // execute at runtime.
984            ..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        // TODO move `apply_commonjs/esm_resolve_options` etc. to here
1015        let options = resolve_options(
1016            origin_path.parent(),
1017            *module_asset_context.await?.resolve_options_context,
1018        );
1019        // Inject the turbopack-ecmascript-runtime import map so that
1020        // @turbopack/* built-in modules and @vercel/turbopack-ecmascript-runtime/*
1021        // paths are always resolvable.
1022        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                                // Determine the package folder, `target` is the full path to the
1100                                // resolved file.
1101                                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                                    // result.affecting_sources can be ignored for tracing, as this
1129                                    // request will later be resolved relative to tracing_root (or
1130                                    // the .next/node_modules/lodash-1238123 symlink) anyway.
1131
1132                                    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                                        // If target is specified, a symlink will be created to
1140                                        // make the folder
1141                                        // itself available, but we still need to trace
1142                                        // resolving the individual file(s) inside the package.
1143                                        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                // TODO report issue
1206                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
1255/// Replaces the externals in the result with `ExternalModuleAsset` instances.
1256pub 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            // we don't want to wrap url externals.
1276            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}