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