turbopack/
lib.rs

1#![feature(box_patterns)]
2#![feature(trivial_bounds)]
3#![feature(min_specialization)]
4#![feature(map_try_insert)]
5#![feature(hash_set_entry)]
6#![recursion_limit = "256"]
7#![feature(arbitrary_self_types)]
8#![feature(arbitrary_self_types_pointers)]
9
10pub mod evaluate_context;
11pub mod global_module_ids;
12mod graph;
13pub mod module_options;
14pub mod transition;
15
16use anyhow::{Result, bail};
17use css::{CssModuleAsset, ModuleCssAsset};
18use ecmascript::{
19    EcmascriptModuleAsset, EcmascriptModuleAssetType, TreeShakingMode,
20    chunk::EcmascriptChunkPlaceable,
21    references::{FollowExportsResult, follow_reexports},
22    side_effect_optimization::facade::module::EcmascriptModuleFacadeModule,
23};
24use graph::{AggregatedGraph, AggregatedGraphNodeContent, aggregate};
25use module_options::{ModuleOptions, ModuleOptionsContext, ModuleRuleEffect, ModuleType};
26use tracing::{Instrument, field::Empty};
27use turbo_rcstr::{RcStr, rcstr};
28use turbo_tasks::{ResolvedVc, ValueToString, Vc};
29use turbo_tasks_fs::{
30    FileSystemPath,
31    glob::{Glob, GlobOptions},
32};
33pub use turbopack_core::condition;
34use turbopack_core::{
35    asset::Asset,
36    chunk::SourceMapsType,
37    compile_time_info::CompileTimeInfo,
38    context::{AssetContext, ProcessResult},
39    ident::Layer,
40    issue::{IssueExt, IssueSource, StyledString, module::ModuleIssue},
41    module::Module,
42    node_addon_module::NodeAddonModule,
43    output::OutputAsset,
44    raw_module::RawModule,
45    reference_type::{
46        CssReferenceSubType, EcmaScriptModulesReferenceSubType, ImportContext, ImportWithType,
47        InnerAssets, ReferenceType,
48    },
49    resolve::{
50        ExternalTraced, ExternalType, ModulePart, ModuleResolveResult, ModuleResolveResultItem,
51        ResolveResult, ResolveResultItem, options::ResolveOptions, origin::PlainResolveOrigin,
52        parse::Request, resolve,
53    },
54    source::Source,
55};
56pub use turbopack_css as css;
57pub use turbopack_ecmascript as ecmascript;
58use turbopack_ecmascript::{
59    references::external_module::{
60        CachedExternalModule, CachedExternalTracingMode, CachedExternalType,
61    },
62    side_effect_optimization::locals::module::EcmascriptModuleLocalsModule,
63    tree_shake::asset::EcmascriptModulePartAsset,
64};
65use turbopack_json::JsonModuleAsset;
66pub use turbopack_resolve::{resolve::resolve_options, resolve_options_context};
67use turbopack_resolve::{resolve_options_context::ResolveOptionsContext, typescript::type_resolve};
68use turbopack_static::{css::StaticUrlCssModule, ecma::StaticUrlJsModule};
69use turbopack_wasm::{module_asset::WebAssemblyModuleAsset, source::WebAssemblySource};
70
71use self::transition::{Transition, TransitionOptions};
72use crate::module_options::{CssOptionsContext, CustomModuleType, EcmascriptOptionsContext};
73
74#[turbo_tasks::function]
75async fn apply_module_type(
76    source: ResolvedVc<Box<dyn Source>>,
77    module_asset_context: Vc<ModuleAssetContext>,
78    module_type: Vc<ModuleType>,
79    part: Option<ModulePart>,
80    inner_assets: Option<ResolvedVc<InnerAssets>>,
81    css_import_context: Option<Vc<ImportContext>>,
82    runtime_code: bool,
83) -> Result<Vc<ProcessResult>> {
84    let module_type = &*module_type.await?;
85    Ok(ProcessResult::Module(match module_type {
86        ModuleType::Ecmascript {
87            preprocess,
88            main,
89            postprocess,
90            options,
91        }
92        | ModuleType::Typescript {
93            preprocess,
94            main,
95            postprocess,
96            tsx: _,
97            analyze_types: _,
98            options,
99        }
100        | ModuleType::TypescriptDeclaration {
101            preprocess,
102            main,
103            postprocess,
104            options,
105        } => {
106            let context_for_module = match module_type {
107                ModuleType::Typescript { analyze_types, .. } if *analyze_types => {
108                    module_asset_context.with_types_resolving_enabled()
109                }
110                ModuleType::TypescriptDeclaration { .. } => {
111                    module_asset_context.with_types_resolving_enabled()
112                }
113                _ => module_asset_context,
114            }
115            .to_resolved()
116            .await?;
117            let mut builder = EcmascriptModuleAsset::builder(
118                source,
119                ResolvedVc::upcast(context_for_module),
120                preprocess
121                    .extend(**main)
122                    .extend(**postprocess)
123                    .to_resolved()
124                    .await?,
125                *options,
126                module_asset_context
127                    .compile_time_info()
128                    .to_resolved()
129                    .await?,
130            );
131            match module_type {
132                ModuleType::Ecmascript { .. } => {
133                    builder = builder.with_type(EcmascriptModuleAssetType::Ecmascript)
134                }
135                ModuleType::Typescript {
136                    tsx, analyze_types, ..
137                } => {
138                    builder = builder.with_type(EcmascriptModuleAssetType::Typescript {
139                        tsx: *tsx,
140                        analyze_types: *analyze_types,
141                    })
142                }
143                ModuleType::TypescriptDeclaration { .. } => {
144                    builder = builder.with_type(EcmascriptModuleAssetType::TypescriptDeclaration)
145                }
146                _ => unreachable!(),
147            }
148
149            if let Some(inner_assets) = inner_assets {
150                builder = builder.with_inner_assets(inner_assets);
151            }
152
153            let module = builder.build().to_resolved().await?;
154            if runtime_code {
155                ResolvedVc::upcast(module)
156            } else {
157                if matches!(&part, Some(ModulePart::Evaluation)) {
158                    // Skip the evaluation part if the module is marked as side effect free.
159                    let side_effect_free_packages = module_asset_context
160                        .side_effect_free_packages()
161                        .resolve()
162                        .await?;
163
164                    if *module
165                        .is_marked_as_side_effect_free(side_effect_free_packages)
166                        .await?
167                    {
168                        return Ok(ProcessResult::Ignore.cell());
169                    }
170                }
171
172                let options_value = options.await?;
173                match options_value.tree_shaking_mode {
174                    Some(TreeShakingMode::ModuleFragments) => {
175                        Vc::upcast(EcmascriptModulePartAsset::select_part(
176                            *module,
177                            part.unwrap_or(ModulePart::facade()),
178                        ))
179                    }
180                    Some(TreeShakingMode::ReexportsOnly) => {
181                        if let Some(part) = part {
182                            match part {
183                                ModulePart::Evaluation => {
184                                    if *module.get_exports().split_locals_and_reexports().await? {
185                                        Vc::upcast(EcmascriptModuleLocalsModule::new(*module))
186                                    } else {
187                                        Vc::upcast(*module)
188                                    }
189                                }
190                                ModulePart::Export(_) => {
191                                    let side_effect_free_packages = module_asset_context
192                                        .side_effect_free_packages()
193                                        .resolve()
194                                        .await?;
195
196                                    if *module.get_exports().split_locals_and_reexports().await? {
197                                        apply_reexport_tree_shaking(
198                                            Vc::upcast(
199                                                EcmascriptModuleFacadeModule::new(
200                                                    Vc::upcast(*module),
201                                                    ModulePart::facade(),
202                                                )
203                                                .resolve()
204                                                .await?,
205                                            ),
206                                            part,
207                                            side_effect_free_packages,
208                                        )
209                                    } else {
210                                        apply_reexport_tree_shaking(
211                                            Vc::upcast(*module),
212                                            part,
213                                            side_effect_free_packages,
214                                        )
215                                    }
216                                }
217                                _ => bail!(
218                                    "Invalid module part \"{}\" for reexports only tree shaking \
219                                     mode",
220                                    part
221                                ),
222                            }
223                        } else if *module.get_exports().split_locals_and_reexports().await? {
224                            Vc::upcast(EcmascriptModuleFacadeModule::new(
225                                Vc::upcast(*module),
226                                ModulePart::facade(),
227                            ))
228                        } else {
229                            Vc::upcast(*module)
230                        }
231                    }
232                    None => Vc::upcast(*module),
233                }
234                .to_resolved()
235                .await?
236            }
237        }
238        ModuleType::Json => ResolvedVc::upcast(JsonModuleAsset::new(*source).to_resolved().await?),
239        ModuleType::Raw => ResolvedVc::upcast(RawModule::new(*source).to_resolved().await?),
240        ModuleType::NodeAddon => {
241            ResolvedVc::upcast(NodeAddonModule::new(*source).to_resolved().await?)
242        }
243        ModuleType::CssModule => ResolvedVc::upcast(
244            ModuleCssAsset::new(*source, Vc::upcast(module_asset_context))
245                .to_resolved()
246                .await?,
247        ),
248
249        ModuleType::Css { ty, environment } => ResolvedVc::upcast(
250            CssModuleAsset::new(
251                *source,
252                Vc::upcast(module_asset_context),
253                *ty,
254                css_import_context,
255                environment.as_deref().copied(),
256            )
257            .to_resolved()
258            .await?,
259        ),
260        ModuleType::StaticUrlJs => {
261            ResolvedVc::upcast(StaticUrlJsModule::new(*source).to_resolved().await?)
262        }
263        ModuleType::StaticUrlCss => {
264            ResolvedVc::upcast(StaticUrlCssModule::new(*source).to_resolved().await?)
265        }
266        ModuleType::WebAssembly { source_ty } => ResolvedVc::upcast(
267            WebAssemblyModuleAsset::new(
268                WebAssemblySource::new(*source, *source_ty),
269                Vc::upcast(module_asset_context),
270            )
271            .to_resolved()
272            .await?,
273        ),
274        ModuleType::Custom(custom) => {
275            custom
276                .create_module(*source, module_asset_context, part)
277                .to_resolved()
278                .await?
279        }
280    })
281    .cell())
282}
283
284#[turbo_tasks::function]
285async fn apply_reexport_tree_shaking(
286    module: Vc<Box<dyn EcmascriptChunkPlaceable>>,
287    part: ModulePart,
288    side_effect_free_packages: Vc<Glob>,
289) -> Result<Vc<Box<dyn Module>>> {
290    if let ModulePart::Export(export) = &part {
291        let FollowExportsResult {
292            module: final_module,
293            export_name: new_export,
294            ..
295        } = &*follow_reexports(module, export.clone(), side_effect_free_packages, true).await?;
296        let module = if let Some(new_export) = new_export {
297            if *new_export == *export {
298                Vc::upcast(**final_module)
299            } else {
300                Vc::upcast(EcmascriptModuleFacadeModule::new(
301                    **final_module,
302                    ModulePart::renamed_export(new_export.clone(), export.clone()),
303                ))
304            }
305        } else {
306            Vc::upcast(EcmascriptModuleFacadeModule::new(
307                **final_module,
308                ModulePart::renamed_namespace(export.clone()),
309            ))
310        };
311        return Ok(module);
312    }
313    Ok(Vc::upcast(module))
314}
315
316#[turbo_tasks::value]
317#[derive(Debug)]
318pub struct ModuleAssetContext {
319    pub transitions: ResolvedVc<TransitionOptions>,
320    pub compile_time_info: ResolvedVc<CompileTimeInfo>,
321    pub module_options_context: ResolvedVc<ModuleOptionsContext>,
322    pub resolve_options_context: ResolvedVc<ResolveOptionsContext>,
323    pub layer: Layer,
324    transition: Option<ResolvedVc<Box<dyn Transition>>>,
325    /// Whether to replace external resolutions with CachedExternalModules. Used with
326    /// ModuleOptionsContext.enable_externals_tracing to handle transitive external dependencies.
327    replace_externals: bool,
328}
329
330#[turbo_tasks::value_impl]
331impl ModuleAssetContext {
332    #[turbo_tasks::function]
333    pub fn new(
334        transitions: ResolvedVc<TransitionOptions>,
335        compile_time_info: ResolvedVc<CompileTimeInfo>,
336        module_options_context: ResolvedVc<ModuleOptionsContext>,
337        resolve_options_context: ResolvedVc<ResolveOptionsContext>,
338        layer: Layer,
339    ) -> Vc<Self> {
340        Self::cell(ModuleAssetContext {
341            transitions,
342            compile_time_info,
343            module_options_context,
344            resolve_options_context,
345            transition: None,
346            layer,
347            replace_externals: true,
348        })
349    }
350
351    #[turbo_tasks::function]
352    pub fn new_transition(
353        transitions: ResolvedVc<TransitionOptions>,
354        compile_time_info: ResolvedVc<CompileTimeInfo>,
355        module_options_context: ResolvedVc<ModuleOptionsContext>,
356        resolve_options_context: ResolvedVc<ResolveOptionsContext>,
357        layer: Layer,
358        transition: ResolvedVc<Box<dyn Transition>>,
359    ) -> Vc<Self> {
360        Self::cell(ModuleAssetContext {
361            transitions,
362            compile_time_info,
363            module_options_context,
364            resolve_options_context,
365            layer,
366            transition: Some(transition),
367            replace_externals: true,
368        })
369    }
370
371    #[turbo_tasks::function]
372    fn new_without_replace_externals(
373        transitions: ResolvedVc<TransitionOptions>,
374        compile_time_info: ResolvedVc<CompileTimeInfo>,
375        module_options_context: ResolvedVc<ModuleOptionsContext>,
376        resolve_options_context: ResolvedVc<ResolveOptionsContext>,
377        layer: Layer,
378    ) -> Vc<Self> {
379        Self::cell(ModuleAssetContext {
380            transitions,
381            compile_time_info,
382            module_options_context,
383            resolve_options_context,
384            transition: None,
385            layer,
386            replace_externals: false,
387        })
388    }
389
390    #[turbo_tasks::function]
391    pub fn module_options_context(&self) -> Vc<ModuleOptionsContext> {
392        *self.module_options_context
393    }
394
395    #[turbo_tasks::function]
396    pub fn resolve_options_context(&self) -> Vc<ResolveOptionsContext> {
397        *self.resolve_options_context
398    }
399
400    #[turbo_tasks::function]
401    pub async fn is_types_resolving_enabled(&self) -> Result<Vc<bool>> {
402        let resolve_options_context = self.resolve_options_context.await?;
403        Ok(Vc::cell(
404            resolve_options_context.enable_types && resolve_options_context.enable_typescript,
405        ))
406    }
407
408    #[turbo_tasks::function]
409    pub async fn with_types_resolving_enabled(self: Vc<Self>) -> Result<Vc<ModuleAssetContext>> {
410        if *self.is_types_resolving_enabled().await? {
411            return Ok(self);
412        }
413        let this = self.await?;
414        let resolve_options_context = this
415            .resolve_options_context
416            .with_types_enabled()
417            .resolve()
418            .await?;
419
420        Ok(ModuleAssetContext::new(
421            *this.transitions,
422            *this.compile_time_info,
423            *this.module_options_context,
424            resolve_options_context,
425            this.layer.clone(),
426        ))
427    }
428}
429
430impl ModuleAssetContext {
431    async fn process_with_transition_rules(
432        self: Vc<Self>,
433        source: ResolvedVc<Box<dyn Source>>,
434        reference_type: ReferenceType,
435    ) -> Result<Vc<ProcessResult>> {
436        let this = self.await?;
437        Ok(
438            if let Some(transition) = this
439                .transitions
440                .await?
441                .get_by_rules(source, &reference_type)
442                .await?
443            {
444                transition.process(*source, self, reference_type)
445            } else {
446                self.process_default(source, reference_type).await?
447            },
448        )
449    }
450
451    async fn process_default(
452        self: Vc<Self>,
453        source: ResolvedVc<Box<dyn Source>>,
454        reference_type: ReferenceType,
455    ) -> Result<Vc<ProcessResult>> {
456        process_default(self, source, reference_type, Vec::new()).await
457    }
458}
459
460async fn process_default(
461    module_asset_context: Vc<ModuleAssetContext>,
462    source: ResolvedVc<Box<dyn Source>>,
463    reference_type: ReferenceType,
464    processed_rules: Vec<usize>,
465) -> Result<Vc<ProcessResult>> {
466    let span = tracing::info_span!(
467        "process module",
468        name = %source.ident().to_string().await?,
469        layer = Empty,
470        reference_type = display(&reference_type)
471    );
472    if !span.is_disabled() {
473        // Need to use record, otherwise future is not Send for some reason.
474        let module_asset_context_ref = module_asset_context.await?;
475        span.record("layer", module_asset_context_ref.layer.name().as_str());
476    }
477    process_default_internal(
478        module_asset_context,
479        source,
480        reference_type,
481        processed_rules,
482    )
483    .instrument(span)
484    .await
485}
486
487async fn process_default_internal(
488    module_asset_context: Vc<ModuleAssetContext>,
489    source: ResolvedVc<Box<dyn Source>>,
490    reference_type: ReferenceType,
491    processed_rules: Vec<usize>,
492) -> Result<Vc<ProcessResult>> {
493    let ident = source.ident().resolve().await?;
494    let path_ref = ident.path().await?;
495    let options = ModuleOptions::new(
496        ident.path().await?.parent(),
497        module_asset_context.module_options_context(),
498        module_asset_context.resolve_options_context(),
499    );
500
501    let part: Option<ModulePart> = match &reference_type {
502        ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::ImportPart(part)) => {
503            Some(part.clone())
504        }
505        _ => None,
506    };
507    let inner_assets = match &reference_type {
508        ReferenceType::Internal(inner_assets) => Some(*inner_assets),
509        _ => None,
510    };
511
512    let mut has_type_attribute = false;
513
514    let mut current_source = source;
515    let mut current_module_type = match &reference_type {
516        ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::ImportWithType(ty)) => {
517            has_type_attribute = true;
518
519            match ty {
520                ImportWithType::Json => Some(ModuleType::Json),
521            }
522        }
523        _ => None,
524    };
525
526    for (i, rule) in options.await?.rules.iter().enumerate() {
527        if has_type_attribute && current_module_type.is_some() {
528            continue;
529        }
530        if processed_rules.contains(&i) {
531            continue;
532        }
533        if rule.matches(source, &path_ref, &reference_type).await? {
534            for effect in rule.effects() {
535                match effect {
536                    ModuleRuleEffect::Ignore => {
537                        return Ok(ProcessResult::Ignore.cell());
538                    }
539                    ModuleRuleEffect::SourceTransforms(transforms) => {
540                        current_source =
541                            transforms.transform(*current_source).to_resolved().await?;
542                        if current_source.ident().resolve().await? != ident {
543                            // The ident has been changed, so we need to apply new rules.
544                            if let Some(transition) = module_asset_context
545                                .await?
546                                .transitions
547                                .await?
548                                .get_by_rules(current_source, &reference_type)
549                                .await?
550                            {
551                                return Ok(transition.process(
552                                    *current_source,
553                                    module_asset_context,
554                                    reference_type,
555                                ));
556                            } else {
557                                let mut processed_rules = processed_rules.clone();
558                                processed_rules.push(i);
559                                return Box::pin(process_default(
560                                    module_asset_context,
561                                    current_source,
562                                    reference_type,
563                                    processed_rules,
564                                ))
565                                .await;
566                            }
567                        }
568                    }
569                    ModuleRuleEffect::ModuleType(module) => {
570                        current_module_type = Some(module.clone());
571                    }
572                    ModuleRuleEffect::ExtendEcmascriptTransforms {
573                        preprocess: extend_preprocess,
574                        main: extend_main,
575                        postprocess: extend_postprocess,
576                    } => {
577                        current_module_type = match current_module_type {
578                            Some(ModuleType::Ecmascript {
579                                preprocess,
580                                main,
581                                postprocess,
582                                options,
583                            }) => Some(ModuleType::Ecmascript {
584                                preprocess: extend_preprocess
585                                    .extend(*preprocess)
586                                    .to_resolved()
587                                    .await?,
588                                main: extend_main.extend(*main).to_resolved().await?,
589                                postprocess: postprocess
590                                    .extend(**extend_postprocess)
591                                    .to_resolved()
592                                    .await?,
593                                options,
594                            }),
595                            Some(ModuleType::Typescript {
596                                preprocess,
597                                main,
598                                postprocess,
599                                tsx,
600                                analyze_types,
601                                options,
602                            }) => Some(ModuleType::Typescript {
603                                preprocess: extend_preprocess
604                                    .extend(*preprocess)
605                                    .to_resolved()
606                                    .await?,
607                                main: extend_main.extend(*main).to_resolved().await?,
608                                postprocess: postprocess
609                                    .extend(**extend_postprocess)
610                                    .to_resolved()
611                                    .await?,
612                                tsx,
613                                analyze_types,
614                                options,
615                            }),
616                            Some(module_type) => {
617                                ModuleIssue {
618                                    ident: ident.to_resolved().await?,
619                                    title: StyledString::Text(rcstr!("Invalid module type"))
620                                        .resolved_cell(),
621                                    description: StyledString::Text(rcstr!(
622                                        "The module type must be Ecmascript or Typescript to add \
623                                         Ecmascript transforms"
624                                    ))
625                                    .resolved_cell(),
626                                    source: Some(IssueSource::from_source_only(current_source)),
627                                }
628                                .resolved_cell()
629                                .emit();
630                                Some(module_type)
631                            }
632                            None => {
633                                ModuleIssue {
634                                    ident: ident.to_resolved().await?,
635                                    title: StyledString::Text(rcstr!("Missing module type"))
636                                        .resolved_cell(),
637                                    description: StyledString::Text(rcstr!(
638                                        "The module type effect must be applied before adding \
639                                         Ecmascript transforms"
640                                    ))
641                                    .resolved_cell(),
642                                    source: Some(IssueSource::from_source_only(current_source)),
643                                }
644                                .resolved_cell()
645                                .emit();
646                                None
647                            }
648                        };
649                    }
650                }
651            }
652        }
653    }
654
655    let Some(module_type) = current_module_type else {
656        return Ok(ProcessResult::Unknown(current_source).cell());
657    };
658
659    Ok(apply_module_type(
660        *current_source,
661        module_asset_context,
662        module_type.cell(),
663        part,
664        inner_assets.map(|v| *v),
665        if let ReferenceType::Css(CssReferenceSubType::AtImport(import)) = reference_type {
666            import.map(|v| *v)
667        } else {
668            None
669        },
670        matches!(reference_type, ReferenceType::Runtime),
671    ))
672}
673
674#[turbo_tasks::function]
675async fn externals_tracing_module_context(
676    ty: ExternalType,
677    compile_time_info: Vc<CompileTimeInfo>,
678) -> Result<Vc<ModuleAssetContext>> {
679    let resolve_options = ResolveOptionsContext {
680        enable_node_native_modules: true,
681        emulate_environment: Some(compile_time_info.await?.environment),
682        loose_errors: true,
683        custom_conditions: match ty {
684            ExternalType::CommonJs => vec![rcstr!("require"), rcstr!("node")],
685            ExternalType::EcmaScriptModule => vec![rcstr!("import"), rcstr!("node")],
686            ExternalType::Url | ExternalType::Global | ExternalType::Script => vec![rcstr!("node")],
687        },
688        ..Default::default()
689    };
690
691    Ok(ModuleAssetContext::new_without_replace_externals(
692        Default::default(),
693        compile_time_info,
694        // Keep these options more or less in sync with
695        // turbopack/crates/turbopack/tests/node-file-trace.rs to ensure that the NFT unit tests
696        // are actually representative of what Turbopack does.
697        ModuleOptionsContext {
698            ecmascript: EcmascriptOptionsContext {
699                // enable_types should not be enabled here. It gets set automatically when a TS file
700                // is encountered.
701                source_maps: SourceMapsType::None,
702                ..Default::default()
703            },
704            css: CssOptionsContext {
705                source_maps: SourceMapsType::None,
706                enable_raw_css: true,
707                ..Default::default()
708            },
709            // Environment is not passed in order to avoid downleveling JS / CSS for
710            // node-file-trace.
711            environment: None,
712            is_tracing: true,
713            ..Default::default()
714        }
715        .cell(),
716        resolve_options.cell(),
717        Layer::new(rcstr!("externals-tracing")),
718    ))
719}
720
721#[turbo_tasks::value_impl]
722impl AssetContext for ModuleAssetContext {
723    #[turbo_tasks::function]
724    fn compile_time_info(&self) -> Vc<CompileTimeInfo> {
725        *self.compile_time_info
726    }
727
728    fn layer(&self) -> Layer {
729        self.layer.clone()
730    }
731
732    #[turbo_tasks::function]
733    async fn resolve_options(
734        self: Vc<Self>,
735        origin_path: FileSystemPath,
736        _reference_type: ReferenceType,
737    ) -> Result<Vc<ResolveOptions>> {
738        let this = self.await?;
739        let module_asset_context = if let Some(transition) = this.transition {
740            transition.process_context(self)
741        } else {
742            self
743        };
744        // TODO move `apply_commonjs/esm_resolve_options` etc. to here
745        Ok(resolve_options(
746            origin_path.parent(),
747            *module_asset_context.await?.resolve_options_context,
748        ))
749    }
750
751    #[turbo_tasks::function]
752    async fn resolve_asset(
753        self: Vc<Self>,
754        origin_path: FileSystemPath,
755        request: Vc<Request>,
756        resolve_options: Vc<ResolveOptions>,
757        reference_type: ReferenceType,
758    ) -> Result<Vc<ModuleResolveResult>> {
759        let context_path = origin_path.parent();
760
761        let result = resolve(
762            context_path,
763            reference_type.clone(),
764            request,
765            resolve_options,
766        );
767
768        let mut result = self.process_resolve_result(result.resolve().await?, reference_type);
769
770        if *self.is_types_resolving_enabled().await? {
771            let types_result = type_resolve(
772                Vc::upcast(PlainResolveOrigin::new(Vc::upcast(self), origin_path)),
773                request,
774            );
775
776            result = ModuleResolveResult::alternatives(vec![result, types_result]);
777        }
778
779        Ok(result)
780    }
781
782    #[turbo_tasks::function]
783    async fn process_resolve_result(
784        self: Vc<Self>,
785        result: Vc<ResolveResult>,
786        reference_type: ReferenceType,
787    ) -> Result<Vc<ModuleResolveResult>> {
788        let this = self.await?;
789
790        let replace_externals = this.replace_externals;
791        let import_externals = this
792            .module_options_context
793            .await?
794            .ecmascript
795            .import_externals;
796
797        let result = result.await?;
798
799        let result = result
800            .map_primary_items(|item| {
801                let reference_type = reference_type.clone();
802                async move {
803                    Ok(match item {
804                        ResolveResultItem::Source(source) => {
805                            match &*self.process(*source, reference_type).await? {
806                                ProcessResult::Module(module) => {
807                                    ModuleResolveResultItem::Module(*module)
808                                }
809                                ProcessResult::Unknown(source) => {
810                                    ModuleResolveResultItem::Unknown(*source)
811                                }
812                                ProcessResult::Ignore => ModuleResolveResultItem::Ignore,
813                            }
814                        }
815                        ResolveResultItem::External { name, ty, traced } => {
816                            let replacement = if replace_externals {
817                                let tracing_mode = if traced == ExternalTraced::Traced
818                                    && let Some(options) = &self
819                                        .module_options_context()
820                                        .await?
821                                        .enable_externals_tracing
822                                {
823                                    // result.affecting_sources can be ignored for tracing, as this
824                                    // request will later be resolved relative to tracing_root
825                                    // anyway.
826
827                                    let options = options.await?;
828                                    CachedExternalTracingMode::Traced {
829                                        externals_context: ResolvedVc::upcast(
830                                            externals_tracing_module_context(
831                                                ty,
832                                                *options.compile_time_info,
833                                            )
834                                            .to_resolved()
835                                            .await?,
836                                        ),
837                                        root_origin: options.tracing_root.join("_")?,
838                                    }
839                                } else {
840                                    CachedExternalTracingMode::Untraced
841                                };
842
843                                replace_external(&name, ty, import_externals, tracing_mode).await?
844                            } else {
845                                None
846                            };
847
848                            replacement
849                                .unwrap_or_else(|| ModuleResolveResultItem::External { name, ty })
850                        }
851                        ResolveResultItem::Ignore => ModuleResolveResultItem::Ignore,
852                        ResolveResultItem::Empty => ModuleResolveResultItem::Empty,
853                        ResolveResultItem::Error(e) => ModuleResolveResultItem::Error(e),
854                        ResolveResultItem::Custom(u8) => ModuleResolveResultItem::Custom(u8),
855                    })
856                }
857            })
858            .await?;
859
860        Ok(result.cell())
861    }
862
863    #[turbo_tasks::function]
864    async fn process(
865        self: Vc<Self>,
866        asset: ResolvedVc<Box<dyn Source>>,
867        reference_type: ReferenceType,
868    ) -> Result<Vc<ProcessResult>> {
869        let this = self.await?;
870        if let Some(transition) = this.transition {
871            Ok(transition.process(*asset, self, reference_type))
872        } else {
873            Ok(self
874                .process_with_transition_rules(asset, reference_type)
875                .await?)
876        }
877    }
878
879    #[turbo_tasks::function]
880    async fn with_transition(&self, transition: RcStr) -> Result<Vc<Box<dyn AssetContext>>> {
881        Ok(
882            if let Some(transition) = self.transitions.await?.get_named(transition) {
883                Vc::upcast(ModuleAssetContext::new_transition(
884                    *self.transitions,
885                    *self.compile_time_info,
886                    *self.module_options_context,
887                    *self.resolve_options_context,
888                    self.layer.clone(),
889                    *transition,
890                ))
891            } else {
892                // TODO report issue
893                Vc::upcast(ModuleAssetContext::new(
894                    *self.transitions,
895                    *self.compile_time_info,
896                    *self.module_options_context,
897                    *self.resolve_options_context,
898                    self.layer.clone(),
899                ))
900            },
901        )
902    }
903
904    #[turbo_tasks::function]
905    async fn side_effect_free_packages(&self) -> Result<Vc<Glob>> {
906        let pkgs = &self.module_options_context.await?.side_effect_free_packages;
907
908        let mut globs = String::new();
909        globs.push_str("**/node_modules/{");
910        globs.push_str(&pkgs.join(","));
911        globs.push_str("}/**");
912
913        Ok(Glob::new(globs.into(), GlobOptions::default()))
914    }
915}
916
917#[turbo_tasks::function]
918pub async fn emit_with_completion(
919    asset: Vc<Box<dyn OutputAsset>>,
920    output_dir: FileSystemPath,
921) -> Result<()> {
922    emit_assets_aggregated(asset, output_dir)
923        .as_side_effect()
924        .await
925}
926
927#[turbo_tasks::function(operation)]
928pub fn emit_with_completion_operation(
929    asset: ResolvedVc<Box<dyn OutputAsset>>,
930    output_dir: FileSystemPath,
931) -> Vc<()> {
932    emit_with_completion(*asset, output_dir)
933}
934
935#[turbo_tasks::function]
936async fn emit_assets_aggregated(
937    asset: Vc<Box<dyn OutputAsset>>,
938    output_dir: FileSystemPath,
939) -> Result<()> {
940    let aggregated = aggregate(asset);
941    emit_aggregated_assets(aggregated, output_dir)
942        .as_side_effect()
943        .await
944}
945
946#[turbo_tasks::function]
947async fn emit_aggregated_assets(
948    aggregated: Vc<AggregatedGraph>,
949    output_dir: FileSystemPath,
950) -> Result<()> {
951    match &*aggregated.content().await? {
952        AggregatedGraphNodeContent::Asset(asset) => {
953            emit_asset_into_dir(**asset, output_dir)
954                .as_side_effect()
955                .await?;
956        }
957        AggregatedGraphNodeContent::Children(children) => {
958            for aggregated in children {
959                emit_aggregated_assets(**aggregated, output_dir.clone())
960                    .as_side_effect()
961                    .await?;
962            }
963        }
964    }
965    Ok(())
966}
967
968#[turbo_tasks::function]
969pub async fn emit_asset(asset: Vc<Box<dyn OutputAsset>>) -> Result<()> {
970    asset
971        .content()
972        .write(asset.path().owned().await?)
973        .as_side_effect()
974        .await?;
975
976    Ok(())
977}
978
979#[turbo_tasks::function]
980pub async fn emit_asset_into_dir(
981    asset: Vc<Box<dyn OutputAsset>>,
982    output_dir: FileSystemPath,
983) -> Result<()> {
984    let dir = output_dir.clone();
985    if asset.path().await?.is_inside_ref(&dir) {
986        emit_asset(asset).as_side_effect().await?;
987    }
988    Ok(())
989}
990
991/// Replaces the externals in the result with `ExternalModuleAsset` instances.
992pub async fn replace_external(
993    name: &RcStr,
994    ty: ExternalType,
995    import_externals: bool,
996    tracing_mode: CachedExternalTracingMode,
997) -> Result<Option<ModuleResolveResultItem>> {
998    let external_type = match ty {
999        ExternalType::CommonJs => CachedExternalType::CommonJs,
1000        ExternalType::EcmaScriptModule => {
1001            if import_externals {
1002                CachedExternalType::EcmaScriptViaImport
1003            } else {
1004                CachedExternalType::EcmaScriptViaRequire
1005            }
1006        }
1007        ExternalType::Global => CachedExternalType::Global,
1008        ExternalType::Script => CachedExternalType::Script,
1009        ExternalType::Url => {
1010            // we don't want to wrap url externals.
1011            return Ok(None);
1012        }
1013    };
1014
1015    let module = CachedExternalModule::new(name.clone(), external_type, tracing_mode)
1016        .to_resolved()
1017        .await?;
1018
1019    Ok(Some(ModuleResolveResultItem::Module(ResolvedVc::upcast(
1020        module,
1021    ))))
1022}
1023
1024pub fn register() {
1025    turbo_tasks::register();
1026    turbo_tasks_fs::register();
1027    turbopack_core::register();
1028    turbopack_css::register();
1029    turbopack_ecmascript::register();
1030    turbopack_node::register();
1031    turbopack_env::register();
1032    turbopack_mdx::register();
1033    turbopack_json::register();
1034    turbopack_resolve::register();
1035    turbopack_static::register();
1036    turbopack_wasm::register();
1037    include!(concat!(env!("OUT_DIR"), "/register.rs"));
1038}