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