turbopack/module_options/
mod.rs

1pub(crate) mod custom_module_type;
2pub mod match_mode;
3pub mod module_options_context;
4pub mod module_rule;
5pub mod rule_condition;
6pub mod transition_rule;
7
8use anyhow::{Context, Result};
9pub use custom_module_type::CustomModuleType;
10pub use module_options_context::*;
11pub use module_rule::*;
12pub use rule_condition::*;
13use turbo_rcstr::{RcStr, rcstr};
14use turbo_tasks::{ResolvedVc, Vc};
15use turbo_tasks_fs::{FileSystemPath, glob::Glob};
16use turbopack_core::{
17    chunk::SourceMapsType,
18    ident::Layer,
19    reference_type::{CssReferenceSubType, ReferenceType, UrlReferenceSubType},
20    resolve::options::{ImportMap, ImportMapping},
21};
22use turbopack_css::CssModuleAssetType;
23use turbopack_ecmascript::{
24    EcmascriptInputTransform, EcmascriptInputTransforms, EcmascriptOptions, SpecifiedModuleType,
25};
26use turbopack_mdx::MdxTransform;
27use turbopack_node::transforms::{postcss::PostCssTransform, webpack::WebpackLoaders};
28use turbopack_wasm::source::WebAssemblySourceType;
29
30use crate::{
31    evaluate_context::node_evaluate_asset_context, resolve_options_context::ResolveOptionsContext,
32};
33
34#[turbo_tasks::function]
35fn package_import_map_from_import_mapping(
36    package_name: RcStr,
37    package_mapping: ResolvedVc<ImportMapping>,
38) -> Vc<ImportMap> {
39    let mut import_map = ImportMap::default();
40    import_map.insert_exact_alias(format!("@vercel/turbopack/{package_name}"), package_mapping);
41    import_map.cell()
42}
43
44#[turbo_tasks::function]
45fn package_import_map_from_context(
46    package_name: RcStr,
47    context_path: FileSystemPath,
48) -> Vc<ImportMap> {
49    let mut import_map = ImportMap::default();
50    import_map.insert_exact_alias(
51        format!("@vercel/turbopack/{package_name}"),
52        ImportMapping::PrimaryAlternative(package_name, Some(context_path)).resolved_cell(),
53    );
54    import_map.cell()
55}
56
57#[turbo_tasks::value(cell = "new", eq = "manual")]
58pub struct ModuleOptions {
59    pub rules: Vec<ModuleRule>,
60}
61
62#[turbo_tasks::value_impl]
63impl ModuleOptions {
64    #[turbo_tasks::function]
65    pub async fn new(
66        path: FileSystemPath,
67        module_options_context: Vc<ModuleOptionsContext>,
68        resolve_options_context: Vc<ResolveOptionsContext>,
69    ) -> Result<Vc<ModuleOptions>> {
70        let ModuleOptionsContext {
71            css: CssOptionsContext { enable_raw_css, .. },
72            ref enable_postcss_transform,
73            ref enable_webpack_loaders,
74            ref rules,
75            ..
76        } = *module_options_context.await?;
77
78        if !rules.is_empty() {
79            for (condition, new_context) in rules.iter() {
80                if condition.matches(&path) {
81                    return Ok(ModuleOptions::new(
82                        path,
83                        **new_context,
84                        resolve_options_context,
85                    ));
86                }
87            }
88        }
89
90        let need_path = (!enable_raw_css
91            && if let Some(options) = enable_postcss_transform {
92                let options = options.await?;
93                options.postcss_package.is_none()
94            } else {
95                false
96            })
97            || if let Some(options) = enable_webpack_loaders {
98                let options = options.await?;
99                options.loader_runner_package.is_none()
100            } else {
101                false
102            };
103
104        Ok(Self::new_internal(
105            need_path.then_some(path),
106            module_options_context,
107            resolve_options_context,
108        ))
109    }
110
111    #[turbo_tasks::function]
112    async fn new_internal(
113        path: Option<FileSystemPath>,
114        module_options_context: Vc<ModuleOptionsContext>,
115        resolve_options_context: Vc<ResolveOptionsContext>,
116    ) -> Result<Vc<ModuleOptions>> {
117        let ModuleOptionsContext {
118            ecmascript:
119                EcmascriptOptionsContext {
120                    enable_jsx,
121                    enable_types,
122                    ref enable_typescript_transform,
123                    ref enable_decorators,
124                    ignore_dynamic_requests,
125                    import_externals,
126                    esm_url_rewrite_behavior,
127                    ref enable_typeof_window_inlining,
128                    source_maps: ecmascript_source_maps,
129                    ..
130                },
131            enable_mdx,
132            enable_mdx_rs,
133            css:
134                CssOptionsContext {
135                    enable_raw_css,
136                    source_maps: css_source_maps,
137                    ..
138                },
139            ref enable_postcss_transform,
140            ref enable_webpack_loaders,
141            environment,
142            ref module_rules,
143            execution_context,
144            tree_shaking_mode,
145            keep_last_successful_parse,
146            ..
147        } = *module_options_context.await?;
148
149        let mut refresh = false;
150        let mut transforms = vec![];
151
152        // Order of transforms is important. e.g. if the React transform occurs before
153        // Styled JSX, there won't be JSX nodes for Styled JSX to transform.
154        // If a custom plugin requires specific order _before_ core transform kicks in,
155        // should use `before_transform_plugins`.
156        if let Some(enable_jsx) = enable_jsx {
157            let jsx = enable_jsx.await?;
158            refresh = jsx.react_refresh;
159
160            transforms.push(EcmascriptInputTransform::React {
161                development: jsx.development,
162                refresh: jsx.react_refresh,
163                import_source: ResolvedVc::cell(jsx.import_source.clone()),
164                runtime: ResolvedVc::cell(jsx.runtime.clone()),
165            });
166        }
167
168        let ecmascript_options = EcmascriptOptions {
169            tree_shaking_mode,
170            url_rewrite_behavior: esm_url_rewrite_behavior,
171            import_externals,
172            ignore_dynamic_requests,
173            refresh,
174            extract_source_map: matches!(ecmascript_source_maps, SourceMapsType::Full),
175            keep_last_successful_parse,
176            ..Default::default()
177        };
178        let ecmascript_options_vc = ecmascript_options.resolved_cell();
179
180        if let Some(environment) = environment {
181            transforms.push(EcmascriptInputTransform::PresetEnv(environment));
182        }
183
184        if let Some(enable_typeof_window_inlining) = enable_typeof_window_inlining {
185            transforms.push(EcmascriptInputTransform::GlobalTypeofs {
186                window_value: match enable_typeof_window_inlining {
187                    TypeofWindow::Object => "object".to_string(),
188                    TypeofWindow::Undefined => "undefined".to_string(),
189                },
190            });
191        }
192
193        let ts_transform = if let Some(options) = enable_typescript_transform {
194            let options = options.await?;
195            Some(EcmascriptInputTransform::TypeScript {
196                use_define_for_class_fields: options.use_define_for_class_fields,
197            })
198        } else {
199            None
200        };
201
202        let decorators_transform = if let Some(options) = &enable_decorators {
203            let options = options.await?;
204            options
205                .decorators_kind
206                .as_ref()
207                .map(|kind| EcmascriptInputTransform::Decorators {
208                    is_legacy: kind == &DecoratorsKind::Legacy,
209                    is_ecma: kind == &DecoratorsKind::Ecma,
210                    emit_decorators_metadata: options.emit_decorators_metadata,
211                    use_define_for_class_fields: options.use_define_for_class_fields,
212                })
213        } else {
214            None
215        };
216
217        let vendor_transforms = Vc::<EcmascriptInputTransforms>::cell(vec![]);
218        let ts_app_transforms = if let Some(transform) = &ts_transform {
219            let base_transforms = if let Some(decorators_transform) = &decorators_transform {
220                vec![decorators_transform.clone(), transform.clone()]
221            } else {
222                vec![transform.clone()]
223            };
224            Vc::<EcmascriptInputTransforms>::cell(
225                base_transforms
226                    .iter()
227                    .cloned()
228                    .chain(transforms.iter().cloned())
229                    .collect(),
230            )
231        } else {
232            Vc::cell(transforms.clone())
233        };
234
235        // Apply decorators transform for the ModuleType::Ecmascript as well after
236        // constructing ts_app_transforms. Ecmascript can have decorators for
237        // the cases of 1. using jsconfig, to enable ts-specific runtime
238        // decorators (i.e legacy) 2. ecma spec decorators
239        //
240        // Since typescript transform (`ts_app_transforms`) needs to apply decorators
241        // _before_ stripping types, we create ts_app_transforms first in a
242        // specific order with typescript, then apply decorators to app_transforms.
243        let app_transforms = Vc::<EcmascriptInputTransforms>::cell(
244            if let Some(decorators_transform) = &decorators_transform {
245                vec![decorators_transform.clone()]
246            } else {
247                vec![]
248            }
249            .iter()
250            .cloned()
251            .chain(transforms.iter().cloned())
252            .collect(),
253        );
254
255        let mut rules = vec![
256            ModuleRule::new_all(
257                RuleCondition::any(vec![
258                    RuleCondition::ResourcePathEndsWith(".json".to_string()),
259                    RuleCondition::ContentTypeStartsWith("application/json".to_string()),
260                ]),
261                vec![ModuleRuleEffect::ModuleType(ModuleType::Json)],
262            ),
263            ModuleRule::new_all(
264                RuleCondition::any(vec![
265                    RuleCondition::ResourcePathEndsWith(".js".to_string()),
266                    RuleCondition::ResourcePathEndsWith(".jsx".to_string()),
267                    RuleCondition::ContentTypeStartsWith("application/javascript".to_string()),
268                    RuleCondition::ContentTypeStartsWith("text/javascript".to_string()),
269                ]),
270                vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
271                    transforms: app_transforms.to_resolved().await?,
272                    options: ecmascript_options_vc,
273                })],
274            ),
275            ModuleRule::new_all(
276                RuleCondition::ResourcePathEndsWith(".mjs".to_string()),
277                vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
278                    transforms: app_transforms.to_resolved().await?,
279                    options: EcmascriptOptions {
280                        specified_module_type: SpecifiedModuleType::EcmaScript,
281                        ..ecmascript_options
282                    }
283                    .resolved_cell(),
284                })],
285            ),
286            ModuleRule::new_all(
287                RuleCondition::ResourcePathEndsWith(".cjs".to_string()),
288                vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
289                    transforms: app_transforms.to_resolved().await?,
290                    options: EcmascriptOptions {
291                        specified_module_type: SpecifiedModuleType::CommonJs,
292                        ..ecmascript_options
293                    }
294                    .resolved_cell(),
295                })],
296            ),
297            ModuleRule::new_all(
298                RuleCondition::ResourcePathEndsWith(".ts".to_string()),
299                vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
300                    transforms: ts_app_transforms.to_resolved().await?,
301                    tsx: false,
302                    analyze_types: enable_types,
303                    options: ecmascript_options_vc,
304                })],
305            ),
306            ModuleRule::new_all(
307                RuleCondition::ResourcePathEndsWith(".tsx".to_string()),
308                vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
309                    transforms: ts_app_transforms.to_resolved().await?,
310                    tsx: true,
311                    analyze_types: enable_types,
312                    options: ecmascript_options_vc,
313                })],
314            ),
315            ModuleRule::new_all(
316                RuleCondition::ResourcePathEndsWith(".mts".to_string()),
317                vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
318                    transforms: ts_app_transforms.to_resolved().await?,
319                    tsx: false,
320                    analyze_types: enable_types,
321                    options: EcmascriptOptions {
322                        specified_module_type: SpecifiedModuleType::EcmaScript,
323                        ..ecmascript_options
324                    }
325                    .resolved_cell(),
326                })],
327            ),
328            ModuleRule::new_all(
329                RuleCondition::ResourcePathEndsWith(".mtsx".to_string()),
330                vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
331                    transforms: ts_app_transforms.to_resolved().await?,
332                    tsx: true,
333                    analyze_types: enable_types,
334                    options: EcmascriptOptions {
335                        specified_module_type: SpecifiedModuleType::EcmaScript,
336                        ..ecmascript_options
337                    }
338                    .resolved_cell(),
339                })],
340            ),
341            ModuleRule::new_all(
342                RuleCondition::ResourcePathEndsWith(".cts".to_string()),
343                vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
344                    transforms: ts_app_transforms.to_resolved().await?,
345                    tsx: false,
346                    analyze_types: enable_types,
347                    options: EcmascriptOptions {
348                        specified_module_type: SpecifiedModuleType::CommonJs,
349                        ..ecmascript_options
350                    }
351                    .resolved_cell(),
352                })],
353            ),
354            ModuleRule::new_all(
355                RuleCondition::ResourcePathEndsWith(".ctsx".to_string()),
356                vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
357                    transforms: ts_app_transforms.to_resolved().await?,
358                    tsx: true,
359                    analyze_types: enable_types,
360                    options: EcmascriptOptions {
361                        specified_module_type: SpecifiedModuleType::CommonJs,
362                        ..ecmascript_options
363                    }
364                    .resolved_cell(),
365                })],
366            ),
367            ModuleRule::new(
368                RuleCondition::ResourcePathEndsWith(".d.ts".to_string()),
369                vec![ModuleRuleEffect::ModuleType(
370                    ModuleType::TypescriptDeclaration {
371                        transforms: vendor_transforms.to_resolved().await?,
372                        options: ecmascript_options_vc,
373                    },
374                )],
375            ),
376            ModuleRule::new(
377                RuleCondition::any(vec![RuleCondition::ResourcePathEndsWith(
378                    ".node".to_string(),
379                )]),
380                vec![ModuleRuleEffect::ModuleType(ModuleType::Raw)],
381            ),
382            // WebAssembly
383            ModuleRule::new(
384                RuleCondition::any(vec![
385                    RuleCondition::ResourcePathEndsWith(".wasm".to_string()),
386                    RuleCondition::ContentTypeStartsWith("application/wasm".to_string()),
387                ]),
388                vec![ModuleRuleEffect::ModuleType(ModuleType::WebAssembly {
389                    source_ty: WebAssemblySourceType::Binary,
390                })],
391            ),
392            ModuleRule::new(
393                RuleCondition::any(vec![RuleCondition::ResourcePathEndsWith(
394                    ".wat".to_string(),
395                )]),
396                vec![ModuleRuleEffect::ModuleType(ModuleType::WebAssembly {
397                    source_ty: WebAssemblySourceType::Text,
398                })],
399            ),
400            // Fallback to ecmascript without extension (this is node.js behavior)
401            ModuleRule::new(
402                RuleCondition::all(vec![
403                    RuleCondition::ResourcePathHasNoExtension,
404                    RuleCondition::ContentTypeEmpty,
405                ]),
406                vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
407                    transforms: vendor_transforms.to_resolved().await?,
408                    options: ecmascript_options_vc,
409                })],
410            ),
411            // Static assets
412            ModuleRule::new(
413                RuleCondition::any(vec![
414                    RuleCondition::ResourcePathEndsWith(".apng".to_string()),
415                    RuleCondition::ResourcePathEndsWith(".avif".to_string()),
416                    RuleCondition::ResourcePathEndsWith(".gif".to_string()),
417                    RuleCondition::ResourcePathEndsWith(".ico".to_string()),
418                    RuleCondition::ResourcePathEndsWith(".jpg".to_string()),
419                    RuleCondition::ResourcePathEndsWith(".jpeg".to_string()),
420                    RuleCondition::ResourcePathEndsWith(".png".to_string()),
421                    RuleCondition::ResourcePathEndsWith(".svg".to_string()),
422                    RuleCondition::ResourcePathEndsWith(".webp".to_string()),
423                    RuleCondition::ResourcePathEndsWith(".woff2".to_string()),
424                ]),
425                vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs)],
426            ),
427            ModuleRule::new(
428                RuleCondition::ReferenceType(ReferenceType::Url(UrlReferenceSubType::Undefined)),
429                vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs)],
430            ),
431            ModuleRule::new(
432                RuleCondition::ReferenceType(ReferenceType::Url(UrlReferenceSubType::CssUrl)),
433                vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlCss)],
434            ),
435        ];
436
437        if enable_raw_css {
438            rules.extend([
439                ModuleRule::new(
440                    RuleCondition::any(vec![
441                        RuleCondition::ResourcePathEndsWith(".css".to_string()),
442                        RuleCondition::ContentTypeStartsWith("text/css".to_string()),
443                    ]),
444                    vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
445                        ty: CssModuleAssetType::Default,
446                        environment,
447                    })],
448                ),
449                ModuleRule::new(
450                    RuleCondition::any(vec![
451                        RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
452                        RuleCondition::ContentTypeStartsWith("text/css+module".to_string()),
453                    ]),
454                    vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
455                        ty: CssModuleAssetType::Module,
456                        environment,
457                    })],
458                ),
459            ]);
460        } else {
461            if let Some(options) = enable_postcss_transform {
462                let options = options.await?;
463                let execution_context = execution_context
464                    .context("execution_context is required for the postcss_transform")?;
465
466                let import_map = if let Some(postcss_package) = options.postcss_package {
467                    package_import_map_from_import_mapping("postcss".into(), *postcss_package)
468                } else {
469                    package_import_map_from_context(
470                        rcstr!("postcss"),
471                        path.clone()
472                            .context("need_path in ModuleOptions::new is incorrect")?,
473                    )
474                };
475
476                rules.push(ModuleRule::new(
477                    RuleCondition::Any(vec![
478                        RuleCondition::ResourcePathEndsWith(".css".to_string()),
479                        RuleCondition::ContentTypeStartsWith("text/css".to_string()),
480                    ]),
481                    vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
482                        ResolvedVc::upcast(
483                            PostCssTransform::new(
484                                node_evaluate_asset_context(
485                                    *execution_context,
486                                    Some(import_map),
487                                    None,
488                                    Layer::new(rcstr!("postcss")),
489                                    true,
490                                ),
491                                *execution_context,
492                                options.config_location,
493                                matches!(css_source_maps, SourceMapsType::Full),
494                            )
495                            .to_resolved()
496                            .await?,
497                        ),
498                    ]))],
499                ));
500            }
501
502            rules.extend([
503                ModuleRule::new_all(
504                    RuleCondition::Any(vec![
505                        RuleCondition::ResourcePathEndsWith(".css".to_string()),
506                        RuleCondition::ContentTypeStartsWith("text/css".to_string()),
507                    ]),
508                    vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
509                        ty: CssModuleAssetType::Default,
510                        environment,
511                    })],
512                ),
513                ModuleRule::new(
514                    RuleCondition::all(vec![
515                        RuleCondition::Any(vec![
516                            RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
517                            RuleCondition::ContentTypeStartsWith("text/css+module".to_string()),
518                        ]),
519                        // Only create a module CSS asset if not `@import`ed from CSS already.
520                        // NOTE: `composes` references should not be treated as `@import`s and
521                        // should also create a module CSS asset.
522                        RuleCondition::not(RuleCondition::ReferenceType(ReferenceType::Css(
523                            CssReferenceSubType::AtImport(None),
524                        ))),
525                    ]),
526                    vec![ModuleRuleEffect::ModuleType(ModuleType::CssModule)],
527                ),
528                ModuleRule::new(
529                    RuleCondition::all(vec![
530                        RuleCondition::Any(vec![
531                            RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
532                            RuleCondition::ContentTypeStartsWith("text/css+module".to_string()),
533                        ]),
534                        // Create a normal CSS asset if `@import`ed from CSS already.
535                        RuleCondition::ReferenceType(ReferenceType::Css(
536                            CssReferenceSubType::AtImport(None),
537                        )),
538                    ]),
539                    vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
540                        ty: CssModuleAssetType::Module,
541                        environment,
542                    })],
543                ),
544                // Ecmascript CSS Modules referencing the actual CSS module to include it
545                ModuleRule::new_internal(
546                    RuleCondition::Any(vec![
547                        RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
548                        RuleCondition::ContentTypeStartsWith("text/css+module".to_string()),
549                    ]),
550                    vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
551                        ty: CssModuleAssetType::Module,
552                        environment,
553                    })],
554                ),
555                // Ecmascript CSS Modules referencing the actual CSS module to list the classes
556                ModuleRule::new(
557                    RuleCondition::all(vec![
558                        RuleCondition::ReferenceType(ReferenceType::Css(
559                            CssReferenceSubType::Analyze,
560                        )),
561                        RuleCondition::Any(vec![
562                            RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
563                            RuleCondition::ContentTypeStartsWith("text/css+module".to_string()),
564                        ]),
565                    ]),
566                    vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
567                        ty: CssModuleAssetType::Module,
568                        environment,
569                    })],
570                ),
571            ]);
572        }
573
574        if enable_mdx || enable_mdx_rs.is_some() {
575            let (jsx_runtime, jsx_import_source, development) = if let Some(enable_jsx) = enable_jsx
576            {
577                let jsx = enable_jsx.await?;
578                (
579                    jsx.runtime.clone(),
580                    jsx.import_source.clone(),
581                    jsx.development,
582                )
583            } else {
584                (None, None, false)
585            };
586
587            let mdx_options = &*enable_mdx_rs
588                .unwrap_or_else(|| MdxTransformOptions::default().resolved_cell())
589                .await?;
590
591            let mdx_transform_options = (MdxTransformOptions {
592                development: Some(development),
593                jsx: Some(false),
594                jsx_runtime,
595                jsx_import_source,
596                ..(mdx_options.clone())
597            })
598            .cell();
599
600            rules.push(ModuleRule::new(
601                RuleCondition::any(vec![
602                    RuleCondition::ResourcePathEndsWith(".md".to_string()),
603                    RuleCondition::ResourcePathEndsWith(".mdx".to_string()),
604                    RuleCondition::ContentTypeStartsWith("text/markdown".to_string()),
605                ]),
606                vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
607                    ResolvedVc::upcast(
608                        MdxTransform::new(mdx_transform_options)
609                            .to_resolved()
610                            .await?,
611                    ),
612                ]))],
613            ));
614        }
615
616        if let Some(webpack_loaders_options) = enable_webpack_loaders {
617            let webpack_loaders_options = webpack_loaders_options.await?;
618            let execution_context =
619                execution_context.context("execution_context is required for webpack_loaders")?;
620            let import_map = if let Some(loader_runner_package) =
621                webpack_loaders_options.loader_runner_package
622            {
623                package_import_map_from_import_mapping(
624                    "loader-runner".into(),
625                    *loader_runner_package,
626                )
627            } else {
628                package_import_map_from_context(
629                    "loader-runner".into(),
630                    path.context("need_path in ModuleOptions::new is incorrect")?,
631                )
632            };
633            for (key, rule) in webpack_loaders_options.rules.await?.iter() {
634                rules.push(ModuleRule::new(
635                    RuleCondition::All(vec![
636                        if key.starts_with("#") {
637                            // This is a custom marker requiring a corresponding condition entry
638                            let conditions = (*webpack_loaders_options.conditions.await?)
639                                .context(
640                                    "Expected a condition entry for the webpack loader rule \
641                                     matching {key}. Create a `conditions` mapping in your \
642                                     next.config.js",
643                                )?
644                                .await?;
645
646                            let condition = conditions.get(key).context(
647                                "Expected a condition entry for the webpack loader rule matching \
648                                 {key}.",
649                            )?;
650
651                            match &condition.path {
652                                ConditionPath::Glob(glob) => RuleCondition::ResourcePathGlob {
653                                    base: execution_context.project_path().await?.clone_value(),
654                                    glob: Glob::new(glob.clone()).await?,
655                                },
656                                ConditionPath::Regex(regex) => {
657                                    RuleCondition::ResourcePathEsRegex(regex.await?)
658                                }
659                            }
660                        } else if key.contains('/') {
661                            RuleCondition::ResourcePathGlob {
662                                base: execution_context.project_path().await?.clone_value(),
663                                glob: Glob::new(key.clone()).await?,
664                            }
665                        } else {
666                            RuleCondition::ResourceBasePathGlob(Glob::new(key.clone()).await?)
667                        },
668                        RuleCondition::not(RuleCondition::ResourceIsVirtualSource),
669                    ]),
670                    vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
671                        ResolvedVc::upcast(
672                            WebpackLoaders::new(
673                                node_evaluate_asset_context(
674                                    *execution_context,
675                                    Some(import_map),
676                                    None,
677                                    Layer::new(rcstr!("webpack_loaders")),
678                                    false,
679                                ),
680                                *execution_context,
681                                *rule.loaders,
682                                rule.rename_as.clone(),
683                                resolve_options_context,
684                                matches!(ecmascript_source_maps, SourceMapsType::Full),
685                            )
686                            .to_resolved()
687                            .await?,
688                        ),
689                    ]))],
690                ));
691            }
692        }
693
694        rules.extend(module_rules.iter().cloned());
695
696        Ok(ModuleOptions::cell(ModuleOptions { rules }))
697    }
698}