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