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