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