1use std::{
2 fmt,
3 ops::{Deref, DerefMut},
4 sync::Arc,
5};
6
7use async_recursion::async_recursion;
8use bitflags::bitflags;
9use derive_more::Debug;
10use futures::future::BoxFuture;
11use rspack_cacheable::{cacheable, with::Unsupported};
12use rspack_error::Result;
13use rspack_macros::MergeFrom;
14use rspack_regex::RspackRegex;
15use rspack_util::{MergeFrom, try_all, try_any};
16use rustc_hash::FxHashMap as HashMap;
17use tokio::sync::OnceCell;
18
19use crate::{Compilation, Filename, Module, ModuleType, PublicPath, Resolve};
20
21#[derive(Debug, Default)]
22pub struct ParserOptionsMap(HashMap<String, ParserOptions>);
23
24impl Deref for ParserOptionsMap {
25 type Target = HashMap<String, ParserOptions>;
26
27 fn deref(&self) -> &Self::Target {
28 &self.0
29 }
30}
31
32impl DerefMut for ParserOptionsMap {
33 fn deref_mut(&mut self) -> &mut Self::Target {
34 &mut self.0
35 }
36}
37
38impl FromIterator<(String, ParserOptions)> for ParserOptionsMap {
39 fn from_iter<I: IntoIterator<Item = (String, ParserOptions)>>(i: I) -> Self {
40 Self(HashMap::from_iter(i))
41 }
42}
43
44impl ParserOptionsMap {
45 pub fn get<'a>(&'a self, key: &'a str) -> Option<&'a ParserOptions> {
46 self.0.get(key)
47 }
48}
49
50#[cacheable]
51#[derive(Debug, Clone, MergeFrom)]
52pub enum ParserOptions {
53 Asset(AssetParserOptions),
54 Css(CssParserOptions),
55 CssAuto(CssAutoParserOptions),
56 CssModule(CssModuleParserOptions),
57 Javascript(JavascriptParserOptions),
58 JavascriptAuto(JavascriptParserOptions),
59 JavascriptEsm(JavascriptParserOptions),
60 JavascriptDynamic(JavascriptParserOptions),
61 Json(JsonParserOptions),
62 Unknown,
63}
64
65macro_rules! get_variant {
66 ($fn_name:ident, $variant:ident, $ret_ty:ident) => {
67 pub fn $fn_name(&self) -> Option<&$ret_ty> {
68 match self {
69 Self::$variant(value) => Some(value),
70 _ => None,
71 }
72 }
73 };
74}
75
76impl ParserOptions {
77 get_variant!(get_asset, Asset, AssetParserOptions);
78 get_variant!(get_css, Css, CssParserOptions);
79 get_variant!(get_css_auto, CssAuto, CssAutoParserOptions);
80 get_variant!(get_css_module, CssModule, CssModuleParserOptions);
81 get_variant!(get_javascript, Javascript, JavascriptParserOptions);
82 get_variant!(get_javascript_auto, JavascriptAuto, JavascriptParserOptions);
83 get_variant!(get_javascript_esm, JavascriptEsm, JavascriptParserOptions);
84 get_variant!(
85 get_javascript_dynamic,
86 JavascriptDynamic,
87 JavascriptParserOptions
88 );
89 get_variant!(get_json, Json, JsonParserOptions);
90}
91
92#[cacheable]
93#[derive(Debug, Clone, Copy, MergeFrom)]
94pub enum DynamicImportMode {
95 Lazy,
96 Weak,
97 Eager,
98 LazyOnce,
99}
100
101impl From<&str> for DynamicImportMode {
102 fn from(value: &str) -> Self {
103 match value {
104 "weak" => DynamicImportMode::Weak,
105 "eager" => DynamicImportMode::Eager,
106 "lazy" => DynamicImportMode::Lazy,
107 "lazy-once" => DynamicImportMode::LazyOnce,
108 _ => {
109 DynamicImportMode::Lazy
111 }
112 }
113 }
114}
115
116#[cacheable]
117#[derive(Debug, Clone, Copy, MergeFrom, PartialEq, Eq, Hash, PartialOrd, Ord)]
118pub enum DynamicImportFetchPriority {
119 Low,
120 High,
121 Auto,
122}
123
124impl From<&str> for DynamicImportFetchPriority {
125 fn from(value: &str) -> Self {
126 match value {
127 "low" => DynamicImportFetchPriority::Low,
128 "high" => DynamicImportFetchPriority::High,
129 "auto" => DynamicImportFetchPriority::Auto,
130 _ => DynamicImportFetchPriority::Auto,
131 }
132 }
133}
134
135impl fmt::Display for DynamicImportFetchPriority {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 match self {
138 DynamicImportFetchPriority::Low => write!(f, "low"),
139 DynamicImportFetchPriority::High => write!(f, "high"),
140 DynamicImportFetchPriority::Auto => write!(f, "auto"),
141 }
142 }
143}
144
145#[cacheable]
146#[derive(Debug, Clone, Copy, MergeFrom)]
147pub enum JavascriptParserUrl {
148 Enable,
149 Disable,
150 NewUrlRelative,
151 Relative,
152}
153
154impl From<&str> for JavascriptParserUrl {
155 fn from(value: &str) -> Self {
156 match value {
157 "false" => Self::Disable,
158 "relative" => Self::Relative,
159 "new-url-relative" => Self::NewUrlRelative,
160 _ => Self::Enable,
161 }
162 }
163}
164
165#[cacheable]
166#[derive(Debug, Clone, Copy, MergeFrom)]
167pub enum JavascriptParserOrder {
168 Disable,
169 Order(i32),
170}
171
172impl JavascriptParserOrder {
173 pub fn get_order(&self) -> Option<i32> {
174 match self {
175 Self::Disable => None,
176 Self::Order(o) => Some(*o),
177 }
178 }
179}
180
181impl From<&str> for JavascriptParserOrder {
182 fn from(value: &str) -> Self {
183 match value {
184 "false" => Self::Disable,
185 "true" => Self::Order(0),
186 _ => {
187 if let Ok(order) = value.parse::<i32>() {
188 Self::Order(order)
189 } else {
190 Self::Order(0)
191 }
192 }
193 }
194 }
195}
196
197#[cacheable]
198#[derive(Debug, Clone, Copy, MergeFrom, PartialEq, Eq)]
199pub enum JavascriptParserCommonjsExportsOption {
200 Enable,
201 Disable,
202 SkipInEsm,
203}
204
205impl From<bool> for JavascriptParserCommonjsExportsOption {
206 fn from(value: bool) -> Self {
207 if value { Self::Enable } else { Self::Disable }
208 }
209}
210
211#[cacheable]
212#[derive(Debug, Clone, MergeFrom)]
213pub struct JavascriptParserCommonjsOptions {
214 pub exports: JavascriptParserCommonjsExportsOption,
215}
216
217#[cacheable]
218#[derive(Debug, Clone, Copy, MergeFrom)]
219pub enum ExportPresenceMode {
220 None,
221 Warn,
222 Auto,
223 Error,
224}
225
226impl From<&str> for ExportPresenceMode {
227 fn from(value: &str) -> Self {
228 match value {
229 "false" => Self::None,
230 "warn" => Self::Warn,
231 "error" => Self::Error,
232 _ => Self::Auto,
233 }
234 }
235}
236
237impl ExportPresenceMode {
238 pub fn get_effective_export_presence(&self, module: &dyn Module) -> Option<bool> {
239 match self {
240 ExportPresenceMode::None => None,
241 ExportPresenceMode::Warn => Some(false),
242 ExportPresenceMode::Error => Some(true),
243 ExportPresenceMode::Auto => Some(module.build_meta().strict_esm_module),
244 }
245 }
246}
247
248#[cacheable]
249#[derive(Debug, Default, Clone, Copy, MergeFrom)]
250pub enum TypeReexportPresenceMode {
251 #[default]
252 NoTolerant,
253 Tolerant,
254 TolerantNoCheck,
255}
256
257impl From<&str> for TypeReexportPresenceMode {
258 fn from(value: &str) -> Self {
259 match value {
260 "tolerant" => Self::Tolerant,
261 "tolerant-no-check" => Self::TolerantNoCheck,
262 _ => Self::NoTolerant,
263 }
264 }
265}
266
267#[cacheable]
268#[derive(Debug, Clone, Copy, MergeFrom)]
269pub enum OverrideStrict {
270 Strict,
271 NoneStrict,
272}
273
274impl From<&str> for OverrideStrict {
275 fn from(value: &str) -> Self {
276 match value {
277 "strict" => Self::Strict,
278 "non-strict" => Self::NoneStrict,
279 _ => unreachable!("parser.overrideStrict should be 'strict' or 'non-strict'"),
280 }
281 }
282}
283
284#[cacheable]
285#[derive(Debug, Clone, MergeFrom, Default)]
286pub struct JavascriptParserOptions {
287 pub dynamic_import_mode: Option<DynamicImportMode>,
288 pub dynamic_import_preload: Option<JavascriptParserOrder>,
289 pub dynamic_import_prefetch: Option<JavascriptParserOrder>,
290 pub dynamic_import_fetch_priority: Option<DynamicImportFetchPriority>,
291 pub url: Option<JavascriptParserUrl>,
292 pub unknown_context_critical: Option<bool>,
293 pub expr_context_critical: Option<bool>,
294 pub wrapped_context_critical: Option<bool>,
295 pub wrapped_context_reg_exp: Option<RspackRegex>,
296 pub exports_presence: Option<ExportPresenceMode>,
297 pub import_exports_presence: Option<ExportPresenceMode>,
298 pub reexport_exports_presence: Option<ExportPresenceMode>,
299 pub strict_export_presence: Option<bool>,
300 pub type_reexports_presence: Option<TypeReexportPresenceMode>,
301 pub worker: Option<Vec<String>>,
302 pub override_strict: Option<OverrideStrict>,
303 pub import_meta: Option<bool>,
304 pub require_as_expression: Option<bool>,
305 pub require_dynamic: Option<bool>,
306 pub require_resolve: Option<bool>,
307 pub commonjs: Option<JavascriptParserCommonjsOptions>,
308 pub import_dynamic: Option<bool>,
309 pub commonjs_magic_comments: Option<bool>,
310 pub inline_const: Option<bool>,
311 pub jsx: Option<bool>,
312 pub defer_import: Option<bool>,
313}
314
315#[cacheable]
316#[derive(Debug, Clone, MergeFrom)]
317pub struct AssetParserOptions {
318 pub data_url_condition: Option<AssetParserDataUrl>,
319}
320
321#[cacheable]
322#[derive(Debug, Clone, MergeFrom)]
323pub enum AssetParserDataUrl {
324 Options(AssetParserDataUrlOptions),
325 }
327
328#[cacheable]
329#[derive(Debug, Clone, MergeFrom)]
330pub struct AssetParserDataUrlOptions {
331 pub max_size: Option<f64>,
332}
333
334#[cacheable]
335#[derive(Debug, Clone, MergeFrom)]
336pub struct CssParserOptions {
337 pub named_exports: Option<bool>,
338 pub url: Option<bool>,
339}
340
341#[cacheable]
342#[derive(Debug, Clone, MergeFrom)]
343pub struct CssAutoParserOptions {
344 pub named_exports: Option<bool>,
345 pub url: Option<bool>,
346}
347
348impl From<CssParserOptions> for CssAutoParserOptions {
349 fn from(value: CssParserOptions) -> Self {
350 Self {
351 named_exports: value.named_exports,
352 url: value.url,
353 }
354 }
355}
356
357#[cacheable]
358#[derive(Debug, Clone, MergeFrom)]
359pub struct CssModuleParserOptions {
360 pub named_exports: Option<bool>,
361 pub url: Option<bool>,
362}
363
364impl From<CssParserOptions> for CssModuleParserOptions {
365 fn from(value: CssParserOptions) -> Self {
366 Self {
367 named_exports: value.named_exports,
368 url: value.url,
369 }
370 }
371}
372
373pub type JsonParseFn = Arc<dyn Fn(String) -> BoxFuture<'static, Result<String>> + Sync + Send>;
374
375#[cacheable]
376pub enum ParseOption {
377 Func(#[cacheable(with=Unsupported)] JsonParseFn),
378 None,
379}
380
381impl fmt::Debug for ParseOption {
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 match self {
384 Self::Func(_) => write!(f, "ParseOption::Func(...)"),
385 _ => write!(f, "ParseOption::None"),
386 }
387 }
388}
389
390impl Clone for ParseOption {
391 fn clone(&self) -> Self {
392 match self {
393 Self::Func(f) => Self::Func(f.clone()),
394 Self::None => Self::None,
395 }
396 }
397}
398
399impl MergeFrom for ParseOption {
400 fn merge_from(self, other: &Self) -> Self {
401 other.clone()
402 }
403}
404
405#[cacheable]
406#[derive(Debug, Clone, MergeFrom)]
407pub struct JsonParserOptions {
408 pub exports_depth: Option<u32>,
409 pub parse: ParseOption,
410}
411
412#[derive(Debug, Default)]
413pub struct GeneratorOptionsMap(HashMap<String, GeneratorOptions>);
414
415impl Deref for GeneratorOptionsMap {
416 type Target = HashMap<String, GeneratorOptions>;
417
418 fn deref(&self) -> &Self::Target {
419 &self.0
420 }
421}
422
423impl DerefMut for GeneratorOptionsMap {
424 fn deref_mut(&mut self) -> &mut Self::Target {
425 &mut self.0
426 }
427}
428
429impl FromIterator<(String, GeneratorOptions)> for GeneratorOptionsMap {
430 fn from_iter<I: IntoIterator<Item = (String, GeneratorOptions)>>(i: I) -> Self {
431 Self(HashMap::from_iter(i))
432 }
433}
434
435impl GeneratorOptionsMap {
436 pub fn get(&self, key: &str) -> Option<&GeneratorOptions> {
437 self.0.get(key)
438 }
439}
440
441#[cacheable]
442#[derive(Debug, Clone, MergeFrom)]
443pub enum GeneratorOptions {
444 Asset(AssetGeneratorOptions),
445 AssetInline(AssetInlineGeneratorOptions),
446 AssetResource(AssetResourceGeneratorOptions),
447 Css(CssGeneratorOptions),
448 CssAuto(CssAutoGeneratorOptions),
449 CssModule(CssModuleGeneratorOptions),
450 Json(JsonGeneratorOptions),
451 Unknown,
452}
453
454impl GeneratorOptions {
455 get_variant!(get_asset, Asset, AssetGeneratorOptions);
456 get_variant!(get_asset_inline, AssetInline, AssetInlineGeneratorOptions);
457 get_variant!(
458 get_asset_resource,
459 AssetResource,
460 AssetResourceGeneratorOptions
461 );
462 get_variant!(get_css, Css, CssGeneratorOptions);
463 get_variant!(get_css_auto, CssAuto, CssAutoGeneratorOptions);
464 get_variant!(get_css_module, CssModule, CssModuleGeneratorOptions);
465 get_variant!(get_json, Json, JsonGeneratorOptions);
466
467 pub fn asset_filename(&self) -> Option<&Filename> {
468 self
469 .get_asset()
470 .and_then(|x| x.filename.as_ref())
471 .or_else(|| self.get_asset_resource().and_then(|x| x.filename.as_ref()))
472 }
473
474 pub fn asset_output_path(&self) -> Option<&Filename> {
475 self
476 .get_asset()
477 .and_then(|x| x.output_path.as_ref())
478 .or_else(|| {
479 self
480 .get_asset_resource()
481 .and_then(|x| x.output_path.as_ref())
482 })
483 }
484
485 pub fn asset_public_path(&self) -> Option<&PublicPath> {
486 self
487 .get_asset()
488 .and_then(|x| x.public_path.as_ref())
489 .or_else(|| {
490 self
491 .get_asset_resource()
492 .and_then(|x| x.public_path.as_ref())
493 })
494 }
495
496 pub fn asset_data_url(&self) -> Option<&AssetGeneratorDataUrl> {
497 self
498 .get_asset()
499 .and_then(|x| x.data_url.as_ref())
500 .or_else(|| self.get_asset_inline().and_then(|x| x.data_url.as_ref()))
501 }
502
503 pub fn asset_emit(&self) -> Option<bool> {
504 self
505 .get_asset()
506 .and_then(|x| x.emit)
507 .or_else(|| self.get_asset_resource().and_then(|x| x.emit))
508 }
509}
510
511#[cacheable]
512#[derive(Debug, Clone, MergeFrom)]
513pub struct AssetInlineGeneratorOptions {
514 pub data_url: Option<AssetGeneratorDataUrl>,
515 pub binary: Option<bool>,
516}
517
518impl From<AssetGeneratorOptions> for AssetInlineGeneratorOptions {
519 fn from(value: AssetGeneratorOptions) -> Self {
520 Self {
521 data_url: value.data_url,
522 binary: value.binary,
523 }
524 }
525}
526
527#[cacheable]
528#[derive(Debug, Clone, Copy, MergeFrom)]
529struct AssetGeneratorImportModeFlags(u8);
530bitflags! {
531 impl AssetGeneratorImportModeFlags: u8 {
532 const URL = 1 << 0;
533 const PRESERVE = 1 << 1;
534 }
535}
536
537#[cacheable]
538#[derive(Debug, Clone, Copy, MergeFrom)]
539pub struct AssetGeneratorImportMode(AssetGeneratorImportModeFlags);
540
541impl AssetGeneratorImportMode {
542 pub fn is_url(&self) -> bool {
543 self.0.contains(AssetGeneratorImportModeFlags::URL)
544 }
545 pub fn is_preserve(&self) -> bool {
546 self.0.contains(AssetGeneratorImportModeFlags::PRESERVE)
547 }
548}
549
550impl From<String> for AssetGeneratorImportMode {
551 fn from(s: String) -> Self {
552 match s.as_str() {
553 "url" => Self(AssetGeneratorImportModeFlags::URL),
554 "preserve" => Self(AssetGeneratorImportModeFlags::PRESERVE),
555 _ => unreachable!("AssetGeneratorImportMode error"),
556 }
557 }
558}
559
560impl Default for AssetGeneratorImportMode {
561 fn default() -> Self {
562 Self(AssetGeneratorImportModeFlags::URL)
563 }
564}
565
566#[cacheable]
567#[derive(Debug, Clone, MergeFrom)]
568pub struct AssetResourceGeneratorOptions {
569 pub emit: Option<bool>,
570 pub filename: Option<Filename>,
571 pub output_path: Option<Filename>,
572 pub public_path: Option<PublicPath>,
573 pub import_mode: Option<AssetGeneratorImportMode>,
574 pub binary: Option<bool>,
575}
576
577impl From<AssetGeneratorOptions> for AssetResourceGeneratorOptions {
578 fn from(value: AssetGeneratorOptions) -> Self {
579 Self {
580 emit: value.emit,
581 filename: value.filename,
582 output_path: value.output_path,
583 public_path: value.public_path,
584 import_mode: value.import_mode,
585 binary: value.binary,
586 }
587 }
588}
589
590#[cacheable]
591#[derive(Debug, Clone, MergeFrom)]
592pub struct AssetGeneratorOptions {
593 pub emit: Option<bool>,
594 pub filename: Option<Filename>,
595 pub output_path: Option<Filename>,
596 pub public_path: Option<PublicPath>,
597 pub data_url: Option<AssetGeneratorDataUrl>,
598 pub import_mode: Option<AssetGeneratorImportMode>,
599 pub binary: Option<bool>,
600}
601
602pub struct AssetGeneratorDataUrlFnCtx<'a> {
603 pub filename: String,
604 pub module: &'a dyn Module,
605 pub compilation: &'a Compilation,
606}
607
608pub type AssetGeneratorDataUrlFn = Arc<
609 dyn Fn(Vec<u8>, AssetGeneratorDataUrlFnCtx) -> BoxFuture<'static, Result<String>> + Sync + Send,
610>;
611
612#[cacheable]
613pub enum AssetGeneratorDataUrl {
614 Options(AssetGeneratorDataUrlOptions),
615 Func(#[cacheable(with=Unsupported)] AssetGeneratorDataUrlFn),
616}
617
618impl fmt::Debug for AssetGeneratorDataUrl {
619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620 match self {
621 Self::Options(i) => i.fmt(f),
622 Self::Func(_) => "Func(...)".fmt(f),
623 }
624 }
625}
626
627impl Clone for AssetGeneratorDataUrl {
628 fn clone(&self) -> Self {
629 match self {
630 Self::Options(i) => Self::Options(i.clone()),
631 Self::Func(i) => Self::Func(i.clone()),
632 }
633 }
634}
635
636impl MergeFrom for AssetGeneratorDataUrl {
637 fn merge_from(self, other: &Self) -> Self {
638 other.clone()
639 }
640}
641
642#[cacheable]
643#[derive(Debug, Clone, MergeFrom, Hash)]
644pub struct AssetGeneratorDataUrlOptions {
645 pub encoding: Option<DataUrlEncoding>,
646 pub mimetype: Option<String>,
647}
648
649#[cacheable]
650#[derive(Debug, Clone, MergeFrom, Hash)]
651pub enum DataUrlEncoding {
652 None,
653 Base64,
654}
655
656impl fmt::Display for DataUrlEncoding {
657 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
658 match self {
659 DataUrlEncoding::None => write!(f, ""),
660 DataUrlEncoding::Base64 => write!(f, "base64"),
661 }
662 }
663}
664
665impl From<String> for DataUrlEncoding {
666 fn from(value: String) -> Self {
667 match value.as_str() {
668 "base64" => Self::Base64,
669 "false" => Self::None,
670 _ => unreachable!("DataUrlEncoding should be base64 or false"),
671 }
672 }
673}
674
675#[cacheable]
676#[derive(Debug, Clone, MergeFrom)]
677pub struct CssGeneratorOptions {
678 pub exports_only: Option<bool>,
679 pub es_module: Option<bool>,
680}
681
682#[cacheable]
683#[derive(Default, Debug, Clone, MergeFrom)]
684pub struct CssAutoGeneratorOptions {
685 pub exports_convention: Option<CssExportsConvention>,
686 pub exports_only: Option<bool>,
687 pub local_ident_name: Option<LocalIdentName>,
688 pub es_module: Option<bool>,
689}
690
691impl From<CssGeneratorOptions> for CssAutoGeneratorOptions {
692 fn from(value: CssGeneratorOptions) -> Self {
693 Self {
694 exports_only: value.exports_only,
695 es_module: value.es_module,
696 ..Default::default()
697 }
698 }
699}
700
701#[cacheable]
702#[derive(Default, Debug, Clone, MergeFrom)]
703pub struct CssModuleGeneratorOptions {
704 pub exports_convention: Option<CssExportsConvention>,
705 pub exports_only: Option<bool>,
706 pub local_ident_name: Option<LocalIdentName>,
707 pub es_module: Option<bool>,
708}
709
710impl From<CssGeneratorOptions> for CssModuleGeneratorOptions {
711 fn from(value: CssGeneratorOptions) -> Self {
712 Self {
713 exports_only: value.exports_only,
714 es_module: value.es_module,
715 ..Default::default()
716 }
717 }
718}
719
720#[cacheable]
721#[derive(Default, Debug, Clone, MergeFrom)]
722pub struct JsonGeneratorOptions {
723 pub json_parse: Option<bool>,
724}
725
726#[cacheable]
727#[derive(Debug, Clone, MergeFrom)]
728pub struct LocalIdentName {
729 pub template: Filename,
730}
731
732impl From<String> for LocalIdentName {
733 fn from(value: String) -> Self {
734 Self {
735 template: value.into(),
736 }
737 }
738}
739
740impl From<&str> for LocalIdentName {
741 fn from(value: &str) -> Self {
742 Self {
743 template: crate::Filename::from(value),
744 }
745 }
746}
747
748#[cacheable]
749#[derive(Debug, Clone, Copy)]
750struct ExportsConventionFlags(u8);
751bitflags! {
752 impl ExportsConventionFlags: u8 {
753 const ASIS = 1 << 0;
754 const CAMELCASE = 1 << 1;
755 const DASHES = 1 << 2;
756 }
757}
758
759impl MergeFrom for ExportsConventionFlags {
760 fn merge_from(self, other: &Self) -> Self {
761 *other
762 }
763}
764
765#[cacheable]
766#[derive(Debug, Clone, Copy, MergeFrom)]
767pub struct CssExportsConvention(ExportsConventionFlags);
768
769impl CssExportsConvention {
770 pub fn as_is(&self) -> bool {
771 self.0.contains(ExportsConventionFlags::ASIS)
772 }
773
774 pub fn camel_case(&self) -> bool {
775 self.0.contains(ExportsConventionFlags::CAMELCASE)
776 }
777
778 pub fn dashes(&self) -> bool {
779 self.0.contains(ExportsConventionFlags::DASHES)
780 }
781}
782
783impl From<String> for CssExportsConvention {
784 fn from(s: String) -> Self {
785 match s.as_str() {
786 "as-is" => Self(ExportsConventionFlags::ASIS),
787 "camel-case" => Self(ExportsConventionFlags::ASIS | ExportsConventionFlags::CAMELCASE),
788 "camel-case-only" => Self(ExportsConventionFlags::CAMELCASE),
789 "dashes" => Self(ExportsConventionFlags::ASIS | ExportsConventionFlags::DASHES),
790 "dashes-only" => Self(ExportsConventionFlags::DASHES),
791 _ => unreachable!("css exportsConventions error"),
792 }
793 }
794}
795
796impl Default for CssExportsConvention {
797 fn default() -> Self {
798 Self(ExportsConventionFlags::ASIS)
799 }
800}
801
802pub type DescriptionData = HashMap<String, RuleSetConditionWithEmpty>;
803pub type With = HashMap<String, RuleSetConditionWithEmpty>;
804
805pub type RuleSetConditionFnMatcher =
806 Box<dyn Fn(DataRef) -> BoxFuture<'static, Result<bool>> + Sync + Send>;
807
808pub enum RuleSetCondition {
809 String(String),
810 Regexp(RspackRegex),
811 Logical(Box<RuleSetLogicalConditions>),
812 Array(Vec<RuleSetCondition>),
813 Func(RuleSetConditionFnMatcher),
814}
815
816impl fmt::Debug for RuleSetCondition {
817 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
818 match self {
819 Self::String(i) => i.fmt(f),
820 Self::Regexp(i) => i.fmt(f),
821 Self::Logical(i) => i.fmt(f),
822 Self::Array(i) => i.fmt(f),
823 Self::Func(_) => "Func(...)".fmt(f),
824 }
825 }
826}
827
828#[derive(Copy, Clone)]
829pub enum DataRef<'a> {
830 Str(&'a str),
831 Value(&'a serde_json::Value),
832}
833
834impl<'s> From<&'s str> for DataRef<'s> {
835 fn from(value: &'s str) -> Self {
836 Self::Str(value)
837 }
838}
839
840impl<'s> From<&'s serde_json::Value> for DataRef<'s> {
841 fn from(value: &'s serde_json::Value) -> Self {
842 Self::Value(value)
843 }
844}
845
846impl DataRef<'_> {
847 pub fn as_str(&self) -> Option<&str> {
848 match self {
849 Self::Str(s) => Some(s),
850 Self::Value(v) => v.as_str(),
851 }
852 }
853
854 pub fn to_value(&self) -> serde_json::Value {
855 match self {
856 Self::Str(s) => serde_json::Value::String((*s).to_owned()),
857 Self::Value(v) => (*v).to_owned(),
858 }
859 }
860}
861
862impl RuleSetCondition {
863 #[async_recursion]
864 pub async fn try_match(&self, data: DataRef<'async_recursion>) -> Result<bool> {
865 match self {
866 Self::String(s) => Ok(
867 data
868 .as_str()
869 .map(|data| data.starts_with(s))
870 .unwrap_or_default(),
871 ),
872 Self::Regexp(r) => Ok(data.as_str().map(|data| r.test(data)).unwrap_or_default()),
873 Self::Logical(g) => g.try_match(data).await,
874 Self::Array(l) => try_any(l, |i| async { i.try_match(data).await }).await,
875 Self::Func(f) => f(data).await,
876 }
877 }
878
879 #[async_recursion]
880 async fn match_when_empty(&self) -> Result<bool> {
881 let res = match self {
882 RuleSetCondition::String(s) => s.is_empty(),
883 RuleSetCondition::Regexp(rspack_regex) => rspack_regex.test(""),
884 RuleSetCondition::Logical(logical) => logical.match_when_empty().await?,
885 RuleSetCondition::Array(arr) => {
886 arr.is_empty() && try_any(arr, |c| async move { c.match_when_empty().await }).await?
887 }
888 RuleSetCondition::Func(func) => func("".into()).await?,
889 };
890 Ok(res)
891 }
892}
893
894#[derive(Debug)]
895pub struct RuleSetConditionWithEmpty {
896 condition: RuleSetCondition,
897 match_when_empty: OnceCell<bool>,
898}
899
900impl RuleSetConditionWithEmpty {
901 pub fn new(condition: RuleSetCondition) -> Self {
902 Self {
903 condition,
904 match_when_empty: OnceCell::new(),
905 }
906 }
907
908 pub async fn try_match(&self, data: DataRef<'_>) -> Result<bool> {
909 self.condition.try_match(data).await
910 }
911
912 pub async fn match_when_empty(&self) -> Result<bool> {
913 self
914 .match_when_empty
915 .get_or_try_init(|| async { self.condition.match_when_empty().await })
916 .await
917 .copied()
918 }
919}
920
921impl From<RuleSetCondition> for RuleSetConditionWithEmpty {
922 fn from(condition: RuleSetCondition) -> Self {
923 Self::new(condition)
924 }
925}
926
927#[derive(Debug, Default)]
928pub struct RuleSetLogicalConditions {
929 pub and: Option<Vec<RuleSetCondition>>,
930 pub or: Option<Vec<RuleSetCondition>>,
931 pub not: Option<RuleSetCondition>,
932}
933
934impl RuleSetLogicalConditions {
935 #[async_recursion]
936 pub async fn try_match(&self, data: DataRef<'async_recursion>) -> Result<bool> {
937 if let Some(and) = &self.and
938 && try_any(and, |i| async { i.try_match(data).await.map(|i| !i) }).await?
939 {
940 return Ok(false);
941 }
942 if let Some(or) = &self.or
943 && try_all(or, |i| async { i.try_match(data).await.map(|i| !i) }).await?
944 {
945 return Ok(false);
946 }
947 if let Some(not) = &self.not
948 && not.try_match(data).await?
949 {
950 return Ok(false);
951 }
952 Ok(true)
953 }
954
955 pub async fn match_when_empty(&self) -> Result<bool> {
956 let mut has_condition = false;
957 let mut match_when_empty = true;
958 if let Some(and) = &self.and {
959 has_condition = true;
960 match_when_empty &= try_all(and, |i| async { i.match_when_empty().await }).await?;
961 }
962 if let Some(or) = &self.or {
963 has_condition = true;
964 match_when_empty &= try_any(or, |i| async { i.match_when_empty().await }).await?;
965 }
966 if let Some(not) = &self.not {
967 has_condition = true;
968 match_when_empty &= !not.match_when_empty().await?;
969 }
970 Ok(has_condition && match_when_empty)
971 }
972}
973
974pub struct FuncUseCtx {
975 pub resource: Option<String>,
976 pub real_resource: Option<String>,
977 pub resource_query: Option<String>,
978 pub resource_fragment: Option<String>,
979 pub issuer: Option<Box<str>>,
980 pub issuer_layer: Option<String>,
981}
982
983#[derive(Debug, Clone)]
984pub struct ModuleRuleUseLoader {
985 pub loader: String,
988 pub options: Option<String>,
991}
992
993pub type FnUse =
994 Box<dyn Fn(FuncUseCtx) -> BoxFuture<'static, Result<Vec<ModuleRuleUseLoader>>> + Sync + Send>;
995
996#[derive(Debug, Default)]
997pub struct ModuleRule {
998 pub rspack_resource: Option<RuleSetCondition>,
1003 pub test: Option<RuleSetCondition>,
1005 pub include: Option<RuleSetCondition>,
1006 pub exclude: Option<RuleSetCondition>,
1007 pub resource: Option<RuleSetCondition>,
1009 pub resource_query: Option<RuleSetConditionWithEmpty>,
1011 pub resource_fragment: Option<RuleSetConditionWithEmpty>,
1012 pub dependency: Option<RuleSetCondition>,
1013 pub issuer: Option<RuleSetConditionWithEmpty>,
1014 pub issuer_layer: Option<RuleSetConditionWithEmpty>,
1015 pub scheme: Option<RuleSetConditionWithEmpty>,
1016 pub mimetype: Option<RuleSetConditionWithEmpty>,
1017 pub description_data: Option<DescriptionData>,
1018 pub with: Option<With>,
1019 pub one_of: Option<Vec<ModuleRule>>,
1020 pub rules: Option<Vec<ModuleRule>>,
1021 pub effect: ModuleRuleEffect,
1022 pub extract_source_map: Option<bool>,
1023}
1024
1025#[derive(Debug, Default)]
1026pub struct ModuleRuleEffect {
1027 pub side_effects: Option<bool>,
1028 pub r#type: Option<ModuleType>,
1030 pub layer: Option<String>,
1031 pub r#use: ModuleRuleUse,
1032 pub parser: Option<ParserOptions>,
1033 pub generator: Option<GeneratorOptions>,
1034 pub resolve: Option<Resolve>,
1035 pub enforce: ModuleRuleEnforce,
1036 pub extract_source_map: Option<bool>,
1037}
1038
1039pub enum ModuleRuleUse {
1040 Array(Vec<ModuleRuleUseLoader>),
1041 Func(FnUse),
1042}
1043
1044impl Default for ModuleRuleUse {
1045 fn default() -> Self {
1046 Self::Array(vec![])
1047 }
1048}
1049
1050impl fmt::Debug for ModuleRuleUse {
1051 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
1052 match self {
1053 ModuleRuleUse::Array(array_use) => write!(
1054 f,
1055 "{}",
1056 array_use
1057 .iter()
1058 .map(|l| &*l.loader)
1059 .collect::<Vec<_>>()
1060 .join("!")
1061 ),
1062 ModuleRuleUse::Func(_) => write!(f, "Fn(...)"),
1063 }
1064 }
1065}
1066
1067pub type ModuleNoParseTestFn =
1068 Box<dyn Fn(String) -> BoxFuture<'static, Result<bool>> + Sync + Send>;
1069
1070pub enum ModuleNoParseRule {
1071 AbsPathPrefix(String),
1072 Regexp(RspackRegex),
1073 TestFn(ModuleNoParseTestFn),
1074}
1075
1076impl fmt::Debug for ModuleNoParseRule {
1077 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1078 match self {
1079 Self::TestFn(_) => "Fn(...)".fmt(f),
1080 _ => self.fmt(f),
1081 }
1082 }
1083}
1084
1085impl ModuleNoParseRule {
1086 pub async fn try_match(&self, request: &str) -> Result<bool> {
1087 match self {
1088 Self::AbsPathPrefix(s) => Ok(request.starts_with(s)),
1089 Self::Regexp(reg) => Ok(reg.test(request)),
1090 Self::TestFn(func) => func(request.to_string()).await,
1091 }
1092 }
1093}
1094
1095#[derive(Debug)]
1096pub enum ModuleNoParseRules {
1097 Rule(ModuleNoParseRule),
1098 Rules(Vec<ModuleNoParseRule>),
1099}
1100
1101impl ModuleNoParseRules {
1102 #[async_recursion]
1103 pub async fn try_match(&self, request: &str) -> Result<bool> {
1104 match self {
1105 Self::Rule(r) => r.try_match(request).await,
1106 Self::Rules(list) => try_any(list, |r| r.try_match(request)).await,
1107 }
1108 }
1109}
1110
1111#[derive(Debug, Default)]
1112pub enum ModuleRuleEnforce {
1113 Post,
1114 #[default]
1115 Normal,
1116 Pre,
1117}
1118
1119pub type UnsafeCachePredicate =
1120 Box<dyn Fn(&dyn Module) -> BoxFuture<'static, Result<bool>> + Sync + Send>;
1121
1122#[derive(Debug, Default)]
1126pub struct ModuleOptions {
1127 pub rules: Vec<ModuleRule>,
1128 pub parser: Option<ParserOptionsMap>,
1129 pub generator: Option<GeneratorOptionsMap>,
1130 pub no_parse: Option<ModuleNoParseRules>,
1131 #[debug(skip)]
1132 pub unsafe_cache: Option<UnsafeCachePredicate>,
1133}