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