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