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