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