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::{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::{
23 CssReferenceSubType, EcmaScriptModulesReferenceSubType, ReferenceTypeCondition,
24 UrlReferenceSubType,
25 },
26 resolve::options::{ImportMap, ImportMapping},
27};
28use turbopack_css::CssModuleType;
29use turbopack_ecmascript::{
30 AnalyzeMode, EcmascriptInputTransform, EcmascriptInputTransforms, EcmascriptOptions,
31 SpecifiedModuleType, bytes_source_transform::BytesSourceTransform,
32 json_source_transform::JsonSourceTransform, text_source_transform::TextSourceTransform,
33};
34use turbopack_mdx::MdxTransform;
35use turbopack_node::{
36 execution_context::ExecutionContext,
37 transforms::{postcss::PostCssTransform, webpack::WebpackLoaders},
38};
39use turbopack_resolve::resolve_options_context::ResolveOptionsContext;
40use turbopack_wasm::source::WebAssemblySourceType;
41
42use crate::evaluate_context::{config_tracing_module_context, node_evaluate_asset_context};
43
44#[turbo_tasks::function]
45pub(crate) fn package_import_map_from_import_mapping(
46 package_name: RcStr,
47 package_mapping: ResolvedVc<ImportMapping>,
48) -> Vc<ImportMap> {
49 let mut import_map = ImportMap::default();
50 import_map.insert_exact_alias(
51 RcStr::from(format!("@vercel/turbopack/{package_name}")),
52 package_mapping,
53 );
54 import_map.cell()
55}
56
57#[turbo_tasks::function]
58pub(crate) fn package_import_map_from_context(
59 package_name: RcStr,
60 context_path: FileSystemPath,
61) -> Vc<ImportMap> {
62 let mut import_map = ImportMap::default();
63 import_map.insert_exact_alias(
64 RcStr::from(format!("@vercel/turbopack/{package_name}")),
65 ImportMapping::PrimaryAlternative(package_name, Some(context_path)).resolved_cell(),
66 );
67 import_map.cell()
68}
69
70async fn rule_condition_from_webpack_condition_glob(
71 execution_context: ResolvedVc<ExecutionContext>,
72 glob: &RcStr,
73) -> Result<RuleCondition> {
74 Ok(if glob.contains('/') {
75 RuleCondition::ResourcePathGlob {
76 base: execution_context.project_path().owned().await?,
77 glob: Glob::new(glob.clone(), GlobOptions::default()).await?,
78 }
79 } else {
80 RuleCondition::ResourceBasePathGlob(Glob::new(glob.clone(), GlobOptions::default()).await?)
81 })
82}
83
84async fn rule_condition_from_webpack_condition(
85 execution_context: ResolvedVc<ExecutionContext>,
86 builtin_conditions: &dyn WebpackLoaderBuiltinConditionSet,
87 webpack_loader_condition: &ConditionItem,
88) -> Result<RuleCondition> {
89 Ok(match webpack_loader_condition {
90 ConditionItem::All(conds) => RuleCondition::All(
91 conds
92 .iter()
93 .map(|c| {
94 rule_condition_from_webpack_condition(execution_context, builtin_conditions, c)
95 })
96 .try_join()
97 .await?,
98 ),
99 ConditionItem::Any(conds) => RuleCondition::Any(
100 conds
101 .iter()
102 .map(|c| {
103 rule_condition_from_webpack_condition(execution_context, builtin_conditions, c)
104 })
105 .try_join()
106 .await?,
107 ),
108 ConditionItem::Not(cond) => RuleCondition::Not(Box::new(
109 Box::pin(rule_condition_from_webpack_condition(
110 execution_context,
111 builtin_conditions,
112 cond,
113 ))
114 .await?,
115 )),
116 ConditionItem::Builtin(name) => match builtin_conditions.match_condition(name) {
117 WebpackLoaderBuiltinConditionSetMatch::Matched => RuleCondition::True,
118 WebpackLoaderBuiltinConditionSetMatch::Unmatched => RuleCondition::False,
119 WebpackLoaderBuiltinConditionSetMatch::Invalid => {
120 anyhow::bail!("{name:?} is not a valid built-in condition")
123 }
124 },
125 ConditionItem::Base {
126 path,
127 content,
128 query,
129 content_type,
130 } => {
131 let mut rule_conditions = Vec::new();
132 match &path {
133 Some(ConditionPath::Glob(glob)) => rule_conditions.push(
134 rule_condition_from_webpack_condition_glob(execution_context, glob).await?,
135 ),
136 Some(ConditionPath::Regex(regex)) => {
137 rule_conditions.push(RuleCondition::ResourcePathEsRegex(regex.await?));
138 }
139 None => {}
140 }
141 match &query {
142 Some(ConditionQuery::Constant(value)) => {
143 rule_conditions.push(RuleCondition::ResourceQueryEquals(value.clone().into()));
144 }
145 Some(ConditionQuery::Regex(regex)) => {
146 rule_conditions.push(RuleCondition::ResourceQueryEsRegex(regex.await?));
147 }
148 None => {}
149 }
150 match &content_type {
151 Some(ConditionContentType::Glob(glob)) => {
152 rule_conditions.push(RuleCondition::ContentTypeGlob(
153 Glob::new(glob.clone(), GlobOptions::default()).await?,
154 ));
155 }
156 Some(ConditionContentType::Regex(regex)) => {
157 rule_conditions.push(RuleCondition::ContentTypeEsRegex(regex.await?));
158 }
159 None => {}
160 }
161 if let Some(content) = content {
163 rule_conditions.push(RuleCondition::ResourceContentEsRegex(content.await?));
164 }
165 RuleCondition::All(rule_conditions)
166 }
167 })
168}
169
170#[turbo_tasks::value(cell = "new", eq = "manual")]
171pub struct ModuleOptions {
172 pub rules: Vec<ModuleRule>,
173}
174
175#[turbo_tasks::value_impl]
176impl ModuleOptions {
177 #[turbo_tasks::function]
178 pub async fn new(
179 path: FileSystemPath,
180 module_options_context: Vc<ModuleOptionsContext>,
181 resolve_options_context: Vc<ResolveOptionsContext>,
182 ) -> Result<Vc<ModuleOptions>> {
183 let ModuleOptionsContext {
184 css: CssOptionsContext { enable_raw_css, .. },
185 ref enable_postcss_transform,
186 ref enable_webpack_loaders,
187 ref rules,
188 ..
189 } = *module_options_context.await?;
190
191 if !rules.is_empty() {
192 for (condition, new_context) in rules.iter() {
193 if condition.matches(&path) {
194 return Ok(ModuleOptions::new(
195 path,
196 **new_context,
197 resolve_options_context,
198 ));
199 }
200 }
201 }
202
203 let need_path = (!enable_raw_css
204 && if let Some(options) = enable_postcss_transform {
205 let options = options.await?;
206 options.postcss_package.is_none()
207 } else {
208 false
209 })
210 || if let Some(options) = enable_webpack_loaders {
211 let options = options.await?;
212 options.loader_runner_package.is_none()
213 } else {
214 false
215 };
216
217 Ok(Self::new_internal(
218 need_path.then_some(path),
219 module_options_context,
220 resolve_options_context,
221 ))
222 }
223
224 #[turbo_tasks::function]
225 async fn new_internal(
226 path: Option<FileSystemPath>,
227 module_options_context: Vc<ModuleOptionsContext>,
228 resolve_options_context: Vc<ResolveOptionsContext>,
229 ) -> Result<Vc<ModuleOptions>> {
230 let ModuleOptionsContext {
231 ecmascript:
232 EcmascriptOptionsContext {
233 enable_jsx,
234 enable_types,
235 ref enable_typescript_transform,
236 ref enable_decorators,
237 ignore_dynamic_requests,
238 import_externals,
239 esm_url_rewrite_behavior,
240 enable_typeof_window_inlining,
241 enable_exports_info_inlining,
242 enable_import_as_bytes,
243 enable_import_as_text,
244 source_maps: ecmascript_source_maps,
245 inline_helpers,
246 infer_module_side_effects,
247 ..
248 },
249 enable_mdx,
250 enable_mdx_rs,
251 css:
252 CssOptionsContext {
253 enable_raw_css,
254 source_maps: css_source_maps,
255 ref module_css_condition,
256 lightningcss_features,
257 ..
258 },
259 ref static_url_tag,
260 ref enable_postcss_transform,
261 ref enable_webpack_loaders,
262 environment,
263 ref module_rules,
264 execution_context,
265 tree_shaking_mode,
266 keep_last_successful_parse,
267 analyze_mode,
268 ..
269 } = *module_options_context.await?;
270
271 let module_css_condition = module_css_condition.clone().unwrap_or_else(|| {
272 RuleCondition::any(vec![
273 RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
274 RuleCondition::ContentTypeStartsWith("text/css+module".to_string()),
275 ])
276 });
277
278 let module_css_external_transform_conditions = RuleCondition::Any(vec![
290 RuleCondition::not(module_css_condition.clone()),
291 RuleCondition::ReferenceType(ReferenceTypeCondition::Css(Some(
292 CssReferenceSubType::Inner,
293 ))),
294 RuleCondition::ReferenceType(ReferenceTypeCondition::Css(Some(
295 CssReferenceSubType::Analyze,
296 ))),
297 ]);
298
299 let mut ecma_preprocess = vec![];
300 let mut postprocess = vec![];
301
302 if let Some(enable_jsx) = enable_jsx {
307 let jsx = enable_jsx.await?;
308
309 postprocess.push(EcmascriptInputTransform::React {
310 development: jsx.development,
311 refresh: jsx.react_refresh,
312 import_source: ResolvedVc::cell(jsx.import_source.clone()),
313 runtime: ResolvedVc::cell(jsx.runtime.clone()),
314 });
315 }
316
317 let ecmascript_options = EcmascriptOptions {
318 tree_shaking_mode,
319 url_rewrite_behavior: esm_url_rewrite_behavior,
320 import_externals,
321 ignore_dynamic_requests,
322 extract_source_map: matches!(ecmascript_source_maps, SourceMapsType::Full),
323 keep_last_successful_parse,
324 analyze_mode,
325 enable_typeof_window_inlining,
326 enable_exports_info_inlining,
327 inline_helpers,
328 infer_module_side_effects,
329 ..Default::default()
330 };
331 let ecmascript_options_vc = ecmascript_options.resolved_cell();
332
333 if let Some(environment) = environment {
334 postprocess.push(EcmascriptInputTransform::PresetEnv(environment));
335 }
336
337 let decorators_transform = if let Some(options) = &enable_decorators {
338 let options = options.await?;
339 options
340 .decorators_kind
341 .as_ref()
342 .map(|kind| EcmascriptInputTransform::Decorators {
343 is_legacy: kind == &DecoratorsKind::Legacy,
344 is_ecma: kind == &DecoratorsKind::Ecma,
345 emit_decorators_metadata: options.emit_decorators_metadata,
346 use_define_for_class_fields: options.use_define_for_class_fields,
347 })
348 } else {
349 None
350 };
351
352 if let Some(decorators_transform) = &decorators_transform {
353 ecma_preprocess.splice(0..0, [decorators_transform.clone()]);
362 }
363
364 let ecma_preprocess = ResolvedVc::cell(ecma_preprocess);
365 let main = ResolvedVc::<EcmascriptInputTransforms>::cell(vec![]);
366 let postprocess = ResolvedVc::cell(postprocess);
367 let empty = ResolvedVc::<EcmascriptInputTransforms>::cell(vec![]);
368
369 let mut rules = vec![];
370
371 let is_tracing = analyze_mode == AnalyzeMode::Tracing;
377
378 if enable_import_as_bytes {
382 rules.push(ModuleRule::new(
383 RuleCondition::ReferenceType(ReferenceTypeCondition::EcmaScriptModules(Some(
384 EcmaScriptModulesReferenceSubType::ImportWithType("bytes".into()),
385 ))),
386 if is_tracing {
387 vec![ModuleRuleEffect::ModuleType(ModuleType::Raw)]
388 } else {
389 vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
390 ResolvedVc::upcast(BytesSourceTransform::new().to_resolved().await?),
391 ]))]
392 },
393 ));
394 }
395
396 if enable_import_as_text {
397 rules.push(ModuleRule::new(
398 RuleCondition::ReferenceType(ReferenceTypeCondition::EcmaScriptModules(Some(
399 EcmaScriptModulesReferenceSubType::ImportWithType("text".into()),
400 ))),
401 if is_tracing {
402 vec![ModuleRuleEffect::ModuleType(ModuleType::Raw)]
403 } else {
404 vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
405 ResolvedVc::upcast(TextSourceTransform::new().to_resolved().await?),
406 ]))]
407 },
408 ));
409 }
410
411 if let Some(webpack_loaders_options) = enable_webpack_loaders {
412 let webpack_loaders_options = webpack_loaders_options.await?;
413 let execution_context =
414 execution_context.context("execution_context is required for webpack_loaders")?;
415 let import_map = if let Some(loader_runner_package) =
416 webpack_loaders_options.loader_runner_package
417 {
418 package_import_map_from_import_mapping(
419 rcstr!("loader-runner"),
420 *loader_runner_package,
421 )
422 } else {
423 package_import_map_from_context(
424 rcstr!("loader-runner"),
425 path.clone()
426 .context("need_path in ModuleOptions::new is incorrect")?,
427 )
428 };
429 let builtin_conditions = webpack_loaders_options
430 .builtin_conditions
431 .into_trait_ref()
432 .await?;
433 for (key, rule) in webpack_loaders_options.rules.await?.iter() {
434 let mut rule_conditions = Vec::new();
435
436 rule_conditions.push(
439 rule_condition_from_webpack_condition_glob(execution_context, key).await?,
440 );
441
442 if let Some(condition) = &rule.condition {
443 rule_conditions.push(
444 rule_condition_from_webpack_condition(
445 execution_context,
446 &*builtin_conditions,
447 condition,
448 )
449 .await?,
450 )
451 }
452
453 rule_conditions.push(RuleCondition::not(RuleCondition::ResourceIsVirtualSource));
454 rule_conditions.push(module_css_external_transform_conditions.clone());
455
456 let mut all_rule_condition = RuleCondition::All(rule_conditions);
457 all_rule_condition.flatten();
458 if !matches!(all_rule_condition, RuleCondition::False) {
459 let mut effects = Vec::new();
460
461 if !rule.loaders.await?.is_empty() {
463 effects.push(ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
464 ResolvedVc::upcast(
465 WebpackLoaders::new(
466 node_evaluate_asset_context(
467 *execution_context,
468 Some(import_map),
469 None,
470 Layer::new(rcstr!("webpack_loaders")),
471 false,
472 ),
473 *execution_context,
474 *rule.loaders,
475 rule.rename_as.clone(),
476 resolve_options_context,
477 matches!(ecmascript_source_maps, SourceMapsType::Full),
478 )
479 .to_resolved()
480 .await?,
481 ),
482 ])));
483 }
484
485 if let Some(type_str) = rule.module_type.as_ref() {
487 effects.push(
488 ConfiguredModuleType::parse(type_str)?
489 .into_effect(
490 ecma_preprocess,
491 main,
492 postprocess,
493 ecmascript_options_vc,
494 environment,
495 lightningcss_features,
496 )
497 .await?,
498 )
499 }
500
501 if !effects.is_empty() {
502 rules.push(ModuleRule::new(all_rule_condition, effects));
503 }
504 }
505 }
506 }
507
508 rules.extend(module_rules.iter().cloned());
509
510 if enable_mdx || enable_mdx_rs.is_some() {
511 let (jsx_runtime, jsx_import_source, development) = if let Some(enable_jsx) = enable_jsx
512 {
513 let jsx = enable_jsx.await?;
514 (
515 jsx.runtime.clone(),
516 jsx.import_source.clone(),
517 jsx.development,
518 )
519 } else {
520 (None, None, false)
521 };
522
523 let mdx_options = &*enable_mdx_rs
524 .unwrap_or_else(|| MdxTransformOptions::default().resolved_cell())
525 .await?;
526
527 let mdx_transform_options = (MdxTransformOptions {
528 development: Some(development),
529 jsx: Some(false),
530 jsx_runtime,
531 jsx_import_source,
532 ..(mdx_options.clone())
533 })
534 .cell();
535
536 rules.push(ModuleRule::new(
537 RuleCondition::any(vec![
538 RuleCondition::ResourcePathEndsWith(".md".to_string()),
539 RuleCondition::ResourcePathEndsWith(".mdx".to_string()),
540 RuleCondition::ContentTypeStartsWith("text/markdown".to_string()),
541 ]),
542 vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
543 ResolvedVc::upcast(
544 MdxTransform::new(mdx_transform_options)
545 .to_resolved()
546 .await?,
547 ),
548 ]))],
549 ));
550 }
551
552 rules.extend([
554 ModuleRule::new(
555 RuleCondition::ReferenceType(ReferenceTypeCondition::Url(Some(
556 UrlReferenceSubType::CssUrl,
557 ))),
558 vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlCss {
559 tag: static_url_tag.clone(),
560 })],
561 ),
562 ModuleRule::new(
563 RuleCondition::ReferenceType(ReferenceTypeCondition::Url(Some(
564 UrlReferenceSubType::Undefined,
565 ))),
566 vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs {
567 tag: static_url_tag.clone(),
568 })],
569 ),
570 ModuleRule::new(
571 RuleCondition::ReferenceType(ReferenceTypeCondition::Url(Some(
572 UrlReferenceSubType::EcmaScriptNewUrl,
573 ))),
574 vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs {
575 tag: static_url_tag.clone(),
576 })],
577 ),
578 ModuleRule::new(
579 RuleCondition::ReferenceType(ReferenceTypeCondition::EcmaScriptModules(Some(
580 EcmaScriptModulesReferenceSubType::ImportWithType("json".into()),
581 ))),
582 if is_tracing {
583 vec![ModuleRuleEffect::ModuleType(ModuleType::Raw)]
584 } else {
585 vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
586 ResolvedVc::upcast(JsonSourceTransform::new_esm().to_resolved().await?),
588 ]))]
589 },
590 ),
591 ]);
592
593 rules.extend([
595 ModuleRule::new_all(
596 RuleCondition::any(vec![
597 RuleCondition::ResourcePathEndsWith(".json".to_string()),
598 RuleCondition::ContentTypeStartsWith("application/json".to_string()),
599 ]),
600 if is_tracing {
601 vec![ModuleRuleEffect::ModuleType(ModuleType::Raw)]
602 } else {
603 vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
604 ResolvedVc::upcast(JsonSourceTransform::new_cjs().to_resolved().await?),
606 ]))]
607 },
608 ),
609 ModuleRule::new_all(
610 RuleCondition::any(vec![
611 RuleCondition::ResourcePathEndsWith(".js".to_string()),
612 RuleCondition::ResourcePathEndsWith(".jsx".to_string()),
613 RuleCondition::ContentTypeStartsWith("application/javascript".to_string()),
614 RuleCondition::ContentTypeStartsWith("text/javascript".to_string()),
615 ]),
616 vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
617 preprocess: ecma_preprocess,
618 main,
619 postprocess,
620 options: ecmascript_options_vc,
621 })],
622 ),
623 ModuleRule::new_all(
624 RuleCondition::ResourcePathEndsWith(".mjs".to_string()),
625 vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
626 preprocess: ecma_preprocess,
627 main,
628 postprocess,
629 options: EcmascriptOptions {
630 specified_module_type: SpecifiedModuleType::EcmaScript,
631 ..ecmascript_options
632 }
633 .resolved_cell(),
634 })],
635 ),
636 ModuleRule::new_all(
637 RuleCondition::ResourcePathEndsWith(".cjs".to_string()),
638 vec![ModuleRuleEffect::ModuleType(ModuleType::Ecmascript {
639 preprocess: ecma_preprocess,
640 main,
641 postprocess,
642 options: EcmascriptOptions {
643 specified_module_type: SpecifiedModuleType::CommonJs,
644 ..ecmascript_options
645 }
646 .resolved_cell(),
647 })],
648 ),
649 ModuleRule::new(
650 RuleCondition::ResourcePathEndsWith(".d.ts".to_string()),
651 vec![ModuleRuleEffect::ModuleType(
652 ModuleType::TypescriptDeclaration {
653 preprocess: empty,
654 main: empty,
655 postprocess: empty,
656 options: ecmascript_options_vc,
657 },
658 )],
659 ),
660 ModuleRule::new(
661 RuleCondition::any(vec![RuleCondition::ResourcePathEndsWith(
662 ".node".to_string(),
663 )]),
664 vec![ModuleRuleEffect::ModuleType(ModuleType::NodeAddon)],
665 ),
666 ModuleRule::new(
668 RuleCondition::any(vec![
669 RuleCondition::ResourcePathEndsWith(".wasm".to_string()),
670 RuleCondition::ContentTypeStartsWith("application/wasm".to_string()),
671 ]),
672 vec![ModuleRuleEffect::ModuleType(ModuleType::WebAssembly {
673 source_ty: WebAssemblySourceType::Binary,
674 })],
675 ),
676 ModuleRule::new(
677 RuleCondition::any(vec![RuleCondition::ResourcePathEndsWith(
678 ".wat".to_string(),
679 )]),
680 vec![ModuleRuleEffect::ModuleType(ModuleType::WebAssembly {
681 source_ty: WebAssemblySourceType::Text,
682 })],
683 ),
684 ModuleRule::new(
685 RuleCondition::any(vec![
686 RuleCondition::ResourcePathEndsWith(".apng".to_string()),
687 RuleCondition::ResourcePathEndsWith(".avif".to_string()),
688 RuleCondition::ResourcePathEndsWith(".gif".to_string()),
689 RuleCondition::ResourcePathEndsWith(".ico".to_string()),
690 RuleCondition::ResourcePathEndsWith(".jpg".to_string()),
691 RuleCondition::ResourcePathEndsWith(".jpeg".to_string()),
692 RuleCondition::ResourcePathEndsWith(".png".to_string()),
693 RuleCondition::ResourcePathEndsWith(".svg".to_string()),
694 RuleCondition::ResourcePathEndsWith(".webp".to_string()),
695 RuleCondition::ResourcePathEndsWith(".woff2".to_string()),
696 ]),
697 vec![ModuleRuleEffect::ModuleType(ModuleType::StaticUrlJs {
698 tag: static_url_tag.clone(),
699 })],
700 ),
701 ModuleRule::new(
702 RuleCondition::all(vec![
703 RuleCondition::ResourcePathHasNoExtension,
705 RuleCondition::ContentTypeEmpty,
706 ]),
707 vec![ModuleRuleEffect::ModuleType(
708 ModuleType::EcmascriptExtensionless {
709 preprocess: empty,
710 main: empty,
711 postprocess: empty,
712 options: ecmascript_options_vc,
713 },
714 )],
715 ),
716 ]);
717
718 if let Some(options) = enable_typescript_transform {
719 let options = options.await?;
720 let ts_preprocess = ResolvedVc::cell(
721 decorators_transform
722 .clone()
723 .into_iter()
724 .chain(std::iter::once(EcmascriptInputTransform::TypeScript {
725 use_define_for_class_fields: options.use_define_for_class_fields,
726 verbatim_module_syntax: options.verbatim_module_syntax,
727 }))
728 .collect(),
729 );
730
731 rules.extend([
732 ModuleRule::new_all(
733 RuleCondition::ResourcePathEndsWith(".ts".to_string()),
734 vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
735 preprocess: ts_preprocess,
736 main,
737 postprocess,
738 tsx: false,
739 analyze_types: enable_types,
740 options: ecmascript_options_vc,
741 })],
742 ),
743 ModuleRule::new_all(
744 RuleCondition::ResourcePathEndsWith(".tsx".to_string()),
745 vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
746 preprocess: ts_preprocess,
747 main,
748 postprocess,
749 tsx: true,
750 analyze_types: enable_types,
751 options: ecmascript_options_vc,
752 })],
753 ),
754 ModuleRule::new_all(
755 RuleCondition::ResourcePathEndsWith(".mts".to_string()),
756 vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
757 preprocess: ts_preprocess,
758 main,
759 postprocess,
760 tsx: false,
761 analyze_types: enable_types,
762 options: EcmascriptOptions {
763 specified_module_type: SpecifiedModuleType::EcmaScript,
764 ..ecmascript_options
765 }
766 .resolved_cell(),
767 })],
768 ),
769 ModuleRule::new_all(
770 RuleCondition::ResourcePathEndsWith(".mtsx".to_string()),
771 vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
772 preprocess: ts_preprocess,
773 main,
774 postprocess,
775 tsx: true,
776 analyze_types: enable_types,
777 options: EcmascriptOptions {
778 specified_module_type: SpecifiedModuleType::EcmaScript,
779 ..ecmascript_options
780 }
781 .resolved_cell(),
782 })],
783 ),
784 ModuleRule::new_all(
785 RuleCondition::ResourcePathEndsWith(".cts".to_string()),
786 vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
787 preprocess: ts_preprocess,
788 main,
789 postprocess,
790 tsx: false,
791 analyze_types: enable_types,
792 options: EcmascriptOptions {
793 specified_module_type: SpecifiedModuleType::CommonJs,
794 ..ecmascript_options
795 }
796 .resolved_cell(),
797 })],
798 ),
799 ModuleRule::new_all(
800 RuleCondition::ResourcePathEndsWith(".ctsx".to_string()),
801 vec![ModuleRuleEffect::ModuleType(ModuleType::Typescript {
802 preprocess: ts_preprocess,
803 main,
804 postprocess,
805 tsx: true,
806 analyze_types: enable_types,
807 options: EcmascriptOptions {
808 specified_module_type: SpecifiedModuleType::CommonJs,
809 ..ecmascript_options
810 }
811 .resolved_cell(),
812 })],
813 ),
814 ]);
815 }
816
817 if enable_raw_css {
818 rules.extend([
819 ModuleRule::new(
820 module_css_condition.clone(),
821 vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
822 ty: CssModuleType::Module,
823 environment,
824 lightningcss_features,
825 })],
826 ),
827 ModuleRule::new(
828 RuleCondition::any(vec![
829 RuleCondition::ResourcePathEndsWith(".css".to_string()),
830 RuleCondition::ContentTypeStartsWith("text/css".to_string()),
831 ]),
832 vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
833 ty: CssModuleType::Default,
834 environment,
835 lightningcss_features,
836 })],
837 ),
838 ]);
839 } else {
840 if let Some(options) = enable_postcss_transform {
841 let options = options.await?;
842 let execution_context = execution_context
843 .context("execution_context is required for the postcss_transform")?;
844
845 let import_map = if let Some(postcss_package) = options.postcss_package {
846 package_import_map_from_import_mapping(rcstr!("postcss"), *postcss_package)
847 } else {
848 package_import_map_from_context(
849 rcstr!("postcss"),
850 path.clone()
851 .context("need_path in ModuleOptions::new is incorrect")?,
852 )
853 };
854
855 rules.push(ModuleRule::new(
856 RuleCondition::All(vec![
857 RuleCondition::Any(vec![
858 RuleCondition::ResourcePathEndsWith(".css".to_string()),
860 RuleCondition::ContentTypeStartsWith("text/css".to_string()),
861 module_css_condition.clone(),
862 ]),
863 module_css_external_transform_conditions.clone(),
864 ]),
865 vec![ModuleRuleEffect::SourceTransforms(ResolvedVc::cell(vec![
866 ResolvedVc::upcast(
867 PostCssTransform::new(
868 node_evaluate_asset_context(
869 *execution_context,
870 Some(import_map),
871 None,
872 Layer::new(rcstr!("postcss")),
873 true,
874 ),
875 config_tracing_module_context(*execution_context),
876 *execution_context,
877 options.config_location,
878 matches!(css_source_maps, SourceMapsType::Full),
879 )
880 .to_resolved()
881 .await?,
882 ),
883 ]))],
884 ));
885 }
886
887 rules.extend([
888 ModuleRule::new(
889 RuleCondition::all(vec![
890 module_css_condition.clone(),
891 RuleCondition::ReferenceType(ReferenceTypeCondition::Css(Some(
893 CssReferenceSubType::AtImport(None),
894 ))),
895 ]),
896 vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
897 ty: CssModuleType::Module,
898 environment,
899 lightningcss_features,
900 })],
901 ),
902 ModuleRule::new(
904 RuleCondition::all(vec![
905 module_css_condition.clone(),
906 RuleCondition::ReferenceType(ReferenceTypeCondition::Css(Some(
907 CssReferenceSubType::Inner,
908 ))),
909 ]),
910 vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
911 ty: CssModuleType::Module,
912 environment,
913 lightningcss_features,
914 })],
915 ),
916 ModuleRule::new(
918 RuleCondition::all(vec![
919 module_css_condition.clone(),
920 RuleCondition::ReferenceType(ReferenceTypeCondition::Css(Some(
921 CssReferenceSubType::Analyze,
922 ))),
923 ]),
924 vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
925 ty: CssModuleType::Module,
926 environment,
927 lightningcss_features,
928 })],
929 ),
930 ModuleRule::new(
931 RuleCondition::all(vec![module_css_condition.clone()]),
932 vec![ModuleRuleEffect::ModuleType(ModuleType::CssModule)],
933 ),
934 ModuleRule::new_all(
935 RuleCondition::Any(vec![
936 RuleCondition::ResourcePathEndsWith(".css".to_string()),
937 RuleCondition::ContentTypeStartsWith("text/css".to_string()),
938 ]),
939 vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
940 ty: CssModuleType::Default,
941 environment,
942 lightningcss_features,
943 })],
944 ),
945 ]);
946 }
947
948 Ok(ModuleOptions::cell(ModuleOptions { rules }))
949 }
950}