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 ref enable_typeof_window_inlining,
211 source_maps: ecmascript_source_maps,
212 ..
213 },
214 enable_mdx,
215 enable_mdx_rs,
216 css:
217 CssOptionsContext {
218 enable_raw_css,
219 source_maps: css_source_maps,
220 ref module_css_condition,
221 ..
222 },
223 ref enable_postcss_transform,
224 ref enable_webpack_loaders,
225 environment,
226 ref module_rules,
227 execution_context,
228 tree_shaking_mode,
229 keep_last_successful_parse,
230 is_tracing,
231 ..
232 } = *module_options_context.await?;
233
234 let module_css_condition = module_css_condition.clone().unwrap_or_else(|| {
235 RuleCondition::any(vec![
236 RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
237 RuleCondition::ContentTypeStartsWith("text/css+module".to_string()),
238 ])
239 });
240
241 let module_css_external_transform_conditions = RuleCondition::Any(vec![
253 RuleCondition::not(module_css_condition.clone()),
254 RuleCondition::ReferenceType(ReferenceType::Css(CssReferenceSubType::Inner)),
255 RuleCondition::ReferenceType(ReferenceType::Css(CssReferenceSubType::Analyze)),
256 ]);
257
258 let mut ts_preprocess = vec![];
259 let mut ecma_preprocess = vec![];
260 let mut postprocess = vec![];
261
262 if let Some(enable_jsx) = enable_jsx {
267 let jsx = enable_jsx.await?;
268
269 postprocess.push(EcmascriptInputTransform::React {
270 development: jsx.development,
271 refresh: jsx.react_refresh,
272 import_source: ResolvedVc::cell(jsx.import_source.clone()),
273 runtime: ResolvedVc::cell(jsx.runtime.clone()),
274 });
275 }
276
277 let ecmascript_options = EcmascriptOptions {
278 tree_shaking_mode,
279 url_rewrite_behavior: esm_url_rewrite_behavior,
280 import_externals,
281 ignore_dynamic_requests,
282 extract_source_map: matches!(ecmascript_source_maps, SourceMapsType::Full),
283 keep_last_successful_parse,
284 is_tracing,
285 ..Default::default()
286 };
287 let ecmascript_options_vc = ecmascript_options.resolved_cell();
288
289 if let Some(environment) = environment {
290 postprocess.push(EcmascriptInputTransform::PresetEnv(environment));
291 }
292
293 if let Some(enable_typeof_window_inlining) = enable_typeof_window_inlining {
294 postprocess.push(EcmascriptInputTransform::GlobalTypeofs {
295 window_value: match enable_typeof_window_inlining {
296 TypeofWindow::Object => rcstr!("object"),
297 TypeofWindow::Undefined => rcstr!("undefined"),
298 },
299 });
300 }
301
302 let ts_transform = if let Some(options) = enable_typescript_transform {
303 let options = options.await?;
304 Some(EcmascriptInputTransform::TypeScript {
305 use_define_for_class_fields: options.use_define_for_class_fields,
306 })
307 } else {
308 None
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(ts_transform) = &ts_transform {
327 if let Some(decorators_transform) = &decorators_transform {
328 ts_preprocess.splice(0..0, [decorators_transform.clone(), ts_transform.clone()]);
329 } else {
330 ts_preprocess.splice(0..0, [ts_transform.clone()]);
331 }
332 }
333 if let Some(decorators_transform) = &decorators_transform {
334 ecma_preprocess.splice(0..0, [decorators_transform.clone()]);
343 }
344
345 let ts_preprocess = ResolvedVc::cell(ts_preprocess);
346 let ecma_preprocess = ResolvedVc::cell(ecma_preprocess);
347 let main = ResolvedVc::<EcmascriptInputTransforms>::cell(vec![]);
348 let postprocess = ResolvedVc::cell(postprocess);
349 let empty = ResolvedVc::<EcmascriptInputTransforms>::cell(vec![]);
350
351 let mut rules = vec![
352 ModuleRule::new_all(
353 RuleCondition::any(vec![
354 RuleCondition::ResourcePathEndsWith(".json".to_string()),
355 RuleCondition::ContentTypeStartsWith("application/json".to_string()),
356 ]),
357 vec![ModuleRuleEffect::ModuleType(ModuleType::Json)],
358 ),
359 ModuleRule::new_all(
360 RuleCondition::any(vec![
361 RuleCondition::ResourcePathEndsWith(".js".to_string()),
362 RuleCondition::ResourcePathEndsWith(".jsx".to_string()),
363 RuleCondition::ContentTypeStartsWith("application/javascript".to_string()),
364 RuleCondition::ContentTypeStartsWith("text/javascript".to_string()),
365 ]),
366 vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
367 preprocess: ecma_preprocess,
368 main,
369 postprocess,
370 options: ecmascript_options_vc,
371 })],
372 ),
373 ModuleRule::new_all(
374 RuleCondition::ResourcePathEndsWith(".mjs".to_string()),
375 vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
376 preprocess: ecma_preprocess,
377 main,
378 postprocess,
379 options: EcmascriptOptions {
380 specified_module_type: SpecifiedModuleType::EcmaScript,
381 ..ecmascript_options
382 }
383 .resolved_cell(),
384 })],
385 ),
386 ModuleRule::new_all(
387 RuleCondition::ResourcePathEndsWith(".cjs".to_string()),
388 vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
389 preprocess: ecma_preprocess,
390 main,
391 postprocess,
392 options: EcmascriptOptions {
393 specified_module_type: SpecifiedModuleType::CommonJs,
394 ..ecmascript_options
395 }
396 .resolved_cell(),
397 })],
398 ),
399 ModuleRule::new_all(
400 RuleCondition::ResourcePathEndsWith(".ts".to_string()),
401 vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
402 preprocess: ts_preprocess,
403 main,
404 postprocess,
405 tsx: false,
406 analyze_types: enable_types,
407 options: ecmascript_options_vc,
408 })],
409 ),
410 ModuleRule::new_all(
411 RuleCondition::ResourcePathEndsWith(".tsx".to_string()),
412 vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
413 preprocess: ts_preprocess,
414 main,
415 postprocess,
416 tsx: true,
417 analyze_types: enable_types,
418 options: ecmascript_options_vc,
419 })],
420 ),
421 ModuleRule::new_all(
422 RuleCondition::ResourcePathEndsWith(".mts".to_string()),
423 vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
424 preprocess: ts_preprocess,
425 main,
426 postprocess,
427 tsx: false,
428 analyze_types: enable_types,
429 options: EcmascriptOptions {
430 specified_module_type: SpecifiedModuleType::EcmaScript,
431 ..ecmascript_options
432 }
433 .resolved_cell(),
434 })],
435 ),
436 ModuleRule::new_all(
437 RuleCondition::ResourcePathEndsWith(".mtsx".to_string()),
438 vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
439 preprocess: ts_preprocess,
440 main,
441 postprocess,
442 tsx: true,
443 analyze_types: enable_types,
444 options: EcmascriptOptions {
445 specified_module_type: SpecifiedModuleType::EcmaScript,
446 ..ecmascript_options
447 }
448 .resolved_cell(),
449 })],
450 ),
451 ModuleRule::new_all(
452 RuleCondition::ResourcePathEndsWith(".cts".to_string()),
453 vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
454 preprocess: ts_preprocess,
455 main,
456 postprocess,
457 tsx: false,
458 analyze_types: enable_types,
459 options: EcmascriptOptions {
460 specified_module_type: SpecifiedModuleType::CommonJs,
461 ..ecmascript_options
462 }
463 .resolved_cell(),
464 })],
465 ),
466 ModuleRule::new_all(
467 RuleCondition::ResourcePathEndsWith(".ctsx".to_string()),
468 vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
469 preprocess: ts_preprocess,
470 main,
471 postprocess,
472 tsx: true,
473 analyze_types: enable_types,
474 options: EcmascriptOptions {
475 specified_module_type: SpecifiedModuleType::CommonJs,
476 ..ecmascript_options
477 }
478 .resolved_cell(),
479 })],
480 ),
481 ModuleRule::new(
482 RuleCondition::ResourcePathEndsWith(".d.ts".to_string()),
483 vec![ModuleRuleEffect::ModuleType(
484 ModuleType::TypescriptDeclaration {
485 preprocess: empty,
486 main: empty,
487 postprocess: empty,
488 options: ecmascript_options_vc,
489 },
490 )],
491 ),
492 ModuleRule::new(
493 RuleCondition::any(vec![RuleCondition::ResourcePathEndsWith(
494 ".node".to_string(),
495 )]),
496 vec![ModuleRuleEffect::ModuleType(ModuleType::NodeAddon)],
497 ),
498 ModuleRule::new(
500 RuleCondition::any(vec![
501 RuleCondition::ResourcePathEndsWith(".wasm".to_string()),
502 RuleCondition::ContentTypeStartsWith("application/wasm".to_string()),
503 ]),
504 vec![ModuleRuleEffect::ModuleType(ModuleType::WebAssembly {
505 source_ty: WebAssemblySourceType::Binary,
506 })],
507 ),
508 ModuleRule::new(
509 RuleCondition::any(vec![RuleCondition::ResourcePathEndsWith(
510 ".wat".to_string(),
511 )]),
512 vec![ModuleRuleEffect::ModuleType(ModuleType::WebAssembly {
513 source_ty: WebAssemblySourceType::Text,
514 })],
515 ),
516 ModuleRule::new(
518 RuleCondition::all(vec![
519 RuleCondition::ResourcePathHasNoExtension,
520 RuleCondition::ContentTypeEmpty,
521 ]),
522 vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
523 preprocess: empty,
524 main: empty,
525 postprocess: empty,
526 options: ecmascript_options_vc,
527 })],
528 ),
529 ModuleRule::new(
531 RuleCondition::any(vec![
532 RuleCondition::ResourcePathEndsWith(".apng".to_string()),
533 RuleCondition::ResourcePathEndsWith(".avif".to_string()),
534 RuleCondition::ResourcePathEndsWith(".gif".to_string()),
535 RuleCondition::ResourcePathEndsWith(".ico".to_string()),
536 RuleCondition::ResourcePathEndsWith(".jpg".to_string()),
537 RuleCondition::ResourcePathEndsWith(".jpeg".to_string()),
538 RuleCondition::ResourcePathEndsWith(".png".to_string()),
539 RuleCondition::ResourcePathEndsWith(".svg".to_string()),
540 RuleCondition::ResourcePathEndsWith(".webp".to_string()),
541 RuleCondition::ResourcePathEndsWith(".woff2".to_string()),
542 ]),
543 vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs)],
544 ),
545 ModuleRule::new(
546 RuleCondition::ReferenceType(ReferenceType::Url(UrlReferenceSubType::Undefined)),
547 vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs)],
548 ),
549 ModuleRule::new(
550 RuleCondition::ReferenceType(ReferenceType::Url(UrlReferenceSubType::CssUrl)),
551 vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlCss)],
552 ),
553 ];
554
555 if let Some(webpack_loaders_options) = enable_webpack_loaders {
556 let webpack_loaders_options = webpack_loaders_options.await?;
557 let execution_context =
558 execution_context.context("execution_context is required for webpack_loaders")?;
559 let import_map = if let Some(loader_runner_package) =
560 webpack_loaders_options.loader_runner_package
561 {
562 package_import_map_from_import_mapping(
563 rcstr!("loader-runner"),
564 *loader_runner_package,
565 )
566 } else {
567 package_import_map_from_context(
568 rcstr!("loader-runner"),
569 path.clone()
570 .context("need_path in ModuleOptions::new is incorrect")?,
571 )
572 };
573 let builtin_conditions = webpack_loaders_options
574 .builtin_conditions
575 .into_trait_ref()
576 .await?;
577 for (key, rule) in webpack_loaders_options.rules.await?.iter() {
578 let mut rule_conditions = Vec::new();
579
580 if key.starts_with("#") {
581 let conditions = (*webpack_loaders_options.conditions.await?)
586 .context(
587 "Expected a condition entry for the webpack loader rule matching \
588 {key}. Create a `conditions` mapping in your next.config.js",
589 )?
590 .await?;
591
592 let condition = conditions.get(key).context(
593 "Expected a condition entry for the webpack loader rule matching {key}.",
594 )?;
595
596 rule_conditions.push(
597 rule_condition_from_webpack_condition(
598 execution_context,
599 &*builtin_conditions,
600 condition,
601 )
602 .await?,
603 )
604 } else {
605 rule_conditions.push(
608 rule_condition_from_webpack_condition_glob(execution_context, key).await?,
609 )
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 *execution_context,
713 options.config_location,
714 matches!(css_source_maps, SourceMapsType::Full),
715 )
716 .to_resolved()
717 .await?,
718 ),
719 ]))],
720 ));
721 }
722
723 rules.extend([
724 ModuleRule::new_all(
725 RuleCondition::Any(vec![
726 RuleCondition::ResourcePathEndsWith(".css".to_string()),
727 RuleCondition::ContentTypeStartsWith("text/css".to_string()),
728 ]),
729 vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
730 ty: CssModuleAssetType::Default,
731 environment,
732 })],
733 ),
734 ModuleRule::new(
735 RuleCondition::all(vec![
736 module_css_condition.clone(),
737 RuleCondition::not(RuleCondition::ReferenceType(ReferenceType::Css(
741 CssReferenceSubType::AtImport(None),
742 ))),
743 ]),
744 vec![ModuleRuleEffect::ModuleType(ModuleType::CssModule)],
745 ),
746 ModuleRule::new(
747 RuleCondition::all(vec![
748 module_css_condition.clone(),
749 RuleCondition::ReferenceType(ReferenceType::Css(
751 CssReferenceSubType::AtImport(None),
752 )),
753 ]),
754 vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
755 ty: CssModuleAssetType::Module,
756 environment,
757 })],
758 ),
759 ModuleRule::new(
761 RuleCondition::all(vec![
762 RuleCondition::ReferenceType(ReferenceType::Css(
763 CssReferenceSubType::Inner,
764 )),
765 module_css_condition.clone(),
766 ]),
767 vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
768 ty: CssModuleAssetType::Module,
769 environment,
770 })],
771 ),
772 ModuleRule::new(
774 RuleCondition::all(vec![
775 RuleCondition::ReferenceType(ReferenceType::Css(
776 CssReferenceSubType::Analyze,
777 )),
778 module_css_condition.clone(),
779 ]),
780 vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
781 ty: CssModuleAssetType::Module,
782 environment,
783 })],
784 ),
785 ]);
786 }
787
788 if enable_mdx || enable_mdx_rs.is_some() {
789 let (jsx_runtime, jsx_import_source, development) = if let Some(enable_jsx) = enable_jsx
790 {
791 let jsx = enable_jsx.await?;
792 (
793 jsx.runtime.clone(),
794 jsx.import_source.clone(),
795 jsx.development,
796 )
797 } else {
798 (None, None, false)
799 };
800
801 let mdx_options = &*enable_mdx_rs
802 .unwrap_or_else(|| MdxTransformOptions::default().resolved_cell())
803 .await?;
804
805 let mdx_transform_options = (MdxTransformOptions {
806 development: Some(development),
807 jsx: Some(false),
808 jsx_runtime,
809 jsx_import_source,
810 ..(mdx_options.clone())
811 })
812 .cell();
813
814 rules.push(ModuleRule::new(
815 RuleCondition::any(vec![
816 RuleCondition::ResourcePathEndsWith(".md".to_string()),
817 RuleCondition::ResourcePathEndsWith(".mdx".to_string()),
818 RuleCondition::ContentTypeStartsWith("text/markdown".to_string()),
819 ]),
820 vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
821 ResolvedVc::upcast(
822 MdxTransform::new(mdx_transform_options)
823 .to_resolved()
824 .await?,
825 ),
826 ]))],
827 ));
828 }
829
830 rules.extend(module_rules.iter().cloned());
831
832 Ok(ModuleOptions::cell(ModuleOptions { rules }))
833 }
834}