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