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::{IntoTraitRef, ResolvedVc, TryJoinIterExt, Vc};
15use turbo_tasks_fs::{
16    FileSystemPath,
17    glob::{Glob, GlobOptions},
18};
19use turbopack_core::{
20    chunk::SourceMapsType,
21    ident::Layer,
22    reference_type::{CssReferenceSubType, ReferenceType, UrlReferenceSubType},
23    resolve::options::{ImportMap, ImportMapping},
24};
25use turbopack_css::CssModuleAssetType;
26use turbopack_ecmascript::{
27    EcmascriptInputTransform, EcmascriptInputTransforms, EcmascriptOptions, SpecifiedModuleType,
28};
29use turbopack_mdx::MdxTransform;
30use turbopack_node::{
31    execution_context::ExecutionContext,
32    transforms::{postcss::PostCssTransform, webpack::WebpackLoaders},
33};
34use turbopack_resolve::resolve_options_context::ResolveOptionsContext;
35use turbopack_wasm::source::WebAssemblySourceType;
36
37use crate::evaluate_context::{config_tracing_module_context, node_evaluate_asset_context};
38
39#[turbo_tasks::function]
40fn package_import_map_from_import_mapping(
41    package_name: RcStr,
42    package_mapping: ResolvedVc<ImportMapping>,
43) -> Vc<ImportMap> {
44    let mut import_map = ImportMap::default();
45    import_map.insert_exact_alias(
46        RcStr::from(format!("@vercel/turbopack/{package_name}")),
47        package_mapping,
48    );
49    import_map.cell()
50}
51
52#[turbo_tasks::function]
53fn package_import_map_from_context(
54    package_name: RcStr,
55    context_path: FileSystemPath,
56) -> Vc<ImportMap> {
57    let mut import_map = ImportMap::default();
58    import_map.insert_exact_alias(
59        RcStr::from(format!("@vercel/turbopack/{package_name}")),
60        ImportMapping::PrimaryAlternative(package_name, Some(context_path)).resolved_cell(),
61    );
62    import_map.cell()
63}
64
65async fn rule_condition_from_webpack_condition_glob(
66    execution_context: ResolvedVc<ExecutionContext>,
67    glob: &RcStr,
68) -> Result<RuleCondition> {
69    Ok(if glob.contains('/') {
70        RuleCondition::ResourcePathGlob {
71            base: execution_context.project_path().owned().await?,
72            glob: Glob::new(glob.clone(), GlobOptions::default()).await?,
73        }
74    } else {
75        RuleCondition::ResourceBasePathGlob(Glob::new(glob.clone(), GlobOptions::default()).await?)
76    })
77}
78
79async fn rule_condition_from_webpack_condition(
80    execution_context: ResolvedVc<ExecutionContext>,
81    builtin_conditions: &dyn WebpackLoaderBuiltinConditionSet,
82    webpack_loader_condition: &ConditionItem,
83) -> Result<RuleCondition> {
84    Ok(match webpack_loader_condition {
85        ConditionItem::All(conds) => RuleCondition::All(
86            conds
87                .iter()
88                .map(|c| {
89                    rule_condition_from_webpack_condition(execution_context, builtin_conditions, c)
90                })
91                .try_join()
92                .await?,
93        ),
94        ConditionItem::Any(conds) => RuleCondition::Any(
95            conds
96                .iter()
97                .map(|c| {
98                    rule_condition_from_webpack_condition(execution_context, builtin_conditions, c)
99                })
100                .try_join()
101                .await?,
102        ),
103        ConditionItem::Not(cond) => RuleCondition::Not(Box::new(
104            Box::pin(rule_condition_from_webpack_condition(
105                execution_context,
106                builtin_conditions,
107                cond,
108            ))
109            .await?,
110        )),
111        ConditionItem::Builtin(name) => match builtin_conditions.match_condition(name) {
112            WebpackLoaderBuiltinConditionSetMatch::Matched => RuleCondition::True,
113            WebpackLoaderBuiltinConditionSetMatch::Unmatched => RuleCondition::False,
114            WebpackLoaderBuiltinConditionSetMatch::Invalid => {
115                // We don't expect the user to hit this because whatever deserailizes the user
116                // configuration should validate conditions itself
117                anyhow::bail!("{name:?} is not a valid built-in condition")
118            }
119        },
120        ConditionItem::Base {
121            path,
122            content,
123            query,
124        } => {
125            let mut rule_conditions = Vec::new();
126            match &path {
127                Some(ConditionPath::Glob(glob)) => rule_conditions.push(
128                    rule_condition_from_webpack_condition_glob(execution_context, glob).await?,
129                ),
130                Some(ConditionPath::Regex(regex)) => {
131                    rule_conditions.push(RuleCondition::ResourcePathEsRegex(regex.await?));
132                }
133                None => {}
134            }
135            if let Some(content) = content {
136                rule_conditions.push(RuleCondition::ResourceContentEsRegex(content.await?));
137            }
138            match &query {
139                Some(ConditionQuery::Constant(value)) => {
140                    rule_conditions.push(RuleCondition::ResourceQueryEquals(value.clone().into()));
141                }
142                Some(ConditionQuery::Regex(regex)) => {
143                    rule_conditions.push(RuleCondition::ResourceQueryEsRegex(regex.await?));
144                }
145                None => {}
146            }
147            RuleCondition::All(rule_conditions)
148        }
149    })
150}
151
152#[turbo_tasks::value(cell = "new", eq = "manual")]
153pub struct ModuleOptions {
154    pub rules: Vec<ModuleRule>,
155}
156
157#[turbo_tasks::value_impl]
158impl ModuleOptions {
159    #[turbo_tasks::function]
160    pub async fn new(
161        path: FileSystemPath,
162        module_options_context: Vc<ModuleOptionsContext>,
163        resolve_options_context: Vc<ResolveOptionsContext>,
164    ) -> Result<Vc<ModuleOptions>> {
165        let ModuleOptionsContext {
166            css: CssOptionsContext { enable_raw_css, .. },
167            ref enable_postcss_transform,
168            ref enable_webpack_loaders,
169            ref rules,
170            ..
171        } = *module_options_context.await?;
172
173        if !rules.is_empty() {
174            for (condition, new_context) in rules.iter() {
175                if condition.matches(&path) {
176                    return Ok(ModuleOptions::new(
177                        path,
178                        **new_context,
179                        resolve_options_context,
180                    ));
181                }
182            }
183        }
184
185        let need_path = (!enable_raw_css
186            && if let Some(options) = enable_postcss_transform {
187                let options = options.await?;
188                options.postcss_package.is_none()
189            } else {
190                false
191            })
192            || if let Some(options) = enable_webpack_loaders {
193                let options = options.await?;
194                options.loader_runner_package.is_none()
195            } else {
196                false
197            };
198
199        Ok(Self::new_internal(
200            need_path.then_some(path),
201            module_options_context,
202            resolve_options_context,
203        ))
204    }
205
206    #[turbo_tasks::function]
207    async fn new_internal(
208        path: Option<FileSystemPath>,
209        module_options_context: Vc<ModuleOptionsContext>,
210        resolve_options_context: Vc<ResolveOptionsContext>,
211    ) -> Result<Vc<ModuleOptions>> {
212        let ModuleOptionsContext {
213            ecmascript:
214                EcmascriptOptionsContext {
215                    enable_jsx,
216                    enable_types,
217                    ref enable_typescript_transform,
218                    ref enable_decorators,
219                    ignore_dynamic_requests,
220                    import_externals,
221                    esm_url_rewrite_behavior,
222                    enable_typeof_window_inlining,
223                    enable_exports_info_inlining,
224                    source_maps: ecmascript_source_maps,
225                    inline_helpers,
226                    infer_module_side_effects,
227                    ..
228                },
229            enable_mdx,
230            enable_mdx_rs,
231            css:
232                CssOptionsContext {
233                    enable_raw_css,
234                    source_maps: css_source_maps,
235                    ref module_css_condition,
236                    ..
237                },
238            ref enable_postcss_transform,
239            ref enable_webpack_loaders,
240            environment,
241            ref module_rules,
242            execution_context,
243            tree_shaking_mode,
244            keep_last_successful_parse,
245            analyze_mode,
246            ..
247        } = *module_options_context.await?;
248
249        let module_css_condition = module_css_condition.clone().unwrap_or_else(|| {
250            RuleCondition::any(vec![
251                RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
252                RuleCondition::ContentTypeStartsWith("text/css+module".to_string()),
253            ])
254        });
255
256        // For React Client References, the CSS Module "facade" module lives in the parent (server)
257        // module context, but the facade's references should be transitioned to the client (and
258        // only then be processed with Webpack/PostCSS).
259        //
260        // Note that this is not an exhaustive condition for PostCSS/Webpack, but excludes certain
261        // cases, so it should be added conjunctively together with CSS Module rule.
262        //
263        // If module css, then only when (Inner or Analyze or Compose)
264        // <=> (not (module css)) or (Inner or Analyzer or Compose)
265        //
266        // So only if this is not a CSS module, or one of the special reference type constraints.
267        let module_css_external_transform_conditions = RuleCondition::Any(vec![
268            RuleCondition::not(module_css_condition.clone()),
269            RuleCondition::ReferenceType(ReferenceType::Css(CssReferenceSubType::Inner)),
270            RuleCondition::ReferenceType(ReferenceType::Css(CssReferenceSubType::Analyze)),
271        ]);
272
273        let mut ecma_preprocess = vec![];
274        let mut postprocess = vec![];
275
276        // Order of transforms is important. e.g. if the React transform occurs before
277        // Styled JSX, there won't be JSX nodes for Styled JSX to transform.
278        // If a custom plugin requires specific order _before_ core transform kicks in,
279        // should use `before_transform_plugins`.
280        if let Some(enable_jsx) = enable_jsx {
281            let jsx = enable_jsx.await?;
282
283            postprocess.push(EcmascriptInputTransform::React {
284                development: jsx.development,
285                refresh: jsx.react_refresh,
286                import_source: ResolvedVc::cell(jsx.import_source.clone()),
287                runtime: ResolvedVc::cell(jsx.runtime.clone()),
288            });
289        }
290
291        let ecmascript_options = EcmascriptOptions {
292            tree_shaking_mode,
293            url_rewrite_behavior: esm_url_rewrite_behavior,
294            import_externals,
295            ignore_dynamic_requests,
296            extract_source_map: matches!(ecmascript_source_maps, SourceMapsType::Full),
297            keep_last_successful_parse,
298            analyze_mode,
299            enable_typeof_window_inlining,
300            enable_exports_info_inlining,
301            inline_helpers,
302            infer_module_side_effects,
303            ..Default::default()
304        };
305        let ecmascript_options_vc = ecmascript_options.resolved_cell();
306
307        if let Some(environment) = environment {
308            postprocess.push(EcmascriptInputTransform::PresetEnv(environment));
309        }
310
311        let decorators_transform = if let Some(options) = &enable_decorators {
312            let options = options.await?;
313            options
314                .decorators_kind
315                .as_ref()
316                .map(|kind| EcmascriptInputTransform::Decorators {
317                    is_legacy: kind == &DecoratorsKind::Legacy,
318                    is_ecma: kind == &DecoratorsKind::Ecma,
319                    emit_decorators_metadata: options.emit_decorators_metadata,
320                    use_define_for_class_fields: options.use_define_for_class_fields,
321                })
322        } else {
323            None
324        };
325
326        if let Some(decorators_transform) = &decorators_transform {
327            // Apply decorators transform for the ModuleType::Ecmascript as well after
328            // constructing ts_app_transforms. Ecmascript can have decorators for
329            // the cases of 1. using jsconfig, to enable ts-specific runtime
330            // decorators (i.e legacy) 2. ecma spec decorators
331            //
332            // Since typescript transform (`ts_app_transforms`) needs to apply decorators
333            // _before_ stripping types, we create ts_app_transforms first in a
334            // specific order with typescript, then apply decorators to app_transforms.
335            ecma_preprocess.splice(0..0, [decorators_transform.clone()]);
336        }
337
338        let ecma_preprocess = ResolvedVc::cell(ecma_preprocess);
339        let main = ResolvedVc::<EcmascriptInputTransforms>::cell(vec![]);
340        let postprocess = ResolvedVc::cell(postprocess);
341        let empty = ResolvedVc::<EcmascriptInputTransforms>::cell(vec![]);
342
343        let mut rules = vec![
344            ModuleRule::new_all(
345                RuleCondition::any(vec![
346                    RuleCondition::ResourcePathEndsWith(".json".to_string()),
347                    RuleCondition::ContentTypeStartsWith("application/json".to_string()),
348                ]),
349                vec![ModuleRuleEffect::ModuleType(ModuleType::Json)],
350            ),
351            ModuleRule::new_all(
352                RuleCondition::any(vec![
353                    RuleCondition::ResourcePathEndsWith(".js".to_string()),
354                    RuleCondition::ResourcePathEndsWith(".jsx".to_string()),
355                    RuleCondition::ContentTypeStartsWith("application/javascript".to_string()),
356                    RuleCondition::ContentTypeStartsWith("text/javascript".to_string()),
357                ]),
358                vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
359                    preprocess: ecma_preprocess,
360                    main,
361                    postprocess,
362                    options: ecmascript_options_vc,
363                })],
364            ),
365            ModuleRule::new_all(
366                RuleCondition::ResourcePathEndsWith(".mjs".to_string()),
367                vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
368                    preprocess: ecma_preprocess,
369                    main,
370                    postprocess,
371                    options: EcmascriptOptions {
372                        specified_module_type: SpecifiedModuleType::EcmaScript,
373                        ..ecmascript_options
374                    }
375                    .resolved_cell(),
376                })],
377            ),
378            ModuleRule::new_all(
379                RuleCondition::ResourcePathEndsWith(".cjs".to_string()),
380                vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
381                    preprocess: ecma_preprocess,
382                    main,
383                    postprocess,
384                    options: EcmascriptOptions {
385                        specified_module_type: SpecifiedModuleType::CommonJs,
386                        ..ecmascript_options
387                    }
388                    .resolved_cell(),
389                })],
390            ),
391            ModuleRule::new(
392                RuleCondition::ResourcePathEndsWith(".d.ts".to_string()),
393                vec![ModuleRuleEffect::ModuleType(
394                    ModuleType::TypescriptDeclaration {
395                        preprocess: empty,
396                        main: empty,
397                        postprocess: empty,
398                        options: ecmascript_options_vc,
399                    },
400                )],
401            ),
402            ModuleRule::new(
403                RuleCondition::any(vec![RuleCondition::ResourcePathEndsWith(
404                    ".node".to_string(),
405                )]),
406                vec![ModuleRuleEffect::ModuleType(ModuleType::NodeAddon)],
407            ),
408            // WebAssembly
409            ModuleRule::new(
410                RuleCondition::any(vec![
411                    RuleCondition::ResourcePathEndsWith(".wasm".to_string()),
412                    RuleCondition::ContentTypeStartsWith("application/wasm".to_string()),
413                ]),
414                vec![ModuleRuleEffect::ModuleType(ModuleType::WebAssembly {
415                    source_ty: WebAssemblySourceType::Binary,
416                })],
417            ),
418            ModuleRule::new(
419                RuleCondition::any(vec![RuleCondition::ResourcePathEndsWith(
420                    ".wat".to_string(),
421                )]),
422                vec![ModuleRuleEffect::ModuleType(ModuleType::WebAssembly {
423                    source_ty: WebAssemblySourceType::Text,
424                })],
425            ),
426            // Fallback to ecmascript without extension (this is node.js behavior)
427            ModuleRule::new(
428                RuleCondition::all(vec![
429                    RuleCondition::ResourcePathHasNoExtension,
430                    RuleCondition::ContentTypeEmpty,
431                ]),
432                vec![ModuleRuleEffect::ModuleType(
433                    ModuleType::EcmascriptExtensionless {
434                        preprocess: empty,
435                        main: empty,
436                        postprocess: empty,
437                        options: ecmascript_options_vc,
438                    },
439                )],
440            ),
441            // Static assets
442            ModuleRule::new(
443                RuleCondition::any(vec![
444                    RuleCondition::ResourcePathEndsWith(".apng".to_string()),
445                    RuleCondition::ResourcePathEndsWith(".avif".to_string()),
446                    RuleCondition::ResourcePathEndsWith(".gif".to_string()),
447                    RuleCondition::ResourcePathEndsWith(".ico".to_string()),
448                    RuleCondition::ResourcePathEndsWith(".jpg".to_string()),
449                    RuleCondition::ResourcePathEndsWith(".jpeg".to_string()),
450                    RuleCondition::ResourcePathEndsWith(".png".to_string()),
451                    RuleCondition::ResourcePathEndsWith(".svg".to_string()),
452                    RuleCondition::ResourcePathEndsWith(".webp".to_string()),
453                    RuleCondition::ResourcePathEndsWith(".woff2".to_string()),
454                ]),
455                vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs {
456                    tag: None,
457                })],
458            ),
459            ModuleRule::new(
460                RuleCondition::ReferenceType(ReferenceType::Url(UrlReferenceSubType::Undefined)),
461                vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs {
462                    tag: None,
463                })],
464            ),
465            ModuleRule::new(
466                RuleCondition::ReferenceType(ReferenceType::Url(
467                    UrlReferenceSubType::EcmaScriptNewUrl,
468                )),
469                vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs {
470                    tag: None,
471                })],
472            ),
473            ModuleRule::new(
474                RuleCondition::ReferenceType(ReferenceType::Url(UrlReferenceSubType::CssUrl)),
475                vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlCss {
476                    tag: None,
477                })],
478            ),
479        ];
480
481        if let Some(options) = enable_typescript_transform {
482            let ts_preprocess = ResolvedVc::cell(
483                decorators_transform
484                    .clone()
485                    .into_iter()
486                    .chain(std::iter::once(EcmascriptInputTransform::TypeScript {
487                        use_define_for_class_fields: options.await?.use_define_for_class_fields,
488                    }))
489                    .collect(),
490            );
491
492            rules.splice(
493                0..0,
494                [
495                    ModuleRule::new_all(
496                        RuleCondition::ResourcePathEndsWith(".ts".to_string()),
497                        vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
498                            preprocess: ts_preprocess,
499                            main,
500                            postprocess,
501                            tsx: false,
502                            analyze_types: enable_types,
503                            options: ecmascript_options_vc,
504                        })],
505                    ),
506                    ModuleRule::new_all(
507                        RuleCondition::ResourcePathEndsWith(".tsx".to_string()),
508                        vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
509                            preprocess: ts_preprocess,
510                            main,
511                            postprocess,
512                            tsx: true,
513                            analyze_types: enable_types,
514                            options: ecmascript_options_vc,
515                        })],
516                    ),
517                    ModuleRule::new_all(
518                        RuleCondition::ResourcePathEndsWith(".mts".to_string()),
519                        vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
520                            preprocess: ts_preprocess,
521                            main,
522                            postprocess,
523                            tsx: false,
524                            analyze_types: enable_types,
525                            options: EcmascriptOptions {
526                                specified_module_type: SpecifiedModuleType::EcmaScript,
527                                ..ecmascript_options
528                            }
529                            .resolved_cell(),
530                        })],
531                    ),
532                    ModuleRule::new_all(
533                        RuleCondition::ResourcePathEndsWith(".mtsx".to_string()),
534                        vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
535                            preprocess: ts_preprocess,
536                            main,
537                            postprocess,
538                            tsx: true,
539                            analyze_types: enable_types,
540                            options: EcmascriptOptions {
541                                specified_module_type: SpecifiedModuleType::EcmaScript,
542                                ..ecmascript_options
543                            }
544                            .resolved_cell(),
545                        })],
546                    ),
547                    ModuleRule::new_all(
548                        RuleCondition::ResourcePathEndsWith(".cts".to_string()),
549                        vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
550                            preprocess: ts_preprocess,
551                            main,
552                            postprocess,
553                            tsx: false,
554                            analyze_types: enable_types,
555                            options: EcmascriptOptions {
556                                specified_module_type: SpecifiedModuleType::CommonJs,
557                                ..ecmascript_options
558                            }
559                            .resolved_cell(),
560                        })],
561                    ),
562                    ModuleRule::new_all(
563                        RuleCondition::ResourcePathEndsWith(".ctsx".to_string()),
564                        vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
565                            preprocess: ts_preprocess,
566                            main,
567                            postprocess,
568                            tsx: true,
569                            analyze_types: enable_types,
570                            options: EcmascriptOptions {
571                                specified_module_type: SpecifiedModuleType::CommonJs,
572                                ..ecmascript_options
573                            }
574                            .resolved_cell(),
575                        })],
576                    ),
577                ],
578            );
579        }
580
581        if let Some(webpack_loaders_options) = enable_webpack_loaders {
582            let webpack_loaders_options = webpack_loaders_options.await?;
583            let execution_context =
584                execution_context.context("execution_context is required for webpack_loaders")?;
585            let import_map = if let Some(loader_runner_package) =
586                webpack_loaders_options.loader_runner_package
587            {
588                package_import_map_from_import_mapping(
589                    rcstr!("loader-runner"),
590                    *loader_runner_package,
591                )
592            } else {
593                package_import_map_from_context(
594                    rcstr!("loader-runner"),
595                    path.clone()
596                        .context("need_path in ModuleOptions::new is incorrect")?,
597                )
598            };
599            let builtin_conditions = webpack_loaders_options
600                .builtin_conditions
601                .into_trait_ref()
602                .await?;
603            for (key, rule) in webpack_loaders_options.rules.await?.iter() {
604                let mut rule_conditions = Vec::new();
605
606                // prefer to add the glob condition ahead of the user-defined `condition` field,
607                // because we know it's cheap to check
608                rule_conditions.push(
609                    rule_condition_from_webpack_condition_glob(execution_context, key).await?,
610                );
611
612                if let Some(condition) = &rule.condition {
613                    rule_conditions.push(
614                        rule_condition_from_webpack_condition(
615                            execution_context,
616                            &*builtin_conditions,
617                            condition,
618                        )
619                        .await?,
620                    )
621                }
622
623                rule_conditions.push(RuleCondition::not(RuleCondition::ResourceIsVirtualSource));
624                rule_conditions.push(module_css_external_transform_conditions.clone());
625
626                let mut all_rule_condition = RuleCondition::All(rule_conditions);
627                all_rule_condition.flatten();
628                if !matches!(all_rule_condition, RuleCondition::False) {
629                    rules.push(ModuleRule::new(
630                        all_rule_condition,
631                        vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
632                            ResolvedVc::upcast(
633                                WebpackLoaders::new(
634                                    node_evaluate_asset_context(
635                                        *execution_context,
636                                        Some(import_map),
637                                        None,
638                                        Layer::new(rcstr!("webpack_loaders")),
639                                        false,
640                                    ),
641                                    *execution_context,
642                                    *rule.loaders,
643                                    rule.rename_as.clone(),
644                                    resolve_options_context,
645                                    matches!(ecmascript_source_maps, SourceMapsType::Full),
646                                )
647                                .to_resolved()
648                                .await?,
649                            ),
650                        ]))],
651                    ));
652                }
653            }
654        }
655
656        if enable_raw_css {
657            rules.extend([
658                ModuleRule::new(
659                    RuleCondition::any(vec![
660                        RuleCondition::ResourcePathEndsWith(".css".to_string()),
661                        RuleCondition::ContentTypeStartsWith("text/css".to_string()),
662                    ]),
663                    vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
664                        ty: CssModuleAssetType::Default,
665                        environment,
666                    })],
667                ),
668                ModuleRule::new(
669                    module_css_condition.clone(),
670                    vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
671                        ty: CssModuleAssetType::Module,
672                        environment,
673                    })],
674                ),
675            ]);
676        } else {
677            if let Some(options) = enable_postcss_transform {
678                let options = options.await?;
679                let execution_context = execution_context
680                    .context("execution_context is required for the postcss_transform")?;
681
682                let import_map = if let Some(postcss_package) = options.postcss_package {
683                    package_import_map_from_import_mapping(rcstr!("postcss"), *postcss_package)
684                } else {
685                    package_import_map_from_context(
686                        rcstr!("postcss"),
687                        path.clone()
688                            .context("need_path in ModuleOptions::new is incorrect")?,
689                    )
690                };
691
692                rules.push(ModuleRule::new(
693                    RuleCondition::All(vec![
694                        RuleCondition::Any(vec![
695                            // Both CSS and CSS Modules
696                            RuleCondition::ResourcePathEndsWith(".css".to_string()),
697                            RuleCondition::ContentTypeStartsWith("text/css".to_string()),
698                            module_css_condition.clone(),
699                        ]),
700                        module_css_external_transform_conditions.clone(),
701                    ]),
702                    vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
703                        ResolvedVc::upcast(
704                            PostCssTransform::new(
705                                node_evaluate_asset_context(
706                                    *execution_context,
707                                    Some(import_map),
708                                    None,
709                                    Layer::new(rcstr!("postcss")),
710                                    true,
711                                ),
712                                config_tracing_module_context(*execution_context),
713                                *execution_context,
714                                options.config_location,
715                                matches!(css_source_maps, SourceMapsType::Full),
716                            )
717                            .to_resolved()
718                            .await?,
719                        ),
720                    ]))],
721                ));
722            }
723
724            rules.extend([
725                ModuleRule::new_all(
726                    RuleCondition::Any(vec![
727                        RuleCondition::ResourcePathEndsWith(".css".to_string()),
728                        RuleCondition::ContentTypeStartsWith("text/css".to_string()),
729                    ]),
730                    vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
731                        ty: CssModuleAssetType::Default,
732                        environment,
733                    })],
734                ),
735                ModuleRule::new(
736                    RuleCondition::all(vec![
737                        module_css_condition.clone(),
738                        // Only create a module CSS asset if not `@import`ed from CSS already.
739                        // NOTE: `composes` references should not be treated as `@import`s and
740                        // should also create a module CSS asset.
741                        RuleCondition::not(RuleCondition::ReferenceType(ReferenceType::Css(
742                            CssReferenceSubType::AtImport(None),
743                        ))),
744                    ]),
745                    vec![ModuleRuleEffect::ModuleType(ModuleType::CssModule)],
746                ),
747                ModuleRule::new(
748                    RuleCondition::all(vec![
749                        module_css_condition.clone(),
750                        // Create a normal CSS asset if `@import`ed from CSS already.
751                        RuleCondition::ReferenceType(ReferenceType::Css(
752                            CssReferenceSubType::AtImport(None),
753                        )),
754                    ]),
755                    vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
756                        ty: CssModuleAssetType::Module,
757                        environment,
758                    })],
759                ),
760                // Ecmascript CSS Modules referencing the actual CSS module to include it
761                ModuleRule::new(
762                    RuleCondition::all(vec![
763                        RuleCondition::ReferenceType(ReferenceType::Css(
764                            CssReferenceSubType::Inner,
765                        )),
766                        module_css_condition.clone(),
767                    ]),
768                    vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
769                        ty: CssModuleAssetType::Module,
770                        environment,
771                    })],
772                ),
773                // Ecmascript CSS Modules referencing the actual CSS module to list the classes
774                ModuleRule::new(
775                    RuleCondition::all(vec![
776                        RuleCondition::ReferenceType(ReferenceType::Css(
777                            CssReferenceSubType::Analyze,
778                        )),
779                        module_css_condition.clone(),
780                    ]),
781                    vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
782                        ty: CssModuleAssetType::Module,
783                        environment,
784                    })],
785                ),
786            ]);
787        }
788
789        if enable_mdx || enable_mdx_rs.is_some() {
790            let (jsx_runtime, jsx_import_source, development) = if let Some(enable_jsx) = enable_jsx
791            {
792                let jsx = enable_jsx.await?;
793                (
794                    jsx.runtime.clone(),
795                    jsx.import_source.clone(),
796                    jsx.development,
797                )
798            } else {
799                (None, None, false)
800            };
801
802            let mdx_options = &*enable_mdx_rs
803                .unwrap_or_else(|| MdxTransformOptions::default().resolved_cell())
804                .await?;
805
806            let mdx_transform_options = (MdxTransformOptions {
807                development: Some(development),
808                jsx: Some(false),
809                jsx_runtime,
810                jsx_import_source,
811                ..(mdx_options.clone())
812            })
813            .cell();
814
815            rules.push(ModuleRule::new(
816                RuleCondition::any(vec![
817                    RuleCondition::ResourcePathEndsWith(".md".to_string()),
818                    RuleCondition::ResourcePathEndsWith(".mdx".to_string()),
819                    RuleCondition::ContentTypeStartsWith("text/markdown".to_string()),
820                ]),
821                vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
822                    ResolvedVc::upcast(
823                        MdxTransform::new(mdx_transform_options)
824                            .to_resolved()
825                            .await?,
826                    ),
827                ]))],
828            ));
829        }
830
831        rules.extend(module_rules.iter().cloned());
832
833        Ok(ModuleOptions::cell(ModuleOptions { rules }))
834    }
835}